/** * 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 the text of the current revision. No side-effects... * * @param int $audience One of: * Revision::FOR_PUBLIC to be displayed to all users * Revision::FOR_THIS_USER to be displayed to the given user * Revision::RAW get the text regardless of permissions * @param User $user User object to check for, only if FOR_THIS_USER is passed * to the $audience parameter * @return string|bool The text of the current revision * @deprecated since 1.21, getContent() should be used instead. */ public function getText($audience = Revision::FOR_PUBLIC, User $user = null) { wfDeprecated(__METHOD__, '1.21'); $this->loadLastEdit(); if ($this->mLastRevision) { return $this->mLastRevision->getText($audience, $user); } return false; }
/** * Get the text of the current revision. No side-effects... * * @param $audience Integer: one of: * Revision::FOR_PUBLIC to be displayed to all users * Revision::FOR_THIS_USER to be displayed to the given user * Revision::RAW get the text regardless of permissions * @param $user User object to check for, only if FOR_THIS_USER is passed * to the $audience parameter * @return String|false The text of the current revision * @deprecated as of 1.21, getContent() should be used instead. */ public function getText( $audience = Revision::FOR_PUBLIC, User $user = null ) { // @todo deprecated, replace usage! ContentHandler::deprecated( __METHOD__, '1.21' ); $this->loadLastEdit(); if ( $this->mLastRevision ) { return $this->mLastRevision->getText( $audience, $user ); } return false; }
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'); }
/** * 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); } }
/** * Get the text that needs to be saved in order to undo all revisions * between $undo and $undoafter. Revisions must belong to the same page, * must exist and must not be deleted * @param $undo Revision * @param $undoafter Revision Must be an earlier revision than $undo * @return mixed string on success, false on failure */ public function getUndoText(Revision $undo, Revision $undoafter = null) { $currentRev = Revision::newFromTitle($this->mTitle); if (!$currentRev) { return false; // no page } $undo_text = $undo->getText(); $undoafter_text = $undoafter->getText(); $cur_text = $currentRev->getText(); if ($cur_text == $undo_text) { # No use doing a merge if it's just a straight revert. return $undoafter_text; } $undone_text = ''; if (!wfMerge($undo_text, $undoafter_text, $cur_text, $undone_text)) { return false; } return $undone_text; }
/** * Lazy initialization of article text from DB */ protected function initText() { if (!isset($this->mText)) { if ($this->mRevision != null) { $this->mText = $this->mRevision->getText(); } else { // TODO: can we fetch raw wikitext for commons images? $this->mText = ''; } } }
/** * 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__); }
/** * Perform article updates on a special page creation. * * @param Revision $rev * * @fixme This is a shitty interface function. Kill it and replace the * other shitty functions like editUpdates and such so it's not needed * anymore. */ function createUpdates($rev) { $this->mGoodAdjustment = $this->isCountable($rev->getText()); $this->mTotalAdjustment = 1; $this->editUpdates($rev->getText(), $rev->getComment(), $rev->isMinor(), wfTimestamp(), $rev->getId(), true); }
public function testConstructWithContent() { $this->hideDeprecated("Revision::getText"); $title = Title::newFromText('RevisionTest_testConstructWithContent'); $rev = new Revision(array('content' => ContentHandler::makeContent('hello world.', $title, CONTENT_MODEL_JAVASCRIPT))); $this->assertNotNull($rev->getText(), 'no content text'); $this->assertNotNull($rev->getContent(), 'no content object available'); $this->assertEquals(CONTENT_MODEL_JAVASCRIPT, $rev->getContent()->getModel()); $this->assertEquals(CONTENT_MODEL_JAVASCRIPT, $rev->getContentModel()); }
public function fetchTranslatorsPortal( $natives ) { $titles = array(); foreach ( $natives as $code => $_ ) { $titles[] = Title::capitalize( $code, NS_PORTAL ) . '/translators'; } $dbr = wfGetDB( DB_SLAVE ); $tables = array( 'page', 'revision', 'text' ); $vars = array_merge( Revision::selectTextFields(), array( 'page_title', 'page_namespace' ), Revision::selectFields() ); $conds = array( 'page_latest = rev_id', 'rev_text_id = old_id', 'page_namespace' => NS_PORTAL, 'page_title' => $titles, ); $res = $dbr->select( $tables, $vars, $conds, __METHOD__ ); $users = array(); $lb = new LinkBatch; foreach ( $res as $row ) { $rev = new Revision( $row ); $text = $rev->getText(); $code = strtolower( preg_replace( '!/translators$!', '', $row->page_title ) ); preg_match_all( '!{{[Uu]ser\|([^}|]+)!', $text, $matches, PREG_SET_ORDER ); foreach ( $matches as $match ) { $user = Title::capitalize( $match[1], NS_USER ); $lb->add( NS_USER, $user ); $lb->add( NS_USER_TALK, $user ); if ( !isset( $users[$code] ) ) $users[$code] = array(); $users[$code][strtr( $user, '_', ' ' )] = -1; } } $lb->execute(); return $users; }
private function extractRowInfo($oRow, $deleted = 0) { wfProfileIn(__METHOD__); $vals = array(); if ($deleted == 0) { $oRevision = new Revision($oRow); if (isset($oRow->is_archive) && $oRow->is_archive == 1) { $this->mTitle = Title::makeTitle($oRow->page_namespace, $oRow->page_title); } else { $this->mTitle = $oRevision->getTitle(); } $this->mContent = $oRevision->getText(Revision::FOR_THIS_USER); # revision id $vals['revid'] = intval($oRevision->getId()); # username $vals['username'] = $oRevision->getUserText(); # user id $vals['userid'] = $oRevision->getUser(); # user ip $vals['user_ip'] = IP::isIPAddress($vals['username']) ? $vals['username'] : $this->_get_user_ip($vals['userid'], $oRow->page_title, $oRow->page_namespace); # user is bot $vals['userisbot'] = intval($this->_user_is_bot($vals['username'])); # is new $is_archive = isset($oRow->is_archive); $vals['isnew'] = $this->checkIsNew($is_archive); # timestamp $vals['timestamp'] = wfTimestamp(TS_DB, $oRevision->getTimestamp()); $vals['date'] = gmdate('Y-m-d', wfTimestamp(TS_UNIX, $oRevision->getTimestamp())); # size $vals['size'] = intval($oRevision->getSize()); #words $vals['words'] = str_word_count($this->mContent); # revision is redirect $vals['isredirect'] = intval($this->_revision_is_redirect()); # revision is content $vals['iscontent'] = intval($this->_revision_is_content()); # is deleted $vals['isdeleted'] = $deleted; # links $links = $this->_make_links(); $vals['imagelinks'] = $links['image']; $vals['video'] = $links['video']; } else { $this->mTitle = Title::makeTitle($oRow->page_namespace, $oRow->page_title); # revision id $vals['revid'] = intval($oRow->rev_id); # username $vals['username'] = $oRow->rev_user_text; # user id $vals['userid'] = intval($oRow->rev_user); # user ip $vals['user_ip'] = IP::isIPAddress($vals['username']) ? $vals['username'] : $this->_get_user_ip($vals['userid'], $oRow->page_title, $oRow->page_namespace); # user is bot $vals['userisbot'] = intval($this->_user_is_bot($vals['username'])); # is new $vals['isnew'] = 0; # timestamp $vals['timestamp'] = wfTimestamp(TS_DB, $oRow->rev_timestamp); # size $vals['size'] = intval($oRow->rev_len); # words $vals['words'] = 0; # revision is redirect $vals['isredirect'] = 0; # revision is content $vals['iscontent'] = intval($this->_revision_is_content()); # is deleted $vals['isdeleted'] = $deleted; # links $vals['imagelinks'] = 0; $vals['video'] = 0; } $vals['media_type'] = $this->getMediaType($oRow->page_namespace); $vals['page_latest'] = $this->mTitle->getLatestRevID(); wfProfileOut(__METHOD__); return $vals; }
function showDiff($revision) { global $wgOut; $dbr = wfGetDB(DB_SLAVE); $result = $this->getRevisions($dbr, array('hidden_rev_id' => $revision)); while ($row = $dbr->fetchObject($result)) { $info = $this->listRow($row); $list = $this->revisionInfo($row); $rev = new Revision($row); $rev->mTitle = Title::makeTitle($row->page_namespace, $row->page_title); $prevId = $rev->mTitle->getPreviousRevisionID($row->rev_id); if ($prevId) { $prev = Revision::newFromTitle($rev->mTitle, $prevId); $otext = strval($prev->getText()); } else { $wgOut->addHtml("<ul>" . $info . "</ul>\n" . $list); $wgOut->addWikiText(wfMsgNoTrans('oversight-nodiff')); return; } $ntext = strval($rev->getText()); $diffEngine = new DifferenceEngine(); $diffEngine->showDiffStyle(); $wgOut->addHtml("<ul>" . $info . "</ul>\n" . $list . "<p><strong>" . wfMsgHTML('oversight-difference') . "</strong>" . "</p>" . "<div>" . "<table border='0' width='98%' cellpadding='0' cellspacing='4' class='diff'>" . "<col class='diff-marker' />" . "<col class='diff-content' />" . "<col class='diff-marker' />" . "<col class='diff-content' />" . "<tr>" . "<td colspan='2' width='50%' align='center' class='diff-otitle'>" . wfMsgHTML('oversight-prev') . " (#{$prevId})" . "</td>" . "<td colspan='2' width='50%' align='center' class='diff-ntitle'>" . wfMsgHTML('oversight-hidden') . "</td>" . "</tr>" . $diffEngine->generateDiffBody($otext, $ntext) . "</table>" . "</div>\n"); } $dbr->freeResult($result); }
protected function parseRevisionText(\Revision $revision) { $options = $this->getParserOptions(); return $this->getParser()->parse($revision->getText(), $this->title, $options, true, true, $revision->getId()); }
function compareEventRecordWithRevision($dbname, $oRow, $debug) { global $wgTitle, $wgLanguageCode; $langcode = WikiFactory::getVarValueByName('wgLanguageCode', $oRow->wiki_id); $lang_id = WikiFactory::LangCodeToId($langcode); $cats = WikiFactory::getCategory($oRow->wiki_id); $cat_id = !empty($cats) ? $cats->cat_id : 0; $result = false; if (is_object($oRow) && !empty($oRow->page_id) && !empty($oRow->rev_id)) { $data = loadFromDB($dbname, $oRow->page_id, $oRow->rev_id); if (is_object($data)) { $oRevision = new Revision($data); if ($oRow->rev_id > 0) { $wgTitle = Title::makeTitle($data->page_namespace, $data->page_title); } else { $wgTitle = $oRevision->getTitle(); } $content = $oRevision->getText(Revision::FOR_THIS_USER); $is_bot = _user_is_bot($data->rev_user_text); $is_content = _revision_is_content(); $is_redirect = _revision_is_redirect($content); $size = intval($oRevision->getSize()); $words = str_word_count($content); $links = _make_links($oRow->page_id, $oRow->rev_id, $content); $timestamp = $data->ts; if ($data->rev_page == $oRow->page_id && $data->page_namespace == $oRow->page_ns && $data->rev_id == $oRow->rev_id && $timestamp == $oRow->rev_timestamp && $data->rev_user == $oRow->user_id && $is_bot == $oRow->user_is_bot && $is_content == $oRow->is_content && $is_redirect == $oRow->is_redirect && $size == $oRow->rev_size && $words == $oRow->total_words && $cat_id == $oRow->wiki_cat_id && $lang_id == $oRow->wiki_lang_id && $links['image'] == $oRow->image_links && $links['video'] == $oRow->video_links) { $result = true; } else { if ($debug) { echo <<<TEXT \tpage: {$data->rev_page} == {$oRow->page_id} \tnamespage: {$data->page_namespace}\t== {$oRow->page_ns} \trevision: {$data->rev_id}\t== {$oRow->rev_id} \ttimestamp: {$timestamp} == {$oRow->rev_timestamp} \tuser: {$data->rev_user} == {$oRow->user_id} \tis_bot: {$is_bot} == {$oRow->user_is_bot} \tis_content: {$is_content} == {$oRow->is_content} \tis_redirect: {$is_redirect} == {$oRow->is_redirect} \tsize: {$size} == {$oRow->rev_size} \twords: {$words} == {$oRow->total_words} \tcategory: {$cat_id} == {$oRow->wiki_cat_id} \tlanguage: {$lang_id} == {$oRow->wiki_lang_id} \timage links:{$links['image']} == {$oRow->image_links} \tvideo links: {$links['video']} == {$oRow->video_links} TEXT; } } } else { echo "Not local data found for: page: {$oRow->page_id} && revision: {$oRow->rev_id} \n"; } } else { echo "Not events data found for: page: {$oRow->page_id} && revision: {$oRow->rev_id} \n"; } return $result; }
/** * Automatically review an revision and add a log entry in the review log. * * This is called during edit operations after the new revision is added * and the page tables updated, but before LinksUpdate is called. * * $auto is here for revisions checked off to be reviewed. Auto-review * triggers on edit, but we don't want those to count as just automatic. * This also makes it so the user's name shows up in the page history. * * If $flags is given, then they will be the review tags. If not, the one * from the stable version will be used or minimal tags if that's not possible. * If no appropriate tags can be found, then the review will abort. */ public static function autoReviewEdit(Page $article, $user, Revision $rev, array $flags = null, $auto = true) { wfProfileIn(__METHOD__); $title = $article->getTitle(); // convenience # Get current stable version ID (for logging) $oldSv = FlaggedRevision::newFromStable($title, FR_MASTER); $oldSvId = $oldSv ? $oldSv->getRevId() : 0; # Set the auto-review tags from the prior stable version. # Normally, this should already be done and given here... if (!is_array($flags)) { if ($oldSv) { # Use the last stable version if $flags not given if ($user->isAllowed('bot')) { $flags = $oldSv->getTags(); // no change for bot edits } else { # Account for perms/tags... $flags = self::getAutoReviewTags($user, $oldSv->getTags()); } } else { // new page? $flags = self::quickTags(FR_CHECKED); // use minimal level } if (!is_array($flags)) { wfProfileOut(__METHOD__); return false; // can't auto-review this revision } } # Get review property flags $propFlags = $auto ? array('auto') : array(); # Rev ID is not put into parser on edit, so do the same here. # Also, a second parse would be triggered otherwise. $editInfo = $article->prepareTextForEdit($rev->getText()); $poutput = $editInfo->output; // revision HTML output # If this is an image page, store corresponding file info $fileData = array('name' => null, 'timestamp' => null, 'sha1' => null); if ($title->getNamespace() == NS_FILE) { # We must use WikiFilePage process cache on upload or get bitten by slave lag $file = $article instanceof WikiFilePage || $article instanceof ImagePage ? $article->getFile() : wfFindFile($title, array('bypassCache' => true)); // skip cache; bug 31056 if (is_object($file) && $file->exists()) { $fileData['name'] = $title->getDBkey(); $fileData['timestamp'] = $file->getTimestamp(); $fileData['sha1'] = $file->getSha1(); } } # Our review entry $flaggedRevision = new FlaggedRevision(array('rev' => $rev, 'user_id' => $user->getId(), 'timestamp' => $rev->getTimestamp(), 'quality' => FlaggedRevs::getQualityTier($flags, 0), 'tags' => FlaggedRevision::flattenRevisionTags($flags), 'img_name' => $fileData['name'], 'img_timestamp' => $fileData['timestamp'], 'img_sha1' => $fileData['sha1'], 'templateVersions' => $poutput->getTemplateIds(), 'fileVersions' => $poutput->getFileSearchOptions(), 'flags' => implode(',', $propFlags))); $flaggedRevision->insert(); # Update the article review log FlaggedRevsLog::updateReviewLog($title, $flags, array(), '', $rev->getId(), $oldSvId, true, $auto); # Update page and tracking tables and clear cache FlaggedRevs::stableVersionUpdates($title); wfProfileOut(__METHOD__); return true; }
/** * Propagate an article undelete * Sets xml property and calls the abstract function propagateUndeleteData($title) * @param Revision $revision being undeleted * @return bool true if propagate succeeded */ public function propagateUndelete($revision) { global $wgUser, $wrBotUserID; $result = true; // we do propagate bot edits // if ($wgUser->getID() != $wrBotUserID) { if (!$revision) { error_log("Undeleted revision not found: " . $revision->getTitle()->getPrefixedText() . "\n"); return true; } $text =& $revision->getText(); $this->xml = StructuredData::getXml($this->tagName, $text); $titleString = $revision->getTitle()->getText(); $ns = $revision->getTitle()->getNamespace(); $textChanged = false; // Sometimes the article being undeleted has been re-created, in which case nothing should be changed by undelete // (only previous revisions are restored), so make sure propagateUndeleteData is idempotent $result = $this->propagateMoveDeleteUndelete($titleString, $ns, $text, $textChanged); if ($result && $textChanged) { // clear the link cache so we get the right page id for the undeleted article //$linkCache =& LinkCache::singleton(); //$linkCache->clearBadLink($revision->getTitle()->getPrefixedDBkey()); $newTitle = Title::makeTitle($revision->getTitle()->getNamespace(), $revision->getTitle()->getDBkey()); $newTitle->resetArticleID(0); // clears link cache $newTitle->getArticleID(GAID_FOR_UPDATE); // make sure you read the master db for the pageid $article = new Article($newTitle, 0); PropagationManager::enablePropagation(false); $result = $article->doEdit($text, self::PROPAGATE_MESSAGE, PROPAGATE_EDIT_FLAGS); PropagationManager::enablePropagation(true); } // } return $result; }
$untilHappy = false; } else { $limit = 1000; $untilHappy = true; } $type = isset($options['type']) ? $options['type'] : 'ConcatenatedGzipHistoryBlob'; $dbr = wfGetDB(DB_SLAVE); $res = $dbr->select(array('page', 'revision', 'text'), '*', array('page_namespace' => $title->getNamespace(), 'page_title' => $title->getDBkey(), 'page_id=rev_page', 'rev_timestamp > ' . $dbr->addQuotes($dbr->timestamp($start)), 'rev_text_id=old_id'), __FILE__, array('LIMIT' => $limit)); $blob = new $type(); $hashes = array(); $keys = array(); $uncompressedSize = 0; $t = -microtime(true); foreach ($res as $row) { $revision = new Revision($row); $text = $revision->getText(); $uncompressedSize += strlen($text); $hashes[$row->rev_id] = md5($text); $keys[$row->rev_id] = $blob->addItem($text); if ($untilHappy && !$blob->isHappy()) { break; } } $serialized = serialize($blob); $t += microtime(true); # print_r( $blob->mDiffMap ); printf("%s\nCompression ratio for %d revisions: %5.2f, %s -> %d\n", $type, count($hashes), $uncompressedSize / strlen($serialized), $wgLang->formatSize($uncompressedSize), strlen($serialized)); printf("Compression time: %5.2f ms\n", $t * 1000); $t = -microtime(true); $blob = unserialize($serialized); foreach ($keys as $id => $key) {
/** * Get the text that needs to be saved in order to undo all revisions * between $undo and $undoafter. Revisions must belong to the same page, * must exist and must not be deleted * @param $undo Revision * @param $undoafter Revision Must be an earlier revision than $undo * @return mixed string on success, false on failure */ public function getUndoText(Revision $undo, Revision $undoafter = null) { $undo_text = $undo->getText(); $undoafter_text = $undoafter->getText(); $cur_text = $this->getContent(); if ($cur_text == $undo_text) { # No use doing a merge if it's just a straight revert. return $undoafter_text; } $undone_text = ''; if (!wfMerge($undo_text, $undoafter_text, $cur_text, $undone_text)) { return false; } return $undone_text; }
/** * Look up some text of a revision from its revision id * * Note that this is really *some* text, we do not make *any* guarantee * that this text will be even close to what the user actually sees, or * that the form is fit for any intended purpose. * * Note also that if the revision for any reason is not an Revision * the function returns with an empty string. * * @param Revision $revision a valid revision * @param $audience Integer: one of: * Revision::FOR_PUBLIC to be displayed to all users * Revision::FOR_THIS_USER to be displayed to the given user * Revision::RAW get the text regardless of permissions * @return string|null the content of the revision as some kind of string, * or an empty string if it can not be found */ static function revisionToString($revision, $audience = Revision::FOR_THIS_USER) { if (!$revision instanceof Revision) { return ''; } if (defined('MW_SUPPORTS_CONTENTHANDLER')) { $content = $revision->getContent($audience); if ($content === null) { return ''; } $result = self::contentToString($content); } else { // For MediaWiki without contenthandler support (< 1.21) $result = $revision->getText(); } return $result; }
/** * Update the page record to point to a newly saved revision. * * @param Database $dbw * @param Revision $revision For ID number, and text used to set length and redirect status fields * @param int $lastRevision If given, will not overwrite the page field * when different from the currently set value. * Giving 0 indicates the new page flag should * be set on. * @return bool true on success, false on failure * @private */ function updateRevisionOn(&$dbw, $revision, $lastRevision = null) { wfProfileIn(__METHOD__); $conditions = array('page_id' => $this->getId()); if (!is_null($lastRevision)) { # An extra check against threads stepping on each other $conditions['page_latest'] = $lastRevision; } $text = $revision->getText(); $dbw->update('page', array('page_latest' => $revision->getId(), 'page_touched' => $dbw->timestamp(), 'page_is_new' => $lastRevision === 0 ? 1 : 0, 'page_is_redirect' => Article::isRedirect($text) ? 1 : 0, 'page_len' => strlen($text)), $conditions, __METHOD__); wfProfileOut(__METHOD__); return $dbw->affectedRows() != 0; }
/** * Check if a user reverted himself to the stable version */ protected static function isSelfRevertToStable(Revision $rev, $srev, $baseRevId, $user) { if (!$srev || $baseRevId != $srev->getRevId()) { return false; // user reports they are not the same } $dbw = wfGetDB(DB_MASTER); # Such a revert requires 1+ revs between it and the stable $revertedRevs = $dbw->selectField('revision', '1', array('rev_page' => $rev->getPage(), 'rev_id > ' . intval($baseRevId), 'rev_id < ' . intval($rev->getId()), 'rev_user_text' => $user->getName()), __METHOD__); if (!$revertedRevs) { return false; // can't be a revert } # Check that this user is ONLY reverting his/herself. $otherUsers = $dbw->selectField('revision', '1', array('rev_page' => $rev->getPage(), 'rev_id > ' . intval($baseRevId), 'rev_user_text != ' . $dbw->addQuotes($user->getName())), __METHOD__); if ($otherUsers) { return false; // only looking for self-reverts } # Confirm the text because we can't trust this user. return $rev->getText() == $srev->getRevText(); }
public function execute() { global $wgContLang; $dbw = TranslationMemoryUpdater::getDatabaseHandle(); if ( $dbw === null ) { $this->error( "Database file not configured" ); $this->exit(); } $dbw->setFlag( DBO_TRX ); // HUGE speed improvement $groups = MessageGroups::singleton()->getGroups(); // TODO: encapsulate list of valid language codes $languages = Language::getLanguageNames( false ); unset( $languages['en'] ); foreach ( $groups as $id => $group ) { if ( $group->isMeta() ) { continue; } $this->output( "Processing: {$group->getLabel()} ", $id ); $capitalized = MWNamespace::isCapitalized( $group->getNamespace() ); $ns_text = $wgContLang->getNsText( $group->getNamespace() ); $definitions = $group->load( $group->getSourceLanguage() ); foreach ( $definitions as $key => $definition ) { // TODO: would be nice to do key normalisation closer to the message groups, to avoid transforming back and forth. // But how to preserve the original keys... $key = strtr( $key, ' ', '_' ); $key = $capitalized ? $wgContLang->ucfirst( $key ) : $key; $dbr = wfGetDB( DB_SLAVE ); $tables = array( 'page', 'revision', 'text' ); // selectFields to stfu Revision class $vars = array_merge( Revision::selectTextFields(), array( 'page_title' ), Revision::selectFields() ); $conds = array( 'page_latest = rev_id', 'rev_text_id = old_id', 'page_namespace' => $group->getNamespace(), 'page_title ' . $dbr->buildLike( "$key/", $dbr->anyString() ) ); $res = $dbr->select( $tables, $vars, $conds, __METHOD__ ); // Assure that there is at least one translation if ( $res->numRows() < 1 ) { continue; } $insert = array( 'text' => $definition, 'context' => "$ns_text:$key", 'length' => strlen( $definition ), 'lang' => $group->getSourceLanguage(), ); $source_id = $dbw->selectField( '`sources`', 'sid', $insert, __METHOD__ ); if ( $source_id === false ) { $dbw->insert( '`sources`', $insert, __METHOD__ ); $source_id = $dbw->insertId(); } $this->output( ' ', $id ); foreach ( $res as $row ) { list( , $code ) = TranslateUtils::figureMessage( $row->page_title ); $revision = new Revision( $row ); $insert = array( 'text' => $revision->getText(), 'lang' => $code, 'time' => wfTimestamp(), 'sid' => $source_id ); // We only do SQlite which doesn't need to know unique indexes $dbw->replace( '`targets`', null, $insert, __METHOD__ ); } $this->output( "{$res->numRows()}", $id ); } // each translation> $dbw->commit(); } // each group> }