function execute()
 {
     global $wgOut, $wgLang;
     $stats = RepoStats::newFromRepo($this->mRepo);
     $repoName = $this->mRepo->getName();
     $wgOut->wrapWikiMsg('<h2 id="stats-main">$1</h2>', array('code-stats-header', $repoName));
     $wgOut->addWikiMsg('code-stats-main', $wgLang->timeanddate($stats->time, true), $wgLang->formatNum($stats->revisions), $repoName, $wgLang->formatNum($stats->authors), $wgLang->time($stats->time, true), $wgLang->date($stats->time, true));
     if (!empty($stats->states)) {
         $wgOut->wrapWikiMsg('<h3 id="stats-revisions">$1</h3>', 'code-stats-status-breakdown');
         $wgOut->addHTML('<table class="wikitable">' . '<tr><th>' . wfMsgHtml('code-field-status') . '</th><th>' . wfMsgHtml('code-stats-count') . '</th></tr>');
         foreach (CodeRevision::getPossibleStates() as $state) {
             $count = isset($stats->states[$state]) ? $stats->states[$state] : 0;
             $count = htmlspecialchars($wgLang->formatNum($count));
             $link = Linker::link(SpecialPage::getTitleFor('Code', $repoName . '/status/' . $state), htmlspecialchars($this->statusDesc($state)));
             $wgOut->addHTML("<tr><td>{$link}</td>" . "<td class=\"mw-codereview-status-{$state}\">{$count}</td></tr>");
         }
         $wgOut->addHTML('</table>');
     }
     if (!empty($stats->fixmes)) {
         $this->writeAuthorStatusTable('fixme', $stats->fixmes);
     }
     if (!empty($stats->new)) {
         $this->writeAuthorStatusTable('new', $stats->new);
     }
     if (!empty($stats->fixmesPerPath)) {
         $this->writeStatusPathTable('fixme', $stats->fixmesPerPath);
     }
     if (!empty($stats->newPerPath)) {
         $this->writeStatusPathTable('new', $stats->newPerPath);
     }
 }
 public function execute()
 {
     $repoName = $this->getArg(0);
     if ($repoName == "all") {
         $this->error("Cannot use the 'all' repo", true);
     }
     $repo = CodeRepository::newFromName($repoName);
     if (!$repo) {
         $this->error("Repo '{$repoName}' is not a valid Repository", true);
     }
     $revisions = $this->getArg(1);
     if (strpos($revisions, ':') !== false) {
         $revisionVals = explode(':', $revisions, 2);
     } else {
         $this->error("Invalid revision range", true);
     }
     $start = intval($revisionVals[0]);
     $end = intval($revisionVals[1]);
     $revisions = range($start, $end);
     $dbr = wfGetDB(DB_SLAVE);
     $res = $dbr->select('code_paths', '*', array('cp_rev_id' => $revisions, 'cp_repo_id' => $repo->getId()), __METHOD__);
     $dbw = wfGetDB(DB_MASTER);
     $dbw->begin();
     foreach ($res as $row) {
         $fragments = CodeRevision::getPathFragments(array(array('path' => $row->cp_path, 'action' => $row->cp_action)));
         CodeRevision::insertPaths($dbw, $fragments, $repo->getId(), $row->cp_rev_id);
         $this->output("r{$row->cp_rev_id}, path: " . $row->cp_path . " Fragments: " . count($fragments) . "\n");
     }
     $dbw->commit();
     $this->output("Done!\n");
 }
 function execute()
 {
     global $wgOut;
     $name = $this->mRepo->getName();
     $states = CodeRevision::getPossibleStates();
     $wgOut->wrapWikiMsg("== \$1 ==", 'code-field-status');
     $table_rows = '';
     foreach ($states as $state) {
         $link = $this->skin->link(SpecialPage::getTitleFor('Code', $name . "/status/{$state}"), wfMsgHtml("code-status-" . $state));
         $table_rows .= "<tr><td class=\"mw-codereview-status-{$state}\">{$link}</td>" . "<td>" . wfMsgHtml("code-status-desc-" . $state) . "</td></tr>\n";
     }
     $wgOut->addHTML('<table class="wikitable">' . '<tr><th>' . wfMsgHtml('code-field-status') . '</th>' . '<th>' . wfMsgHtml('code-field-status-description') . '</th></tr>' . $table_rows . '</table>');
 }
Beispiel #4
0
 public function testCommentCanonicalUrl()
 {
     # Fixture:
     $repo = $this->createRepo();
     $cr = CodeRevision::newFromSvn($repo, array('rev' => 305, 'author' => 'hashar', 'date' => '15 august 2011', 'msg' => 'dumb revision message', 'paths' => array('/dev/null')));
     # Find out our revision root URL
     $baseUrl = SpecialPage::getTitleFor('Code', $repo->getName() . '/305')->getCanonicalUrl();
     # Test revision URL with various comment id:
     $this->assertEquals($baseUrl, $cr->getCanonicalUrl(''));
     $this->assertEquals($baseUrl, $cr->getCanonicalUrl(0));
     $this->assertEquals($baseUrl, $cr->getCanonicalUrl(null));
     $this->assertEquals($baseUrl, $cr->getCanonicalUrl("0"));
     $this->assertEquals($baseUrl . '#c777', $cr->getCanonicalUrl(777));
     $this->assertEquals($baseUrl . '#c777', $cr->getCanonicalUrl("777"));
 }
Beispiel #5
0
 public function execute()
 {
     global $wgUser;
     // Before doing anything at all, let's check permissions
     if (!$wgUser->isAllowed('codereview-use')) {
         $this->dieUsage('You don\'t have permission to update code', 'permissiondenied');
     }
     $params = $this->extractRequestParams();
     $repo = CodeRepository::newFromName($params['repo']);
     if (!$repo) {
         $this->dieUsage("Invalid repo ``{$params['repo']}''", 'invalidrepo');
     }
     $svn = SubversionAdaptor::newFromRepo($repo->getPath());
     $lastStoredRev = $repo->getLastStoredRev();
     if ($lastStoredRev >= $params['rev']) {
         // Nothing to do, we're up to date.
         // Return an empty result
         $this->getResult()->addValue(null, $this->getModuleName(), array());
         return;
     }
     // FIXME: this could be a lot?
     $log = $svn->getLog('', $lastStoredRev + 1, $params['rev']);
     if (!$log) {
         // FIXME: When and how often does this happen?
         // Should we use dieUsage() here instead?
         ApiBase::dieDebug(__METHOD__, 'Something awry...');
     }
     $result = array();
     $revs = array();
     foreach ($log as $data) {
         $codeRev = CodeRevision::newFromSvn($repo, $data);
         $codeRev->save();
         $result[] = array('id' => $codeRev->getId(), 'author' => $codeRev->getAuthor(), 'timestamp' => wfTimestamp(TS_ISO_8601, $codeRev->getTimestamp()), 'message' => $codeRev->getMessage());
         $revs[] = $codeRev;
     }
     // Cache the diffs if there are a only a few.
     // Mainly for WMF post-commit ping hook...
     if (count($revs) <= 2) {
         foreach ($revs as $codeRev) {
             $repo->setDiffCache($codeRev);
             // trigger caching
         }
     }
     $this->getResult()->setIndexedTagName($result, 'rev');
     $this->getResult()->addValue(null, $this->getModuleName(), $result);
 }
 private function createRepo()
 {
     $dbw = wfGetDB(DB_MASTER);
     $dbw->insert('code_repo', array('repo_name' => 'Test', 'repo_path' => 'somewhere', 'repo_viewvc' => 'http://example.com/view/', 'repo_bugzilla' => 'http://www.example.com/$1'), __METHOD__);
     $id = $dbw->insertId();
     $this->repo = CodeRepository::newFromId($id);
     # Now insert a revision
     $row = new StdClass();
     $row->cr_repo_id = $this->repo->getId();
     $row->cr_id = 777;
     $row->cr_author = 'hashar';
     $row->cr_timestamp = '20110731063300';
     $row->cr_message = 'I am the very first revision of this life';
     $row->cr_status = '';
     $row->cr_path = '/trunk/';
     $rev = CodeRevision::newFromRow($this->repo, $row);
     $rev->save();
 }
Beispiel #7
0
 public function execute()
 {
     $repoName = $this->getArg(0);
     if ($repoName == "all") {
         $this->error("Cannot use the 'all' repo", true);
     }
     $repo = CodeRepository::newFromName($repoName);
     if (!$repo) {
         $this->error("Repo '{$repoName}' is not a valid Repository", true);
     }
     $revisions = $this->getArg(1);
     if (strpos($revisions, ':') !== false) {
         $revisionVals = explode(':', $revisions, 2);
     } else {
         $this->error("Invalid revision range", true);
     }
     $start = intval($revisionVals[0]);
     $end = intval($revisionVals[1]);
     $revisions = range($start, $end);
     $status = $this->getArg(2);
     if (!CodeRevision::isValidStatus($status)) {
         $this->error("'{$status}' is not a valid status", true);
     }
     $username = $this->getArg(3);
     $user = User::newFromName($username);
     if (!$user) {
         $this->error("'{$username}' is not a valid username ", true);
     }
     if (!$user->isAllowed('codereview-set-status')) {
         $this->error("'{$username}' does not have the 'codereview-set-status' right", true);
     }
     $dbr = wfGetDB(DB_SLAVE);
     $res = $dbr->select('code_rev', '*', array('cr_id' => $revisions, 'cr_repo_id' => $repo->getId()), __METHOD__);
     foreach ($res as $row) {
         $rev = CodeRevision::newFromRow($repo, $row);
         if ($rev && $rev->setStatus($status, $user)) {
             $this->output("r{$row->cr_id} updated\n");
         } else {
             $this->output("r{$row->cr_id} not updated\n");
         }
     }
     $this->output("Done!\n");
 }
 public function execute()
 {
     $repoName = $this->getArg(0);
     if ($repoName == "all") {
         $this->error("Cannot use the 'all' repo", true);
     }
     $repo = CodeRepository::newFromName($repoName);
     if (!$repo) {
         $this->error("Repo '{$repoName}' is not a valid Repository", true);
     }
     $revisions = $this->getArg(1);
     if (strpos($revisions, ':') !== false) {
         $revisionVals = explode(':', $revisions, 2);
     } else {
         $this->error("Invalid revision range", true);
     }
     $start = intval($revisionVals[0]);
     $end = intval($revisionVals[1]);
     $revisions = range($start, $end);
     $dryrun = $this->hasOption('dry-run');
     $dbr = wfGetDB(DB_SLAVE);
     $res = $dbr->select('code_rev', '*', array('cr_id' => $revisions, 'cr_repo_id' => $repo->getId()), __METHOD__);
     foreach ($res as $row) {
         $rev = CodeRevision::newFromRow($repo, $row);
         $affectedRevs = $rev->getUniqueAffectedRevs();
         $this->output("r{$row->cr_id}: ");
         if (count($affectedRevs)) {
             $this->output("associating revs " . implode(',', $affectedRevs) . "\n");
             if (!$dryrun) {
                 $rev->addReferencesTo($affectedRevs);
             }
         } else {
             $this->output("no revisions followed up\n");
         }
     }
     $this->output("Done!\n");
 }
Beispiel #9
0
 /**
  * Render the bottom row of the sign-offs table containing the buttons to
  * strike and submit sign-offs
  *
  * @param $signOffs array
  * @return string HTML
  */
 protected function signoffButtons($signOffs)
 {
     $userSignOffs = $this->getUserSignoffs($signOffs);
     $strikeButton = count($userSignOffs) ? Xml::submitButton(wfMsg('code-signoff-strike'), array('name' => 'wpStrikeSignoffs')) : '';
     $signoffText = wfMsgHtml('code-signoff-signoff');
     $signoffButton = Xml::submitButton(wfMsg('code-signoff-submit'), array('name' => 'wpSignoff'));
     $checks = '';
     foreach (CodeRevision::getPossibleFlags() as $flag) {
         $checks .= Html::input('wpSignoffFlags[]', $flag, 'checkbox', array('id' => "wpSignoffFlags-{$flag}", isset($userSignOffs[$flag]) ? 'disabled' : '' => '')) . ' ' . Xml::label(wfMsg("code-signoff-flag-{$flag}"), "wpSignoffFlags-{$flag}") . ' ';
     }
     return "<tr class='mw-codereview-signoffbuttons'><td colspan='4'>{$strikeButton} " . "<div class='mw-codereview-signoffchecks'>{$signoffText} {$checks} {$signoffButton}</div></td></tr>";
 }
Beispiel #10
0
 /**
  * Import a repository in the local database.
  * @param $repoName String Local name of repository
  * @param $start Int Revision to begin the import from (Default: null, means last stored revision);
  */
 private function importRepo($repoName, $start = null, $cacheSize = 0)
 {
     global $wgCodeReviewImportBatchSize;
     static $adaptorReported = false;
     $repo = CodeRepository::newFromName($repoName);
     if (!$repo) {
         $this->error("Invalid repo {$repoName}");
         return;
     }
     $svn = SubversionAdaptor::newFromRepo($repo->getPath());
     if (!$adaptorReported) {
         $this->output("Using " . get_class($svn) . " adaptor\n");
         $adaptorReported = true;
     }
     $this->output("IMPORT FROM REPO: {$repoName}\n");
     $lastStoredRev = $repo->getLastStoredRev();
     $this->output("Last stored revision: {$lastStoredRev}\n");
     $chunkSize = $wgCodeReviewImportBatchSize;
     $startTime = microtime(true);
     $revCount = 0;
     $start = $start !== null ? intval($start) : $lastStoredRev + 1;
     /*
      * FIXME: when importing only a part of a repository, the given path
      * might not have been created with revision 1. For example, the
      * mediawiki '/trunk/phase3' got created with r1284.
      */
     if ($start > $lastStoredRev + 1) {
         $this->error("Invalid starting point. r{$start} is beyond last stored revision: r" . ($lastStoredRev + 1));
         return;
     }
     $this->output("Syncing from r{$start} to HEAD...\n");
     if (!$svn->canConnect()) {
         $this->error("Unable to connect to repository.");
         return;
     }
     while (true) {
         $log = $svn->getLog('', $start, $start + $chunkSize - 1);
         if (empty($log)) {
             # Repo seems to give a blank when max rev is invalid, which
             # stops new revisions from being added. Try to avoid this
             # by trying less at a time from the last point.
             if ($chunkSize <= 1) {
                 break;
                 // done!
             }
             $chunkSize = max(1, floor($chunkSize / 4));
             continue;
         } else {
             $start += $chunkSize;
         }
         if (!is_array($log)) {
             var_dump($log);
             // @TODO: cleanup :)
             $this->error('Log entry is not an array! See content above.', true);
         }
         foreach ($log as $data) {
             $revCount++;
             $delta = microtime(true) - $startTime;
             $revSpeed = $revCount / $delta;
             $codeRev = CodeRevision::newFromSvn($repo, $data);
             $codeRev->save();
             $this->output(sprintf("%d %s %s (%0.1f revs/sec)\n", $codeRev->getId(), wfTimestamp(TS_DB, $codeRev->getTimestamp()), $codeRev->getAuthor(), $revSpeed));
         }
         wfWaitForSlaves();
     }
     if ($cacheSize !== 0) {
         $dbw = wfGetDB(DB_MASTER);
         $options = array('ORDER BY' => 'cr_id DESC');
         if ($cacheSize == "all") {
             $this->output("Pre-caching all uncached diffs...\n");
         } else {
             if ($cacheSize == 1) {
                 $this->output("Pre-caching the latest diff...\n");
             } else {
                 $this->output("Pre-caching the latest {$cacheSize} diffs...\n");
             }
             $options['LIMIT'] = $cacheSize;
         }
         // Get all rows for this repository that don't already have a diff filled in.
         // This is LIMITed according to the $cacheSize setting, above, so only the
         // rows that we plan to pre-cache are returned.
         // TODO: This was optimised in order to skip rows that already have a diff,
         //		 which is mostly what is required, but there may be situations where
         //		 you want to re-calculate diffs (e.g. if $wgCodeReviewMaxDiffPaths
         //		 changes).  If these situations arise we will either want to revert
         //		 this behaviour, or add a --force flag or something.
         $res = $dbw->select('code_rev', 'cr_id', array('cr_repo_id' => $repo->getId(), 'cr_diff IS NULL OR cr_diff = ""'), __METHOD__, $options);
         foreach ($res as $row) {
             $repo->getRevision($row->cr_id);
             $diff = $repo->getDiff($row->cr_id);
             // trigger caching
             $msg = "Diff r{$row->cr_id} ";
             if (is_integer($diff)) {
                 $msg .= "Skipped: " . CodeRepository::getDiffErrorMessage($diff);
             } else {
                 $msg .= "done";
             }
             $this->output($msg . "\n");
         }
     } else {
         $this->output("Pre-caching skipped.\n");
     }
     $this->output("Done!\n");
 }
 /**
  * @param $row
  * @param $repo CodeRepository
  * @param $result ApiResult
  * @return array
  */
 private function formatRow($row, $repo, $result)
 {
     $item = array();
     if (isset($this->props['revid'])) {
         $item['revid'] = intval($row->cr_id);
     }
     if (isset($this->props['status'])) {
         $item['status'] = $row->cr_status;
     }
     if (isset($this->props['commentcount'])) {
         $item['commentcount'] = $row->comments;
     }
     if (isset($this->props['path'])) {
         $item['path'] = $row->cr_path;
     }
     if (isset($this->props['message'])) {
         ApiResult::setContent($item, $row->cr_message);
     }
     if (isset($this->props['author'])) {
         $item['author'] = $row->cr_author;
     }
     if (isset($this->props['timestamp'])) {
         $item['timestamp'] = wfTimestamp(TS_ISO_8601, $row->cr_timestamp);
     }
     $rev = null;
     if (isset($this->props['tags'])) {
         $rev = CodeRevision::newFromRow($repo, $row);
         $item['tags'] = $rev->getTags();
         $result->setIndexedTagName($item['tags'], 'tags');
     }
     if (isset($this->props['followups'])) {
         if ($rev === null) {
             $rev = CodeRevision::newFromRow($repo, $row);
         }
         $item['followsup'] = $this->addReferenced($rev);
         $result->setIndexedTagName($item['followsup'], 'followsup');
     }
     if (isset($this->props['followedup'])) {
         if ($rev === null) {
             $rev = CodeRevision::newFromRow($repo, $row);
         }
         $item['followedup'] = $this->addReferenced($rev);
         $result->setIndexedTagName($item['followedup'], 'followedup');
     }
     return $item;
 }
Beispiel #12
0
 public function getAllowedParams()
 {
     $flags = CodeRevision::getPossibleFlags();
     return array('repo' => array(ApiBase::PARAM_TYPE => 'string', ApiBase::PARAM_REQUIRED => true), 'rev' => array(ApiBase::PARAM_TYPE => 'integer', ApiBase::PARAM_MIN => 1, ApiBase::PARAM_REQUIRED => true), 'comment' => null, 'status' => array(ApiBase::PARAM_TYPE => CodeRevision::getPossibleStates()), 'addtags' => array(ApiBase::PARAM_TYPE => 'string', ApiBase::PARAM_ISMULTI => true), 'removetags' => array(ApiBase::PARAM_TYPE => 'string', ApiBase::PARAM_ISMULTI => true), 'addflags' => array(ApiBase::PARAM_TYPE => 'string', ApiBase::PARAM_ISMULTI => true, ApiBase::PARAM_TYPE => $flags), 'removeflags' => array(ApiBase::PARAM_TYPE => 'string', ApiBase::PARAM_ISMULTI => true, ApiBase::PARAM_TYPE => $flags), 'addreferences' => array(ApiBase::PARAM_TYPE => 'integer', ApiBase::PARAM_ISMULTI => true), 'removereferences' => array(ApiBase::PARAM_TYPE => 'integer', ApiBase::PARAM_ISMULTI => true), 'addreferenced' => array(ApiBase::PARAM_TYPE => 'integer', ApiBase::PARAM_ISMULTI => true), 'removereferenced' => array(ApiBase::PARAM_TYPE => 'integer', ApiBase::PARAM_ISMULTI => true), 'token' => null);
 }
Beispiel #13
0
 /**
  * Set diff cache (for import operations)
  * @param $codeRev CodeRevision
  */
 public function setDiffCache(CodeRevision $codeRev)
 {
     global $wgMemc;
     wfProfileIn(__METHOD__);
     $rev1 = $codeRev->getId() - 1;
     $rev2 = $codeRev->getId();
     $svn = SubversionAdaptor::newFromRepo($this->path);
     $data = $svn->getDiff('', $rev1, $rev2);
     // Store to cache
     $key = wfMemcKey('svn', md5($this->path), 'diff', $rev1, $rev2);
     $wgMemc->set($key, $data, 3600 * 24 * 3);
     // Permanent DB storage
     $storedData = $data;
     $flags = Revision::compressRevisionText($storedData);
     $dbw = wfGetDB(DB_MASTER);
     $dbw->update('code_rev', array('cr_diff' => $storedData, 'cr_flags' => $flags), array('cr_repo_id' => $this->id, 'cr_id' => $codeRev->getId()), __METHOD__);
     wfProfileOut(__METHOD__);
 }
Beispiel #14
0
function efCodeReviewAddTooltipMessages()
{
    global $wgResourceModules;
    $wgResourceModules['ext.codereview.tooltips']['messages'] = array_merge(CodeRevision::getPossibleStateMessageKeys(), array('code-tooltip-withsummary', 'code-tooltip-withoutsummary'));
}
 /**
  * @param $pager SvnTablePager
  *
  * @return string
  */
 function showForm($pager)
 {
     global $wgScript, $wgRequest;
     $states = CodeRevision::getPossibleStates();
     $name = $this->mRepo->getName();
     $title = SpecialPage::getTitleFor('Code', $name);
     $options = array(Xml::option('', $title->getPrefixedText(), $this->mStatus == ''));
     foreach ($states as $key => $state) {
         $title = SpecialPage::getTitleFor('Code', $name . "/status/{$state}");
         $options[] = Xml::option(wfMsgHtml("code-status-{$state}"), $title->getPrefixedText(), $this->mStatus == $state);
     }
     $ret = "<fieldset><legend>" . wfMsgHtml('code-pathsearch-legend') . "</legend>" . '<table width="100%"><tr><td>' . Xml::openElement('form', array('action' => $wgScript, 'method' => 'get')) . Xml::inputlabel(wfMsg("code-pathsearch-path"), 'path', 'path', 55, $this->getPathsAsString(), array('dir' => 'ltr')) . '&#160;' . Xml::label(wfMsg('code-pathsearch-filter'), 'code-status-filter') . '&#160;' . Xml::openElement('select', array('id' => 'code-status-filter', 'name' => 'title')) . "\n" . implode("\n", $options) . "\n" . Xml::closeElement('select') . '&#160;' . Xml::submitButton(wfMsg('allpagessubmit')) . $pager->getHiddenFields(array('path', 'title')) . Xml::closeElement('form') . '</td></tr></table></fieldset>';
     return $ret;
 }
Beispiel #16
0
 /**
  * @return void
  */
 public function save()
 {
     $dbw = wfGetDB(DB_MASTER);
     $dbw->begin();
     $dbw->insert('code_rev', array('cr_repo_id' => $this->repoId, 'cr_id' => $this->id, 'cr_author' => $this->author, 'cr_timestamp' => $dbw->timestamp($this->timestamp), 'cr_message' => $this->message, 'cr_status' => $this->status, 'cr_path' => $this->commonPath, 'cr_flags' => ''), __METHOD__, array('IGNORE'));
     // Already exists? Update the row!
     $newRevision = $dbw->affectedRows() > 0;
     if (!$newRevision) {
         $dbw->update('code_rev', array('cr_author' => $this->author, 'cr_timestamp' => $dbw->timestamp($this->timestamp), 'cr_message' => $this->message, 'cr_path' => $this->commonPath), array('cr_repo_id' => $this->repoId, 'cr_id' => $this->id), __METHOD__);
     }
     // Update path tracking used for output and searching
     if ($this->paths) {
         CodeRevision::insertPaths($dbw, $this->paths, $this->repoId, $this->id);
     }
     $affectedRevs = $this->getUniqueAffectedRevs();
     if (count($affectedRevs)) {
         $this->addReferencesTo($affectedRevs);
     }
     global $wgEnableEmail;
     // Email the authors of revisions that this follows up on
     if ($wgEnableEmail && $newRevision && count($affectedRevs) > 0) {
         // Get committer wiki user name, or repo name at least
         $commitAuthor = $this->getWikiUser();
         if ($commitAuthor) {
             $committer = $commitAuthor->getName();
             $commitAuthorId = $commitAuthor->getId();
         } else {
             $committer = htmlspecialchars($this->author);
             $commitAuthorId = 0;
         }
         // Get the authors of these revisions
         $res = $dbw->select('code_rev', array('cr_repo_id', 'cr_id', 'cr_author', 'cr_timestamp', 'cr_message', 'cr_status', 'cr_path'), array('cr_repo_id' => $this->repoId, 'cr_id' => $affectedRevs, 'cr_id < ' . intval($this->id), 'cr_author != ' . $dbw->addQuotes($this->author)), __METHOD__, array('USE INDEX' => 'PRIMARY'));
         // Get repo and build comment title (for url)
         $url = $this->getCanonicalUrl();
         foreach ($res as $row) {
             $revision = CodeRevision::newFromRow($this->repo, $row);
             $users = $revision->getCommentingUsers();
             $rowUrl = $revision->getCanonicalUrl();
             $revisionAuthor = $revision->getWikiUser();
             $revisionCommitSummary = $revision->getMessage();
             //Add the followup revision author if they have not already been added as a commentor (they won't want dupe emails!)
             if ($revisionAuthor && !array_key_exists($revisionAuthor->getId(), $users)) {
                 $users[$revisionAuthor->getId()] = $revisionAuthor;
             }
             //Notify commenters and revision author of followup revision
             foreach ($users as $user) {
                 /**
                  * @var $user User
                  */
                 // No sense in notifying the author of this rev if they are a commenter/the author on the target rev
                 if ($commitAuthorId == $user->getId()) {
                     continue;
                 }
                 if ($user->canReceiveEmail()) {
                     // Send message in receiver's language
                     $lang = array('language' => $user->getGlobalPreference('language'));
                     $user->sendMail(wfMsgExt('codereview-email-subj2', $lang, $this->repo->getName(), $this->getIdString($row->cr_id)), wfMsgExt('codereview-email-body2', $lang, $committer, $this->getIdStringUnique($row->cr_id), $url, $this->message, $rowUrl, $revisionCommitSummary));
                 }
             }
         }
     }
     $dbw->commit();
 }