/** * Hook for diff view, giving us a chance to insert a removal * tab on old version views. * * @param $diff * @param $oldRev * @param Revision $newRev */ function hrDiffViewHeaderHook($diff, $oldRev, $newRev) { if (!empty($newRev) && $newRev->getId()) { hrInstallTab($newRev->getId()); } return true; }
/** * 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; }
/** * Get the HTML link to the diff. * Overridden by RevDelArchiveItem * @return string */ protected function getDiffLink() { if ($this->isDeleted() && !$this->canViewContent()) { return $this->list->msg('diff')->escaped(); } else { return Linker::linkKnown($this->list->title, $this->list->msg('diff')->escaped(), array(), array('diff' => $this->revision->getId(), 'oldid' => 'prev', 'unhide' => 1)); } }
protected function assertRevEquals(Revision $orig, Revision $rev = null) { $this->assertNotNull($rev, 'missing revision'); $this->assertEquals($orig->getId(), $rev->getId()); $this->assertEquals($orig->getPage(), $rev->getPage()); $this->assertEquals($orig->getTimestamp(), $rev->getTimestamp()); $this->assertEquals($orig->getUser(), $rev->getUser()); $this->assertEquals($orig->getSha1(), $rev->getSha1()); }
public static function runForTitleInternal(Title $title, Revision $revision, $fname) { global $wgParser, $wgContLang; wfProfileIn($fname . '-parse'); $options = ParserOptions::newFromUserAndLang(new User(), $wgContLang); $parserOutput = $wgParser->parse($revision->getText(), $title, $options, true, true, $revision->getId()); wfProfileOut($fname . '-parse'); wfProfileIn($fname . '-update'); $updates = $parserOutput->getSecondaryDataUpdates($title, false); DataUpdate::runUpdates($updates); wfProfileOut($fname . '-update'); }
/** * Load revision metadata for the specified articles. If newid is 0, then compare * the old article in oldid to the current article; if oldid is 0, then * compare the current article to the immediately previous one (ignoring the * value of newid). * * If oldid is false, leave the corresponding revision object set * to false. This is impossible via ordinary user input, and is provided for * API convenience. * * @return bool */ public function loadRevisionData() { if ($this->mRevisionsLoaded) { return true; } // Whether it succeeds or fails, we don't want to try again $this->mRevisionsLoaded = true; $this->loadRevisionIds(); // Load the new revision object if ($this->mNewid) { $this->mNewRev = Revision::newFromId($this->mNewid); } else { $this->mNewRev = Revision::newFromTitle($this->getTitle(), false, Revision::READ_NORMAL); } if (!$this->mNewRev instanceof Revision) { return false; } // Update the new revision ID in case it was 0 (makes life easier doing UI stuff) $this->mNewid = $this->mNewRev->getId(); $this->mNewPage = $this->mNewRev->getTitle(); // Load the old revision object $this->mOldRev = false; if ($this->mOldid) { $this->mOldRev = Revision::newFromId($this->mOldid); } elseif ($this->mOldid === 0) { $rev = $this->mNewRev->getPrevious(); if ($rev) { $this->mOldid = $rev->getId(); $this->mOldRev = $rev; } else { // No previous revision; mark to show as first-version only. $this->mOldid = false; $this->mOldRev = false; } } /* elseif ( $this->mOldid === false ) leave mOldRev false; */ if (is_null($this->mOldRev)) { return false; } if ($this->mOldRev) { $this->mOldPage = $this->mOldRev->getTitle(); } // Load tags information for both revisions $dbr = wfGetDB(DB_SLAVE); if ($this->mOldid !== false) { $this->mOldTags = $dbr->selectField('tag_summary', 'ts_tags', array('ts_rev_id' => $this->mOldid), __METHOD__); } else { $this->mOldTags = false; } $this->mNewTags = $dbr->selectField('tag_summary', 'ts_tags', array('ts_rev_id' => $this->mNewid), __METHOD__); return true; }
/** * Get text of an article from database * Does *NOT* follow redirects. * * @return mixed string containing article contents, or false if null */ function fetchContent() { if ($this->mContentLoaded) { return $this->mContent; } wfProfileIn(__METHOD__); $this->mContentLoaded = true; $oldid = $this->getOldID(); # Pre-fill content with error message so that if something # fails we'll have something telling us what we intended. $t = $this->getTitle()->getPrefixedText(); $d = $oldid ? wfMsgExt('missingarticle-rev', array('escape'), $oldid) : ''; $this->mContent = wfMsgNoTrans('missing-article', $t, $d); if ($oldid) { # $this->mRevision might already be fetched by getOldIDFromRequest() if (!$this->mRevision) { $this->mRevision = Revision::newFromId($oldid); if (!$this->mRevision) { wfDebug(__METHOD__ . " failed to retrieve specified revision, id {$oldid}\n"); Wikia::log(__METHOD__, 1, "failed to retrieve specified revision, title '{$t}', id {$oldid}", true); # Wikia change - @author macbre wfProfileOut(__METHOD__); return false; } } } else { if (!$this->mPage->getLatest()) { wfDebug(__METHOD__ . " failed to find page data for title " . $this->getTitle()->getPrefixedText() . "\n"); wfProfileOut(__METHOD__); return false; } $this->mRevision = $this->mPage->getRevision(); if (!$this->mRevision) { wfDebug(__METHOD__ . " failed to retrieve current page, rev_id " . $this->mPage->getLatest() . "\n"); Wikia::log(__METHOD__, 3, "failed to retrieve current page, title '{$t}', rev_id " . $this->mPage->getLatest(), true); # Wikia change - @author macbre wfProfileOut(__METHOD__); return false; } } // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks. // We should instead work with the Revision object when we need it... $this->mContent = $this->mRevision->getText(Revision::FOR_THIS_USER); // Loads if user is allowed $this->mRevIdFetched = $this->mRevision->getId(); wfRunHooks('ArticleAfterFetchContent', array(&$this, &$this->mContent)); wfProfileOut(__METHOD__); return $this->mContent; }
/** * Find the latest revision of the article that does not contain spam and revert to it */ function cleanupArticle(Revision $rev, $regexes, $match) { $title = $rev->getTitle(); $revId = $rev->getId(); while ($rev) { $matches = false; foreach ($regexes as $regex) { $matches = $matches || preg_match($regex, $rev->getText()); } if (!$matches) { // Didn't find any spam break; } # Revision::getPrevious can't be used in this way before MW 1.6 (Revision.php 1.26) #$rev = $rev->getPrevious(); $revId = $title->getPreviousRevisionID($revId); if ($revId) { $rev = Revision::newFromTitle($title, $revId); } else { $rev = false; } } $dbw = wfGetDB(DB_MASTER); $dbw->begin(); if (!$rev) { // Didn't find a non-spammy revision, delete the page /* print "All revisions are spam, deleting...\n"; $article = new Article( $title ); $article->doDeleteArticle( "All revisions matched the spam blacklist" ); */ // Too scary, blank instead print "All revisions are spam, blanking...\n"; $text = ''; $comment = "All revisions matched the spam blacklist ({$match}), blanking"; } else { // Revert to this revision $text = $rev->getText(); $comment = "Cleaning up links to {$match}"; } $wikiPage = new WikiPage($title); $wikiPage->doEdit($text, $comment); $dbw->commit(); }
/** * Render the inline difference between two revisions * using InlineDiffEngine */ function showDiff() { $ctx = MobileContext::singleton(); $prevId = $this->prevRev ? $this->prevRev->getId() : 0; $unhide = (bool) $this->getRequest()->getVal('unhide'); $contentHandler = $this->rev->getContentHandler(); $de = $contentHandler->createDifferenceEngine($this->getContext(), $prevId, $this->revId); // HACK: if (get_class($de) == 'DifferenceEngine') { $de = new $this->diffClass($this->getContext(), $prevId, $this->revId, 0, false, $unhide); } else { $de->showDiffPage(); return; } $this->mDiffEngine = $de; $diff = $de->getDiffBody(); if (!$prevId) { $audience = $unhide ? Revision::FOR_THIS_USER : Revision::FOR_PUBLIC; $diff = '<ins>' . nl2br(htmlspecialchars($this->rev->getText($audience))) . '</ins>'; } $warnings = $de->getWarningMessageText(); if ($warnings) { $warnings = MobileUI::warningBox($warnings); } $this->getOutput()->addHtml($warnings . '<div id="mw-mf-minidiff">' . $diff . '</div>'); $prev = $this->rev->getPrevious(); $next = $this->rev->getNext(); if ($prev || $next) { $history = Html::openElement('ul', array('class' => 'hlist revision-history-links')); if ($prev) { $history .= Html::openElement('li') . Html::element('a', array('href' => SpecialPage::getTitleFor('MobileDiff', $prev->getId())->getLocalUrl()), $this->msg('previousdiff')) . Html::closeElement('li'); } if ($next) { $history .= Html::openElement('li') . Html::element('a', array('href' => SpecialPage::getTitleFor('MobileDiff', $next->getId())->getLocalUrl()), $this->msg('nextdiff')) . Html::closeElement('li'); } $history .= Html::closeElement('ul'); $this->getOutput()->addHtml($history); } }
/** * Executes the real stuff. No checks done! * @param User $user * @param Revision $revision * @param null|string $comment * @return Bool, whether the action was recorded. */ public static function doReview(User $user, Revision $revision, $comment = null) { $dbw = wfGetDB(DB_MASTER); $table = 'translate_reviews'; $row = array('trr_user' => $user->getId(), 'trr_page' => $revision->getPage(), 'trr_revision' => $revision->getId()); $options = array('IGNORE'); $dbw->insert($table, $row, __METHOD__, $options); if (!$dbw->affectedRows()) { return false; } $title = $revision->getTitle(); $entry = new ManualLogEntry('translationreview', 'message'); $entry->setPerformer($user); $entry->setTarget($title); $entry->setComment($comment); $entry->setParameters(array('4::revision' => $revision->getId())); $logid = $entry->insert(); $entry->publish($logid); $handle = new MessageHandle($title); Hooks::run('TranslateEventTranslationReview', array($handle)); return true; }
/** * @param Revision $rev * @param string $prefix * @return string */ private function diffHeader($rev, $prefix) { $isDeleted = !($rev->getId() && $rev->getTitle()); if ($isDeleted) { /// @todo FIXME: $rev->getTitle() is null for deleted revs...? $targetPage = $this->getPageTitle(); $targetQuery = array('target' => $this->mTargetObj->getPrefixedText(), 'timestamp' => wfTimestamp(TS_MW, $rev->getTimestamp())); } else { /// @todo FIXME: getId() may return non-zero for deleted revs... $targetPage = $rev->getTitle(); $targetQuery = array('oldid' => $rev->getId()); } // Add show/hide deletion links if available $user = $this->getUser(); $lang = $this->getLanguage(); $rdel = Linker::getRevDeleteLink($user, $rev, $this->mTargetObj); if ($rdel) { $rdel = " {$rdel}"; } $minor = $rev->isMinor() ? ChangesList::flag('minor') : ''; $tags = wfGetDB(DB_SLAVE)->selectField('tag_summary', 'ts_tags', array('ts_rev_id' => $rev->getId()), __METHOD__); $tagSummary = ChangeTags::formatSummaryRow($tags, 'deleteddiff'); // FIXME This is reimplementing DifferenceEngine#getRevisionHeader // and partially #showDiffPage, but worse return '<div id="mw-diff-' . $prefix . 'title1"><strong>' . Linker::link($targetPage, $this->msg('revisionasof', $lang->userTimeAndDate($rev->getTimestamp(), $user), $lang->userDate($rev->getTimestamp(), $user), $lang->userTime($rev->getTimestamp(), $user))->escaped(), array(), $targetQuery) . '</strong></div>' . '<div id="mw-diff-' . $prefix . 'title2">' . Linker::revUserTools($rev) . '<br />' . '</div>' . '<div id="mw-diff-' . $prefix . 'title3">' . $minor . Linker::revComment($rev) . $rdel . '<br />' . '</div>' . '<div id="mw-diff-' . $prefix . 'title5">' . $tagSummary[0] . '<br />' . '</div>'; }
/** * Render the contribution of the pagerevision (time, bytes added/deleted, pagename comment) * @param Revision $rev */ protected function showContributionsRow(Revision $rev) { $user = $this->getUser(); $userId = $rev->getUser(Revision::FOR_THIS_USER, $user); if ($userId === 0) { $username = IP::prettifyIP($rev->getUserText(Revision::RAW)); $isAnon = true; } else { $username = $rev->getUserText(Revision::FOR_THIS_USER, $user); $isAnon = false; } // FIXME: Style differently user comment when this is the case if ($rev->userCan(Revision::DELETED_COMMENT, $user)) { $comment = $rev->getComment(Revision::FOR_THIS_USER, $user); $comment = $this->formatComment($comment, $this->title); } else { $comment = $this->msg('rev-deleted-comment')->plain(); } $ts = $rev->getTimestamp(); $this->renderListHeaderWhereNeeded($this->getLanguage()->userDate($ts, $this->getUser())); $ts = new MWTimestamp($ts); if ($rev->userCan(Revision::DELETED_TEXT, $user)) { $diffLink = SpecialPage::getTitleFor('MobileDiff', $rev->getId())->getLocalUrl(); } else { $diffLink = false; } // FIXME: Style differently user comment when this is the case if (!$rev->userCan(Revision::DELETED_USER, $user)) { $username = $this->msg('rev-deleted-user')->plain(); } $bytes = null; if (isset($this->prevLengths[$rev->getParentId()])) { $bytes = $rev->getSize() - $this->prevLengths[$rev->getParentId()]; } $isMinor = $rev->isMinor(); $this->renderFeedItemHtml($ts, $diffLink, $username, $comment, $rev->getTitle(), $isAnon, $bytes, $isMinor); }
/** * saveComplete -- hook * * @static * @access public * * @param WikiPage $oArticle, * @param User $User * * @author Piotr Molski (MoLi) * @return true */ public static function saveComplete(&$oArticle, &$oUser, $text, $summary, $minor, $undef1, $undef2, &$flags, Revision $oRevision, &$status, $baseRevId) { global $wgCityId; wfProfileIn(__METHOD__); $revId = $pageId = 0; if (is_object($oArticle) && $oUser instanceof User) { # revision if ($oRevision instanceof Revision) { $revId = $oRevision->getId(); $pageId = $oRevision->getPage(); } if (empty($revId)) { $revId = $oArticle->getTitle()->getLatestRevID(Title::GAID_FOR_UPDATE); } # article if (empty($pageId) || $pageId < 0) { $pageId = $oArticle->getID(); } if ($revId > 0 && $pageId > 0) { $key = isset($status->value['new']) && $status->value['new'] == 1 ? 'create' : 'edit'; $oScribeProducer = new ScribeProducer($key, $pageId, $revId, 0, !empty($undef1) ? 1 : 0); if (is_object($oScribeProducer)) { $oScribeProducer->send_log(); } } else { Wikia::log(__METHOD__, "error", "Cannot send log via scribe ({$wgCityId}): revision not found for page: {$pageId}"); } } else { $isArticle = is_object($oArticle); $isUser = is_object($oUser); Wikia::log(__METHOD__, "error", "Cannot send log via scribe ({$wgCityId}): invalid user: {$isUser}, invalid article: {$isArticle}"); } wfProfileOut(__METHOD__); return true; }
protected function getParserOutput(WikiPage $page, Revision $rev) { $parserOptions = $page->makeParserOptions($this->getContext()); if (!$rev->isCurrent() || !$rev->getTitle()->quickUserCan("edit")) { $parserOptions->setEditSection(false); } $parserOutput = $page->getParserOutput($parserOptions, $rev->getId()); return $parserOutput; }
/** * Create radio buttons for page history * * @param Revision $rev * @param bool $firstInList Is this version the first one? * * @return string HTML output for the radio buttons */ function diffButtons($rev, $firstInList) { if ($this->getNumRows() > 1) { $id = $rev->getId(); $radio = array('type' => 'radio', 'value' => $id); /** @todo Move title texts to javascript */ if ($firstInList) { $first = Xml::element('input', array_merge($radio, array('style' => 'visibility:hidden', 'name' => 'oldid', 'id' => 'mw-oldid-null'))); $checkmark = array('checked' => 'checked'); } else { # Check visibility of old revisions if (!$rev->userCan(Revision::DELETED_TEXT, $this->getUser())) { $radio['disabled'] = 'disabled'; $checkmark = array(); // We will check the next possible one } elseif (!$this->oldIdChecked) { $checkmark = array('checked' => 'checked'); $this->oldIdChecked = $id; } else { $checkmark = array(); } $first = Xml::element('input', array_merge($radio, $checkmark, array('name' => 'oldid', 'id' => "mw-oldid-{$id}"))); $checkmark = array(); } $second = Xml::element('input', array_merge($radio, $checkmark, array('name' => 'diff', 'id' => "mw-diff-{$id}"))); return $first . $second; } else { return ''; } }
/** * Purge caches on page update etc * * @param Title $title * @param Revision|null $revision Revision that was just saved, may be null */ public static function onArticleEdit(Title $title, Revision $revision = null) { // Invalidate caches of articles which include this page DeferredUpdates::addUpdate(new HTMLCacheUpdate($title, 'templatelinks')); // Invalidate the caches of all pages which redirect here DeferredUpdates::addUpdate(new HTMLCacheUpdate($title, 'redirect')); MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle($title); // Purge CDN for this page only $title->purgeSquid(); // Clear file cache for this page only HTMLFileCache::clearFileCache($title); $revid = $revision ? $revision->getId() : null; DeferredUpdates::addCallableUpdate(function () use($title, $revid) { InfoAction::invalidateCache($title, $revid); }); }
protected static function getDiffRevMsgAndClass(Revision $rev, FlaggedRevision $srev = null) { $tier = FlaggedRevision::getRevQuality($rev->getId()); if ($tier !== false) { $msg = $tier ? 'revreview-hist-quality' : 'revreview-hist-basic'; } else { $msg = $srev && $rev->getTimestamp() > $srev->getRevTimestamp() ? 'revreview-hist-pending' : 'revreview-hist-draft'; } $css = FlaggedRevsXML::getQualityColor($tier); return array($msg, $css); }
/** * @param Skin $skin * @param Title $title * @param Revision $revision * @param Revision $undoAfterRevision * @return String Undo Link */ public static function generateUndoLink($skin, $title, $revision, $undoAfterRevision) { if (!$revision instanceof Revision || !$undoAfterRevision instanceof Revision || !$title instanceof Title || !$skin instanceof Skin) { return null; } # Create undo tooltip for the first (=latest) line only $undoTooltip = $revision->isCurrent() ? array('title' => wfMsg('tooltip-undo')) : array(); $undolink = $skin->link($title, wfMsgHtml('editundo'), $undoTooltip, array('action' => 'edit', 'undoafter' => $undoAfterRevision->getId(), 'undo' => $revision->getId()), array('known', 'noclasses')); return "<span class=\"mw-history-undo\">{$undolink}</span>"; }
/** * Extract information from the Revision * * @param Revision $revision * @param object $row Should have a field 'ts_tags' if $this->fld_tags is set * @return array */ protected function extractRevisionInfo(Revision $revision, $row) { $title = $revision->getTitle(); $user = $this->getUser(); $vals = array(); $anyHidden = false; if ($this->fld_ids) { $vals['revid'] = intval($revision->getId()); if (!is_null($revision->getParentId())) { $vals['parentid'] = intval($revision->getParentId()); } } if ($this->fld_flags) { $vals['minor'] = $revision->isMinor(); } if ($this->fld_user || $this->fld_userid) { if ($revision->isDeleted(Revision::DELETED_USER)) { $vals['userhidden'] = true; $anyHidden = true; } if ($revision->userCan(Revision::DELETED_USER, $user)) { if ($this->fld_user) { $vals['user'] = $revision->getUserText(Revision::RAW); } $userid = $revision->getUser(Revision::RAW); if (!$userid) { $vals['anon'] = true; } if ($this->fld_userid) { $vals['userid'] = $userid; } } } if ($this->fld_timestamp) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $revision->getTimestamp()); } if ($this->fld_size) { if (!is_null($revision->getSize())) { $vals['size'] = intval($revision->getSize()); } else { $vals['size'] = 0; } } if ($this->fld_sha1) { if ($revision->isDeleted(Revision::DELETED_TEXT)) { $vals['sha1hidden'] = true; $anyHidden = true; } if ($revision->userCan(Revision::DELETED_TEXT, $user)) { if ($revision->getSha1() != '') { $vals['sha1'] = wfBaseConvert($revision->getSha1(), 36, 16, 40); } else { $vals['sha1'] = ''; } } } if ($this->fld_contentmodel) { $vals['contentmodel'] = $revision->getContentModel(); } if ($this->fld_comment || $this->fld_parsedcomment) { if ($revision->isDeleted(Revision::DELETED_COMMENT)) { $vals['commenthidden'] = true; $anyHidden = true; } if ($revision->userCan(Revision::DELETED_COMMENT, $user)) { $comment = $revision->getComment(Revision::RAW); if ($this->fld_comment) { $vals['comment'] = $comment; } if ($this->fld_parsedcomment) { $vals['parsedcomment'] = Linker::formatComment($comment, $title); } } } if ($this->fld_tags) { if ($row->ts_tags) { $tags = explode(',', $row->ts_tags); ApiResult::setIndexedTagName($tags, 'tag'); $vals['tags'] = $tags; } else { $vals['tags'] = array(); } } $content = null; global $wgParser; if ($this->fetchContent) { $content = $revision->getContent(Revision::FOR_THIS_USER, $this->getUser()); // Expand templates after getting section content because // template-added sections don't count and Parser::preprocess() // will have less input if ($content && $this->section !== false) { $content = $content->getSection($this->section, false); if (!$content) { $this->dieUsage("There is no section {$this->section} in r" . $revision->getId(), 'nosuchsection'); } } if ($revision->isDeleted(Revision::DELETED_TEXT)) { $vals['texthidden'] = true; $anyHidden = true; } elseif (!$content) { $vals['textmissing'] = true; } } if ($this->fld_content && $content) { $text = null; if ($this->generateXML) { if ($content->getModel() === CONTENT_MODEL_WIKITEXT) { $t = $content->getNativeData(); # note: don't set $text $wgParser->startExternalParse($title, ParserOptions::newFromContext($this->getContext()), Parser::OT_PREPROCESS); $dom = $wgParser->preprocessToDom($t); if (is_callable(array($dom, 'saveXML'))) { $xml = $dom->saveXML(); } else { $xml = $dom->__toString(); } $vals['parsetree'] = $xml; } else { $vals['badcontentformatforparsetree'] = true; $this->setWarning("Conversion to XML is supported for wikitext only, " . $title->getPrefixedDBkey() . " uses content model " . $content->getModel()); } } if ($this->expandTemplates && !$this->parseContent) { #XXX: implement template expansion for all content types in ContentHandler? if ($content->getModel() === CONTENT_MODEL_WIKITEXT) { $text = $content->getNativeData(); $text = $wgParser->preprocess($text, $title, ParserOptions::newFromContext($this->getContext())); } else { $this->setWarning("Template expansion is supported for wikitext only, " . $title->getPrefixedDBkey() . " uses content model " . $content->getModel()); $vals['badcontentformat'] = true; $text = false; } } if ($this->parseContent) { $po = $content->getParserOutput($title, $revision->getId(), ParserOptions::newFromContext($this->getContext())); $text = $po->getText(); } if ($text === null) { $format = $this->contentFormat ? $this->contentFormat : $content->getDefaultFormat(); $model = $content->getModel(); if (!$content->isSupportedFormat($format)) { $name = $title->getPrefixedDBkey(); $this->setWarning("The requested format {$this->contentFormat} is not " . "supported for content model {$model} used by {$name}"); $vals['badcontentformat'] = true; $text = false; } else { $text = $content->serialize($format); // always include format and model. // Format is needed to deserialize, model is needed to interpret. $vals['contentformat'] = $format; $vals['contentmodel'] = $model; } } if ($text !== false) { ApiResult::setContentValue($vals, 'content', $text); } } if ($content && (!is_null($this->diffto) || !is_null($this->difftotext))) { static $n = 0; // Number of uncached diffs we've had if ($n < $this->getConfig()->get('APIMaxUncachedDiffs')) { $vals['diff'] = array(); $context = new DerivativeContext($this->getContext()); $context->setTitle($title); $handler = $revision->getContentHandler(); if (!is_null($this->difftotext)) { $model = $title->getContentModel(); if ($this->contentFormat && !ContentHandler::getForModelID($model)->isSupportedFormat($this->contentFormat)) { $name = $title->getPrefixedDBkey(); $this->setWarning("The requested format {$this->contentFormat} is not " . "supported for content model {$model} used by {$name}"); $vals['diff']['badcontentformat'] = true; $engine = null; } else { $difftocontent = ContentHandler::makeContent($this->difftotext, $title, $model, $this->contentFormat); $engine = $handler->createDifferenceEngine($context); $engine->setContent($content, $difftocontent); } } else { $engine = $handler->createDifferenceEngine($context, $revision->getID(), $this->diffto); $vals['diff']['from'] = $engine->getOldid(); $vals['diff']['to'] = $engine->getNewid(); } if ($engine) { $difftext = $engine->getDiffBody(); ApiResult::setContentValue($vals['diff'], 'body', $difftext); if (!$engine->wasCacheHit()) { $n++; } } } else { $vals['diff']['notcached'] = true; } } if ($anyHidden && $revision->isDeleted(Revision::DELETED_RESTRICTED)) { $vals['suppressed'] = true; } return $vals; }
/** * @param $title Title * @param $revision Revision * @param $fname string * @return void */ public static function runForTitleInternal(Title $title, Revision $revision, $fname) { wfProfileIn($fname); $content = $revision->getContent(Revision::RAW); if (!$content) { // if there is no content, pretend the content is empty $content = $revision->getContentHandler()->makeEmptyContent(); } // Revision ID must be passed to the parser output to get revision variables correct $parserOutput = $content->getParserOutput($title, $revision->getId(), null, false); $updates = $content->getSecondaryDataUpdates($title, null, false, $parserOutput); DataUpdate::runUpdates($updates); InfoAction::invalidateCache($title); wfProfileOut($fname); }
/** * 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 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 $wgEnableParserCache; wfProfileIn(__METHOD__); $options += array('changed' => true, 'created' => false, 'oldcountable' => null); $content = $revision->getContent(); # 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->prepareContentForEdit($content, $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 and other secondary data $updates = $content->getSecondaryDataUpdates($this->getTitle(), null, true, $editInfo->output); DataUpdate::runUpdates($updates); 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__); } } if (!$this->mTitle->exists()) { wfProfileOut(__METHOD__); return; } $id = $this->getId(); $title = $this->mTitle->getPrefixedDBkey(); $shortTitle = $this->mTitle->getDBkey(); 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; } DeferredUpdates::addUpdate(new SiteStatsUpdate(0, 1, $good, $total)); DeferredUpdates::addUpdate(new SearchUpdate($id, $title, $content->getTextForSearchIndex())); #@TODO: let the search engine decide what to do with the content object # 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, $revision); } elseif ($other->isLoggedIn()) { $other->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->getWikitextForTransclusion(); if ($msgtext === false || $msgtext === null) { $msgtext = ''; } MessageCache::singleton()->replace($shortTitle, $msgtext); } if ($options['created']) { self::onArticleCreate($this->mTitle); } else { self::onArticleEdit($this->mTitle); } wfProfileOut(__METHOD__); }
/** * 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 object * @param $user User object that did the revision * @param array $options 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 $wgEnableParserCache; wfProfileIn( __METHOD__ ); $options += array( 'changed' => true, 'created' => 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->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 and other secondary data if ( $content ) { $recursive = $options['changed']; // bug 50785 $updates = $content->getSecondaryDataUpdates( $this->getTitle(), null, $recursive, $editInfo->output ); DataUpdate::runUpdates( $updates ); } 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 RecentChange::purgeExpiredChanges(); } } if ( !$this->exists() ) { wfProfileOut( __METHOD__ ); return; } $id = $this->getId(); $title = $this->mTitle->getPrefixedDBkey(); $shortTitle = $this->mTitle->getDBkey(); 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; } DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 1, $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 ( wfRunHooks( '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 ); } else { self::onArticleEdit( $this->mTitle ); } wfProfileOut( __METHOD__ ); }
/** * Update the revision's recentchanges record if fields have been hidden * @param Revision $rev * @param int $bitfield new rev_deleted bitfield value */ function updateRecentChangesEdits($rev, $bitfield) { $this->dbw->update('recentchanges', array('rc_deleted' => $bitfield, 'rc_patrolled' => 1), array('rc_this_oldid' => $rev->getId(), 'rc_timestamp' => $this->dbw->timestamp($rev->getTimestamp())), __METHOD__); }
function formatRevisionRow($row) { $rev = new Revision($row); $stxt = ''; $last = $this->msg('last')->escaped(); $ts = wfTimestamp(TS_MW, $row->rev_timestamp); $checkBox = Xml::radio('mergepoint', $ts, $this->mTimestamp === $ts); $user = $this->getUser(); $pageLink = Linker::linkKnown($rev->getTitle(), htmlspecialchars($this->getLanguage()->userTimeAndDate($ts, $user)), [], ['oldid' => $rev->getId()]); if ($rev->isDeleted(Revision::DELETED_TEXT)) { $pageLink = '<span class="history-deleted">' . $pageLink . '</span>'; } # Last link if (!$rev->userCan(Revision::DELETED_TEXT, $user)) { $last = $this->msg('last')->escaped(); } elseif (isset($this->prevId[$row->rev_id])) { $last = Linker::linkKnown($rev->getTitle(), $this->msg('last')->escaped(), [], ['diff' => $row->rev_id, 'oldid' => $this->prevId[$row->rev_id]]); } $userLink = Linker::revUserTools($rev); $size = $row->rev_len; if (!is_null($size)) { $stxt = Linker::formatRevisionSize($size); } $comment = Linker::revComment($rev); return Html::rawElement('li', [], $this->msg('mergehistory-revisionrow')->rawParams($checkBox, $last, $pageLink, $userLink, $stxt, $comment)->escaped()); }
/** * Show a row in history, including: * time of edit * changed bytes * name of editor * comment of edit * @param Revision $rev Revision id of the row wants to show * @param Revision|null $prev Revision id of previous Revision to display the difference */ protected function showRow(Revision $rev, $prev) { $user = $this->getUser(); $userId = $rev->getUser(Revision::FOR_THIS_USER, $user); if ($userId === 0) { $username = IP::prettifyIP($rev->getUserText(Revision::RAW)); $isAnon = true; } else { $username = $rev->getUserText(Revision::FOR_THIS_USER, $user); $isAnon = false; } // FIXME: Style differently user comment when this is the case if ($rev->userCan(Revision::DELETED_COMMENT, $user)) { $comment = $rev->getComment(Revision::FOR_THIS_USER, $user); $comment = $this->formatComment($comment, $this->title); } else { $comment = $this->msg('rev-deleted-comment')->plain(); } $ts = $rev->getTimestamp(); $this->renderListHeaderWhereNeeded($this->getLanguage()->userDate($ts, $this->getUser())); $ts = new MWTimestamp($ts); $canSeeText = $rev->userCan(Revision::DELETED_TEXT, $user); if ($canSeeText && $prev && $prev->userCan(Revision::DELETED_TEXT, $user)) { $diffLink = SpecialPage::getTitleFor('MobileDiff', $rev->getId())->getLocalUrl(); } elseif ($canSeeText && $rev->getTitle() !== null) { $diffLink = $rev->getTitle()->getLocalUrl(array('oldid' => $rev->getId())); } else { $diffLink = false; } // FIXME: Style differently user comment when this is the case if (!$rev->userCan(Revision::DELETED_USER, $user)) { $username = $this->msg('rev-deleted-user')->plain(); } // When the page is named there is no need to print it in output if ($this->title) { $title = null; } else { $title = $rev->getTitle(); } $bytes = $rev->getSize(); if ($prev) { $bytes -= $prev->getSize(); } $isMinor = $rev->isMinor(); $this->renderFeedItemHtml($ts, $diffLink, $username, $comment, $title, $isAnon, $bytes, $isMinor); }
/** * Get a revision-deletion link, or disabled link, or nothing, depending * on user permissions & the settings on the revision. * * Will use forward-compatible revision ID in the Special:RevDelete link * if possible, otherwise the timestamp-based ID which may break after * undeletion. * * @param User $user * @param Revision $rev * @param Revision $title * @return string HTML fragment */ public static function getRevDeleteLink(User $user, Revision $rev, Title $title) { $canHide = $user->isAllowed('deleterevision'); if (!$canHide && !($rev->getVisibility() && $user->isAllowed('deletedhistory'))) { return ''; } if (!$rev->userCan(Revision::DELETED_RESTRICTED, $user)) { return Linker::revDeleteLinkDisabled($canHide); // revision was hidden from sysops } else { if ($rev->getId()) { // RevDelete links using revision ID are stable across // page deletion and undeletion; use when possible. $query = array('type' => 'revision', 'target' => $title->getPrefixedDBkey(), 'ids' => $rev->getId()); } else { // Older deleted entries didn't save a revision ID. // We have to refer to these by timestamp, ick! $query = array('type' => 'archive', 'target' => $title->getPrefixedDBkey(), 'ids' => $rev->getTimestamp()); } return Linker::revDeleteLink($query, $rev->isDeleted(Revision::DELETED_RESTRICTED), $canHide); } }
private function extractRowInfo($row) { $revision = new Revision($row); $title = $revision->getTitle(); $vals = array(); if ($this->fld_ids) { $vals['revid'] = intval($revision->getId()); // $vals['oldid'] = intval( $row->rev_text_id ); // todo: should this be exposed? if (!is_null($revision->getParentId())) { $vals['parentid'] = intval($revision->getParentId()); } } if ($this->fld_flags && $revision->isMinor()) { $vals['minor'] = ''; } if ($this->fld_user || $this->fld_userid) { if ($revision->isDeleted(Revision::DELETED_USER)) { $vals['userhidden'] = ''; } else { if ($this->fld_user) { $vals['user'] = $revision->getUserText(); } $userid = $revision->getUser(); if (!$userid) { $vals['anon'] = ''; } if ($this->fld_userid) { $vals['userid'] = $userid; } } } if ($this->fld_timestamp) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $revision->getTimestamp()); } if ($this->fld_size) { if (!is_null($revision->getSize())) { $vals['size'] = intval($revision->getSize()); } else { $vals['size'] = 0; } } if ($this->fld_sha1) { if ($revision->getSha1() != '') { $vals['sha1'] = wfBaseConvert($revision->getSha1(), 36, 16, 40); } else { $vals['sha1'] = ''; } } if ($this->fld_comment || $this->fld_parsedcomment) { if ($revision->isDeleted(Revision::DELETED_COMMENT)) { $vals['commenthidden'] = ''; } else { $comment = $revision->getComment(); if ($this->fld_comment) { $vals['comment'] = $comment; } if ($this->fld_parsedcomment) { $vals['parsedcomment'] = Linker::formatComment($comment, $title); } } } if ($this->fld_tags) { if ($row->ts_tags) { $tags = explode(',', $row->ts_tags); $this->getResult()->setIndexedTagName($tags, 'tag'); $vals['tags'] = $tags; } else { $vals['tags'] = array(); } } if (!is_null($this->token)) { $tokenFunctions = $this->getTokenFunctions(); foreach ($this->token as $t) { $val = call_user_func($tokenFunctions[$t], $title->getArticleID(), $title, $revision); if ($val === false) { $this->setWarning("Action '{$t}' is not allowed for the current user"); } else { $vals[$t . 'token'] = $val; } } } $text = null; global $wgParser; if ($this->fld_content || !is_null($this->difftotext)) { $text = $revision->getText(); // Expand templates after getting section content because // template-added sections don't count and Parser::preprocess() // will have less input if ($this->section !== false) { $text = $wgParser->getSection($text, $this->section, false); if ($text === false) { $this->dieUsage("There is no section {$this->section} in r" . $revision->getId(), 'nosuchsection'); } } } if ($this->fld_content && !$revision->isDeleted(Revision::DELETED_TEXT)) { if ($this->generateXML) { $wgParser->startExternalParse($title, ParserOptions::newFromContext($this->getContext()), OT_PREPROCESS); $dom = $wgParser->preprocessToDom($text); if (is_callable(array($dom, 'saveXML'))) { $xml = $dom->saveXML(); } else { $xml = $dom->__toString(); } $vals['parsetree'] = $xml; } if ($this->expandTemplates && !$this->parseContent) { $text = $wgParser->preprocess($text, $title, ParserOptions::newFromContext($this->getContext())); } if ($this->parseContent) { $text = $wgParser->parse($text, $title, ParserOptions::newFromContext($this->getContext()))->getText(); } ApiResult::setContent($vals, $text); } elseif ($this->fld_content) { $vals['texthidden'] = ''; } if (!is_null($this->diffto) || !is_null($this->difftotext)) { global $wgAPIMaxUncachedDiffs; static $n = 0; // Number of uncached diffs we've had if ($n < $wgAPIMaxUncachedDiffs) { $vals['diff'] = array(); $context = new DerivativeContext($this->getContext()); $context->setTitle($title); if (!is_null($this->difftotext)) { $engine = new DifferenceEngine($context); $engine->setText($text, $this->difftotext); } else { $engine = new DifferenceEngine($context, $revision->getID(), $this->diffto); $vals['diff']['from'] = $engine->getOldid(); $vals['diff']['to'] = $engine->getNewid(); } $difftext = $engine->getDiffBody(); ApiResult::setContent($vals['diff'], $difftext); if (!$engine->wasCacheHit()) { $n++; } } else { $vals['diff']['notcached'] = ''; } } return $vals; }
/** * DiffViewHeader hook handler * @see https://www.mediawiki.org/wiki/Manual:Hooks/DiffViewHeader * * Redirect Diff page to mobile version if appropriate * * @param DifferenceEngine $diff DifferenceEngine object that's calling * @param Revision $oldRev Revision object of the "old" revision (may be null/invalid) * @param Revision $newRev Revision object of the "new" revision * @return bool */ public static function onDiffViewHeader($diff, $oldRev, $newRev) { $context = MobileContext::singleton(); // Only do redirects to MobileDiff if user is in mobile view and it's not a special page if ($context->shouldDisplayMobileView() && !$diff->getContext()->getTitle()->isSpecialPage()) { $output = $context->getOutput(); $newRevId = $newRev->getId(); // The MobileDiff page currently only supports showing a single revision, so // only redirect to MobileDiff if we are sure this isn't a multi-revision diff. if ($oldRev) { // Get the revision immediately before the new revision $prevRev = $newRev->getPrevious(); if ($prevRev) { $prevRevId = $prevRev->getId(); $oldRevId = $oldRev->getId(); if ($prevRevId === $oldRevId) { $output->redirect(SpecialPage::getTitleFor('MobileDiff', $newRevId)->getFullURL()); } } } else { $output->redirect(SpecialPage::getTitleFor('MobileDiff', $newRevId)->getFullURL()); } } return true; }
function formatRevisionRow($row) { global $wgUser, $wgLang; $rev = new Revision($row); $stxt = ''; $last = $this->message['last']; $ts = wfTimestamp(TS_MW, $row->rev_timestamp); $checkBox = Xml::radio("mergepoint", $ts, false); $pageLink = $this->sk->makeKnownLinkObj($rev->getTitle(), htmlspecialchars($wgLang->timeanddate($ts)), 'oldid=' . $rev->getId()); if ($rev->isDeleted(Revision::DELETED_TEXT)) { $pageLink = '<span class="history-deleted">' . $pageLink . '</span>'; } # Last link if (!$rev->userCan(Revision::DELETED_TEXT)) { $last = $this->message['last']; } else { if (isset($this->prevId[$row->rev_id])) { $last = $this->sk->makeKnownLinkObj($rev->getTitle(), $this->message['last'], "diff=" . $row->rev_id . "&oldid=" . $this->prevId[$row->rev_id]); } } $userLink = $this->sk->revUserTools($rev); if (!is_null($size = $row->rev_len)) { $stxt = $this->sk->formatRevisionSize($size); } $comment = $this->sk->revComment($rev); return "<li>{$checkBox} ({$last}) {$pageLink} . . {$userLink} {$stxt} {$comment}</li>"; }