public function testChangeRemovedWithRev()
 {
     $revision = Revision::newFromId(Title::newFromText(self::$pageName)->getLatestRevID());
     $change = $this->newChange($revision);
     $change->triggerCategoryRemovedNotification(Title::newFromText('CategoryName', NS_CATEGORY));
     $this->assertEquals(1, self::$notifyCallCounter);
     $this->assertTrue(strlen(self::$lastNotifyArgs[0]) === 14);
     $this->assertEquals('Category:CategoryName', self::$lastNotifyArgs[1]->getPrefixedText());
     $this->assertEquals(self::$revUser->getName(), self::$lastNotifyArgs[2]->getName());
     $this->assertEquals('(recentchanges-page-removed-from-category: ' . self::$pageName . ')', self::$lastNotifyArgs[3]);
     $this->assertEquals(self::$pageName, self::$lastNotifyArgs[4]->getPrefixedText());
     $this->assertEquals(self::$pageRev->getParentId(), self::$lastNotifyArgs[5]);
     $this->assertEquals($revision->getId(), self::$lastNotifyArgs[6]);
     $this->assertEquals(null, self::$lastNotifyArgs[7]);
     $this->assertEquals(0, self::$lastNotifyArgs[8]);
     $this->assertEquals('127.0.0.1', self::$lastNotifyArgs[9]);
     $this->assertEquals(0, self::$lastNotifyArgs[10]);
 }
 /**
  * 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);
 }
 /**
  * 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;
 }
Esempio n. 4
0
 /**
  * Generates each row in the contributions list.
  *
  * Contributions which are marked "top" are currently on top of the history.
  * For these contributions, a [rollback] link is shown for users with roll-
  * back privileges. The rollback link restores the most recent version that
  * was not written by the target user.
  *
  * @todo This would probably look a lot nicer in a table.
  * @param $row
  * @return string
  */
 function formatRow($row)
 {
     wfProfileIn(__METHOD__);
     $ret = '';
     $classes = array();
     /*
      * There may be more than just revision rows. To make sure that we'll only be processing
      * revisions here, let's _try_ to build a revision out of our row (without displaying
      * notices though) and then trying to grab data from the built object. If we succeed,
      * we're definitely dealing with revision data and we may proceed, if not, we'll leave it
      * to extensions to subscribe to the hook to parse the row.
      */
     wfSuppressWarnings();
     $rev = new Revision($row);
     $validRevision = $rev->getParentId() !== null;
     wfRestoreWarnings();
     if ($validRevision) {
         $classes = array();
         $page = Title::newFromRow($row);
         $link = Linker::link($page, htmlspecialchars($page->getPrefixedText()), array('class' => 'mw-contributions-title'), $page->isRedirect() ? array('redirect' => 'no') : array());
         # Mark current revisions
         $topmarktext = '';
         $user = $this->getUser();
         if ($row->rev_id == $row->page_latest) {
             $topmarktext .= '<span class="mw-uctop">' . $this->messages['uctop'] . '</span>';
             # Add rollback link
             if (!$row->page_is_new && $page->quickUserCan('rollback', $user) && $page->quickUserCan('edit', $user)) {
                 $this->preventClickjacking();
                 $topmarktext .= ' ' . Linker::generateRollback($rev, $this->getContext());
             }
         }
         # Is there a visible previous revision?
         if ($rev->userCan(Revision::DELETED_TEXT, $user) && $rev->getParentId() !== 0) {
             $difftext = Linker::linkKnown($page, $this->messages['diff'], array(), array('diff' => 'prev', 'oldid' => $row->rev_id));
         } else {
             $difftext = $this->messages['diff'];
         }
         $histlink = Linker::linkKnown($page, $this->messages['hist'], array(), array('action' => 'history'));
         if ($row->rev_parent_id === null) {
             // For some reason rev_parent_id isn't populated for this row.
             // Its rumoured this is true on wikipedia for some revisions (bug 34922).
             // Next best thing is to have the total number of bytes.
             $chardiff = ' <span class="mw-changeslist-separator">. .</span> ' . Linker::formatRevisionSize($row->rev_len) . ' <span class="mw-changeslist-separator">. .</span> ';
         } else {
             $parentLen = isset($this->mParentLens[$row->rev_parent_id]) ? $this->mParentLens[$row->rev_parent_id] : 0;
             $chardiff = ' <span class="mw-changeslist-separator">. .</span> ' . ChangesList::showCharacterDifference($parentLen, $row->rev_len, $this->getContext()) . ' <span class="mw-changeslist-separator">. .</span> ';
         }
         $lang = $this->getLanguage();
         $comment = $lang->getDirMark() . Linker::revComment($rev, false, true);
         $date = $lang->userTimeAndDate($row->rev_timestamp, $user);
         if ($rev->userCan(Revision::DELETED_TEXT, $user)) {
             $d = Linker::linkKnown($page, htmlspecialchars($date), array('class' => 'mw-changeslist-date'), array('oldid' => intval($row->rev_id)));
         } else {
             $d = htmlspecialchars($date);
         }
         if ($rev->isDeleted(Revision::DELETED_TEXT)) {
             $d = '<span class="history-deleted">' . $d . '</span>';
         }
         # Show user names for /newbies as there may be different users.
         # Note that we already excluded rows with hidden user names.
         if ($this->contribs == 'newbie') {
             $userlink = ' . . ' . Linker::userLink($rev->getUser(), $rev->getUserText());
             $userlink .= ' ' . $this->msg('parentheses')->rawParams(Linker::userTalkLink($rev->getUser(), $rev->getUserText()))->escaped() . ' ';
         } else {
             $userlink = '';
         }
         if ($rev->getParentId() === 0) {
             $nflag = ChangesList::flag('newpage');
         } else {
             $nflag = '';
         }
         if ($rev->isMinor()) {
             $mflag = ChangesList::flag('minor');
         } else {
             $mflag = '';
         }
         $del = Linker::getRevDeleteLink($user, $rev, $page);
         if ($del !== '') {
             $del .= ' ';
         }
         $diffHistLinks = $this->msg('parentheses')->rawParams($difftext . $this->messages['pipe-separator'] . $histlink)->escaped();
         $ret = "{$del}{$d} {$diffHistLinks}{$chardiff}{$nflag}{$mflag} {$link}{$userlink} {$comment} {$topmarktext}";
         # Denote if username is redacted for this edit
         if ($rev->isDeleted(Revision::DELETED_USER)) {
             $ret .= " <strong>" . $this->msg('rev-deleted-user-contribs')->escaped() . "</strong>";
         }
         # Tags, if any.
         list($tagSummary, $newClasses) = ChangeTags::formatSummaryRow($row->ts_tags, 'contributions');
         $classes = array_merge($classes, $newClasses);
         $ret .= " {$tagSummary}";
     }
     // Let extensions add data
     wfRunHooks('ContributionsLineEnding', array($this, &$ret, $row, &$classes));
     $classes = implode(' ', $classes);
     $ret = "<li class=\"{$classes}\">{$ret}</li>\n";
     wfProfileOut(__METHOD__);
     return $ret;
 }
Esempio n. 5
0
 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;
 }
 /**
  * @param WikiPage $page
  * @param Revision $newRev
  * @throws MWException
  */
 protected function notifyUpdatesForRevision(WikiPage $page, Revision $newRev)
 {
     $config = RequestContext::getMain()->getConfig();
     $title = $page->getTitle();
     // Get the new revision
     if (!$newRev->getContent()) {
         return;
         // deleted?
     }
     // Get the prior revision (the same for null edits)
     if ($newRev->getParentId()) {
         $oldRev = Revision::newFromId($newRev->getParentId(), Revision::READ_LATEST);
         if (!$oldRev->getContent()) {
             return;
             // deleted?
         }
     } else {
         $oldRev = null;
     }
     // Parse the new revision and get the categories
     $categoryChanges = $this->getExplicitCategoriesChanges($title, $newRev, $oldRev);
     list($categoryInserts, $categoryDeletes) = $categoryChanges;
     if (!$categoryInserts && !$categoryDeletes) {
         return;
         // nothing to do
     }
     $dbw = wfGetDB(DB_MASTER);
     $catMembChange = new CategoryMembershipChange($title, $newRev);
     $catMembChange->checkTemplateLinks();
     $batchSize = $config->get('UpdateRowsPerQuery');
     $insertCount = 0;
     foreach ($categoryInserts as $categoryName) {
         $categoryTitle = Title::makeTitle(NS_CATEGORY, $categoryName);
         $catMembChange->triggerCategoryAddedNotification($categoryTitle);
         if ($insertCount++ && $insertCount % $batchSize == 0) {
             $dbw->commit(__METHOD__, 'flush');
             wfGetLBFactory()->waitForReplication();
         }
     }
     foreach ($categoryDeletes as $categoryName) {
         $categoryTitle = Title::makeTitle(NS_CATEGORY, $categoryName);
         $catMembChange->triggerCategoryRemovedNotification($categoryTitle);
         if ($insertCount++ && $insertCount++ % $batchSize == 0) {
             $dbw->commit(__METHOD__, 'flush');
             wfGetLBFactory()->waitForReplication();
         }
     }
 }
Esempio n. 7
0
 /**
  * Fetch additional information that is related to the saving that has just happened,
  * e.g. regarding the last edit date. In runs where this hook is not triggered, the
  * last DB entry (of MW) will be used to fill such properties.
  *
  * @note This method directly accesses a member of Article that is informally declared to
  * be private. However, there is no way to otherwise access an article's parseroutput for
  * the purpose of adding information there. If the private access ever becomes a problem,
  * a global/static variable appears to be the only way to get more article data to
  * LinksUpdate.
  *
  * @param WikiPage|Article $article WikiPage on 1.19 and later
  * @param Revision $rev
  * @param integer $baseID
  * @param User $user
  *
  * @return true
  */
 public static function onNewRevisionFromEditComplete($article, Revision $rev, $baseID, User $user)
 {
     global $smwgPageSpecialProperties;
     if ($article->mPreparedEdit && $article->mPreparedEdit->output instanceof ParserOutput) {
         $output = $article->mPreparedEdit->output;
         $title = $article->getTitle();
         if (!isset($title)) {
             return true;
             // nothing we can do
         }
         if (!isset($output->mSMWData)) {
             // no data container yet, make one
             $output->mSMWData = new SMWSemanticData(new SMWDIWikiPage($title->getDBkey(), $title->getNamespace(), $title->getInterwiki()));
         }
         $semdata = $output->mSMWData;
     } else {
         // give up, just keep the old data
         return true;
     }
     if (in_array('_MDAT', $smwgPageSpecialProperties)) {
         $timestamp = $article->getTimestamp();
         $di = self::getDataItemFromMWTimestamp($timestamp);
         if (!is_null($di)) {
             $semdata->addPropertyObjectValue(new SMWDIProperty('_MDAT'), $di);
         }
     }
     if (in_array('_LEDT', $smwgPageSpecialProperties)) {
         $di = SMWDIWikiPage::newFromTitle($user->getUserPage());
         if (!is_null($di)) {
             $semdata->addPropertyObjectValue(new SMWDIProperty('_LEDT'), $di);
         }
     }
     if (in_array('_NEWP', $smwgPageSpecialProperties)) {
         $semdata->addPropertyObjectValue(new SMWDIProperty('_NEWP'), new SMWDIBoolean(is_null($rev->getParentId())));
     }
     return true;
 }
 /**
  * Page moving and page protection (and possibly other things) creates null
  * revisions. These revisions re-use the previous text already stored in
  * the database. Those however do not trigger re-parsing of the page and
  * thus the ready tag is not updated. This watches for new revisions,
  * checks if they reuse existing text, checks whether the parent version
  * is the latest version and has a ready tag. If that is the case,
  * also adds a ready tag for the new revision (which is safe, because
  * the text hasn't changed). The interface will say that there has been
  * a change, but shows no change in the content. This lets the user to
  * update the translation pages in the case, the non-text changes affect
  * the rendering of translation pages. I'm not aware of any such cases
  * at the moment.
  * Hook: RevisionInsertComplete
  * @since 2012-05-08
  */
 public static function updateTranstagOnNullRevisions(Revision $rev, $text, $flags)
 {
     $title = $rev->getTitle();
     $newRevId = $rev->getId();
     $oldRevId = $rev->getParentId();
     $newTextId = $rev->getTextId();
     /* This hook doesn't provide any way to detech null revisions
      * without extra query */
     $dbw = wfGetDB(DB_MASTER);
     $table = 'revision';
     $field = 'rev_text_id';
     $conds = array('rev_page' => $rev->getPage(), 'rev_id' => $oldRevId);
     // FIXME: optimize away this query. Bug T38588.
     $oldTextId = $dbw->selectField($table, $field, $conds, __METHOD__);
     if (strval($newTextId) !== strval($oldTextId)) {
         // Not a null revision, bail out.
         return true;
     }
     $page = TranslatablePage::newFromTitle($title);
     if ($page->getReadyTag() === $oldRevId) {
         $page->addReadyTag($newRevId);
     }
     return true;
 }
Esempio n. 9
0
 /**
  * Fetch additional information that is related to the saving that has just happened,
  * e.g. regarding the last edit date. In runs where this hook is not triggered, the
  * last DB entry (of MW) will be used to fill such properties.
  *
  * @note This method directly accesses a member of Article that is informally declared to
  * be private. However, there is no way to otherwise access an article's parseroutput for
  * the purpose of adding information there. If the private access ever becomes a problem,
  * a global/static variable appears to be the only way to get more article data to
  * LinksUpdate.
  *
  * @param WikiPage|Article $article WikiPage on 1.19 and later
  * @param Revision $rev
  * @param integer $baseID
  * @param User $user
  *
  * @return true
  */
 public static function onNewRevisionFromEditComplete($article, Revision $rev, $baseID, User $user)
 {
     global $smwgPageSpecialProperties;
     if ($article->mPreparedEdit && $article->mPreparedEdit->output instanceof ParserOutput) {
         $semdata = self::getSMWDataFromParserOutput($article->mPreparedEdit->output, $article->getTitle());
     } else {
         // give up, just keep the old data
         return true;
     }
     if (in_array('_MDAT', $smwgPageSpecialProperties)) {
         $timestamp = $article->getTimestamp();
         $di = self::getDataItemFromMWTimestamp($timestamp);
         if (!is_null($di)) {
             $semdata->addPropertyObjectValue(new SMWDIProperty('_MDAT'), $di);
         }
     }
     if (in_array('_LEDT', $smwgPageSpecialProperties)) {
         $di = SMWDIWikiPage::newFromTitle($user->getUserPage());
         if (!is_null($di)) {
             $semdata->addPropertyObjectValue(new SMWDIProperty('_LEDT'), $di);
         }
     }
     if (in_array('_NEWP', $smwgPageSpecialProperties)) {
         $semdata->addPropertyObjectValue(new SMWDIProperty('_NEWP'), new SMWDIBoolean(is_null($rev->getParentId())));
     }
     return true;
 }
 private function extractRowInfo($row)
 {
     $revision = new Revision($row);
     $title = $revision->getTitle();
     $vals = array();
     if ($this->fld_ids) {
         $vals['revid'] = intval($revision->getId());
         // $vals['oldid'] = intval( $row->rev_text_id ); // todo: should this be exposed?
         if (!is_null($revision->getParentId())) {
             $vals['parentid'] = intval($revision->getParentId());
         }
     }
     if ($this->fld_flags && $revision->isMinor()) {
         $vals['minor'] = '';
     }
     if ($this->fld_user || $this->fld_userid) {
         if ($revision->isDeleted(Revision::DELETED_USER)) {
             $vals['userhidden'] = '';
         } else {
             if ($this->fld_user) {
                 $vals['user'] = $revision->getUserText();
             }
             $userid = $revision->getUser();
             if (!$userid) {
                 $vals['anon'] = '';
             }
             if ($this->fld_userid) {
                 $vals['userid'] = $userid;
             }
         }
     }
     if ($this->fld_timestamp) {
         $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $revision->getTimestamp());
     }
     if ($this->fld_size) {
         if (!is_null($revision->getSize())) {
             $vals['size'] = intval($revision->getSize());
         } else {
             $vals['size'] = 0;
         }
     }
     if ($this->fld_sha1) {
         if ($revision->getSha1() != '') {
             $vals['sha1'] = wfBaseConvert($revision->getSha1(), 36, 16, 40);
         } else {
             $vals['sha1'] = '';
         }
     }
     if ($this->fld_contentmodel) {
         $vals['contentmodel'] = $revision->getContentModel();
     }
     if ($this->fld_comment || $this->fld_parsedcomment) {
         if ($revision->isDeleted(Revision::DELETED_COMMENT)) {
             $vals['commenthidden'] = '';
         } else {
             $comment = $revision->getComment();
             if ($this->fld_comment) {
                 $vals['comment'] = $comment;
             }
             if ($this->fld_parsedcomment) {
                 $vals['parsedcomment'] = Linker::formatComment($comment, $title);
             }
         }
     }
     if ($this->fld_tags) {
         if ($row->ts_tags) {
             $tags = explode(',', $row->ts_tags);
             $this->getResult()->setIndexedTagName($tags, 'tag');
             $vals['tags'] = $tags;
         } else {
             $vals['tags'] = array();
         }
     }
     if (!is_null($this->token)) {
         $tokenFunctions = $this->getTokenFunctions();
         foreach ($this->token as $t) {
             $val = call_user_func($tokenFunctions[$t], $title->getArticleID(), $title, $revision);
             if ($val === false) {
                 $this->setWarning("Action '{$t}' is not allowed for the current user");
             } else {
                 $vals[$t . 'token'] = $val;
             }
         }
     }
     $content = null;
     global $wgParser;
     if ($this->fld_content || !is_null($this->difftotext)) {
         $content = $revision->getContent();
         // Expand templates after getting section content because
         // template-added sections don't count and Parser::preprocess()
         // will have less input
         if ($this->section !== false) {
             $content = $content->getSection($this->section, false);
             if (!$content) {
                 $this->dieUsage("There is no section {$this->section} in r" . $revision->getId(), 'nosuchsection');
             }
         }
     }
     if ($this->fld_content && !$revision->isDeleted(Revision::DELETED_TEXT)) {
         $text = null;
         if ($this->generateXML) {
             if ($content->getModel() === CONTENT_MODEL_WIKITEXT) {
                 $t = $content->getNativeData();
                 # note: don't set $text
                 $wgParser->startExternalParse($title, ParserOptions::newFromContext($this->getContext()), OT_PREPROCESS);
                 $dom = $wgParser->preprocessToDom($t);
                 if (is_callable(array($dom, 'saveXML'))) {
                     $xml = $dom->saveXML();
                 } else {
                     $xml = $dom->__toString();
                 }
                 $vals['parsetree'] = $xml;
             } else {
                 $this->setWarning("Conversion to XML is supported for wikitext only, " . $title->getPrefixedDBkey() . " uses content model " . $content->getModel() . ")");
             }
         }
         if ($this->expandTemplates && !$this->parseContent) {
             #XXX: implement template expansion for all content types in ContentHandler?
             if ($content->getModel() === CONTENT_MODEL_WIKITEXT) {
                 $text = $content->getNativeData();
                 $text = $wgParser->preprocess($text, $title, ParserOptions::newFromContext($this->getContext()));
             } else {
                 $this->setWarning("Template expansion is supported for wikitext only, " . $title->getPrefixedDBkey() . " uses content model " . $content->getModel() . ")");
                 $text = false;
             }
         }
         if ($this->parseContent) {
             $po = $content->getParserOutput($title, $revision->getId(), ParserOptions::newFromContext($this->getContext()));
             $text = $po->getText();
         }
         if ($text === null) {
             $format = $this->contentFormat ? $this->contentFormat : $content->getDefaultFormat();
             if (!$content->isSupportedFormat($format)) {
                 $model = $content->getModel();
                 $name = $title->getPrefixedDBkey();
                 $this->dieUsage("The requested format {$this->contentFormat} is not supported " . "for content model {$model} used by {$name}", 'badformat');
             }
             $text = $content->serialize($format);
             $vals['contentformat'] = $format;
         }
         if ($text !== false) {
             ApiResult::setContent($vals, $text);
         }
     } elseif ($this->fld_content) {
         $vals['texthidden'] = '';
     }
     if (!is_null($this->diffto) || !is_null($this->difftotext)) {
         global $wgAPIMaxUncachedDiffs;
         static $n = 0;
         // Number of uncached diffs we've had
         if ($n < $wgAPIMaxUncachedDiffs) {
             $vals['diff'] = array();
             $context = new DerivativeContext($this->getContext());
             $context->setTitle($title);
             $handler = $revision->getContentHandler();
             if (!is_null($this->difftotext)) {
                 $model = $title->getContentModel();
                 if ($this->contentFormat && !ContentHandler::getForModelID($model)->isSupportedFormat($this->contentFormat)) {
                     $name = $title->getPrefixedDBkey();
                     $this->dieUsage("The requested format {$this->contentFormat} is not supported for " . "content model {$model} used by {$name}", 'badformat');
                 }
                 $difftocontent = ContentHandler::makeContent($this->difftotext, $title, $model, $this->contentFormat);
                 $engine = $handler->createDifferenceEngine($context);
                 $engine->setContent($content, $difftocontent);
             } else {
                 $engine = $handler->createDifferenceEngine($context, $revision->getID(), $this->diffto);
                 $vals['diff']['from'] = $engine->getOldid();
                 $vals['diff']['to'] = $engine->getNewid();
             }
             $difftext = $engine->getDiffBody();
             ApiResult::setContent($vals['diff'], $difftext);
             if (!$engine->wasCacheHit()) {
                 $n++;
             }
         } else {
             $vals['diff']['notcached'] = '';
         }
     }
     return $vals;
 }