public function testUpdateUserParams() { $p = FRUserCounters::getUserParams(-1); # Assumes (main) IN content namespace $title = Title::makeTitleSafe(0, 'helloworld'); $article = new Article($title); $copyP = $p; FRUserCounters::updateUserParams($copyP, $article, "Manual edit comment"); $this->assertEquals($p['editComments'] + 1, $copyP['editComments'], "Manual summary"); $copyP = $p; FRUserCounters::updateUserParams($copyP, $article, "/* section */"); $this->assertEquals($p['editComments'], $copyP['editComments'], "Auto summary"); $copyP = $p; FRUserCounters::updateUserParams($copyP, $article, "edit summary"); $this->assertEquals($p['totalContentEdits'] + 1, $copyP['totalContentEdits'], "Content edit count on content edit"); $expected = $p['uniqueContentPages']; $expected[] = 0; $this->assertEquals($expected, $copyP['uniqueContentPages'], "Unique content pages on content edit"); # Assumes (user) NOT IN content namespace $title = Title::makeTitleSafe(NS_USER, 'helloworld'); $article = new Article($title); $copyP = $p; FRUserCounters::updateUserParams($copyP, $article, "Manual edit comment"); $this->assertEquals($p['editComments'] + 1, $copyP['editComments'], "Manual summary"); $copyP = $p; FRUserCounters::updateUserParams($copyP, $article, "/* section */"); $this->assertEquals($p['editComments'], $copyP['editComments'], "Auto summary"); $title = Title::makeTitleSafe(NS_USER, 'helloworld'); $article = new Article($title); $copyP = $p; FRUserCounters::updateUserParams($copyP, $article, "edit summary"); $this->assertEquals($p['totalContentEdits'], $copyP['totalContentEdits'], "Content edit count on non-content edit"); $this->assertEquals($p['uniqueContentPages'], $copyP['uniqueContentPages'], "Unique content pages on non-content edit"); }
public function execute() { global $wgContentNamespaces, $wgFlaggedRevsAutopromote; $this->output("Populating and updating flaggedrevs_promote table\n"); $dbr = wfGetDB(DB_SLAVE); $dbw = wfGetDB(DB_MASTER); $start = $dbr->selectField('user', 'MIN(user_id)', false, __METHOD__); $end = $dbr->selectField('user', 'MAX(user_id)', false, __METHOD__); if (is_null($start) || is_null($end)) { $this->output("...user table seems to be empty.\n"); return; } $count = 0; $changed = 0; for ($blockStart = $start; $blockStart <= $end; $blockStart += $this->mBatchSize) { $blockEnd = min($end, $blockStart + $this->mBatchSize - 1); $this->output("...doing user_id from {$blockStart} to {$blockEnd}\n"); $cond = "user_id BETWEEN {$blockStart} AND {$blockEnd}\n"; $res = $dbr->select('user', '*', $cond, __METHOD__); # Go through and clean up missing items, as well as correct fr_quality... foreach ($res as $row) { $dbw->begin(); $user = User::newFromRow($row); $p = FRUserCounters::getUserParams($user->getId(), FR_FOR_UPDATE); $oldp = $p; # Get edit comments used $sres = $dbr->select('revision', '1', array('rev_user' => $user->getID(), "rev_comment NOT LIKE '/*%*/'"), __METHOD__, array('LIMIT' => max($wgFlaggedRevsAutopromote['editComments'], 500))); $p['editComments'] = $dbr->numRows($sres); # Get content page edits $sres = $dbr->select(array('revision', 'page'), '1', array('rev_user' => $user->getID(), 'page_id = rev_page', 'page_namespace' => $wgContentNamespaces), __METHOD__, array('LIMIT' => max($wgFlaggedRevsAutopromote['totalContentEdits'], 500))); $p['totalContentEdits'] = $dbr->numRows($sres); # Get unique content pages edited $sres = $dbr->select(array('revision', 'page'), 'DISTINCT(rev_page)', array('rev_user' => $user->getID(), 'page_id = rev_page', 'page_namespace' => $wgContentNamespaces), __METHOD__, array('LIMIT' => max($wgFlaggedRevsAutopromote['uniqueContentPages'], 50))); $p['uniqueContentPages'] = array(); foreach ($sres as $innerRow) { $p['uniqueContentPages'][] = (int) $innerRow->rev_page; } # Save the new params... if ($oldp != $p) { FRUserCounters::saveUserParams($user->getId(), $p); $changed++; } $count++; $dbw->commit(); } wfWaitForSlaves(5); } $this->output("flaggedrevs_promote table update complete ..." . " {$count} rows [{$changed} changed or added]\n"); }
/** * Submit the form parameters for the page config to the DB. * * @return mixed (true on success, error string on failure) */ public function doSubmit() { # Double-check permissions if (!$this->isAllowed()) { return 'review_denied'; } # We can only approve actual revisions... if ($this->getAction() === 'approve') { $rev = Revision::newFromTitle($this->page, $this->oldid); # Check for archived/deleted revisions... if (!$rev || $rev->getVisibility()) { return 'review_bad_oldid'; } # Check for review conflicts... if ($this->lastChangeTime !== null) { // API uses null $lastChange = $this->oldFrev ? $this->oldFrev->getTimestamp() : ''; if ($lastChange !== $this->lastChangeTime) { return 'review_conflict_oldid'; } } $status = $this->approveRevision($rev, $this->oldFrev); # We can only unapprove approved revisions... } elseif ($this->getAction() === 'unapprove') { # Check for review conflicts... if ($this->lastChangeTime !== null) { // API uses null $lastChange = $this->oldFrev ? $this->oldFrev->getTimestamp() : ''; if ($lastChange !== $this->lastChangeTime) { return 'review_conflict_oldid'; } } # Check if we can find this flagged rev... if (!$this->oldFrev) { return 'review_not_flagged'; } $status = $this->unapproveRevision($this->oldFrev); } elseif ($this->getAction() === 'reject') { $newRev = Revision::newFromTitle($this->page, $this->oldid); $oldRev = Revision::newFromTitle($this->page, $this->refid); # Do not mess with archived/deleted revisions if (!$oldRev || $oldRev->isDeleted(Revision::DELETED_TEXT)) { return 'review_bad_oldid'; } elseif (!$newRev || $newRev->isDeleted(Revision::DELETED_TEXT)) { return 'review_bad_oldid'; } # Check that the revs are in order if ($oldRev->getTimestamp() > $newRev->getTimestamp()) { return 'review_cannot_undo'; } # Make sure we are only rejecting pending changes $srev = FlaggedRevision::newFromStable($this->page, FR_MASTER); if ($srev && $oldRev->getTimestamp() < $srev->getRevTimestamp()) { return 'review_cannot_reject'; // not really a use case } $article = new WikiPage($this->page); # Get text with changes after $oldRev up to and including $newRev removed $new_text = $article->getUndoText($newRev, $oldRev); if ($new_text === false) { return 'review_cannot_undo'; } $baseRevId = $newRev->isCurrent() ? $oldRev->getId() : 0; # Actually make the edit... $editStatus = $article->doEdit($new_text, $this->getComment(), 0, $baseRevId, $this->user); $status = $editStatus->isOK() ? true : 'review_cannot_undo'; if ($editStatus->isOK() && class_exists('EchoEvent') && $editStatus->value['revision']) { $affectedRevisions = array(); // revid -> userid $revisions = wfGetDB(DB_SLAVE)->select('revision', array('rev_id', 'rev_user'), array('rev_id <= ' . $newRev->getId(), 'rev_timestamp <= ' . $newRev->getTimestamp(), 'rev_id > ' . $oldRev->getId(), 'rev_timestamp > ' . $oldRev->getTimestamp(), 'rev_page' => $article->getId()), __METHOD__); foreach ($revisions as $row) { $affectedRevisions[$row->rev_id] = $row->rev_user; } EchoEvent::create(array('type' => 'reverted', 'title' => $this->page, 'extra' => array('revid' => $editStatus->value['revision']->getId(), 'reverted-users-ids' => array_values($affectedRevisions), 'reverted-revision-ids' => array_keys($affectedRevisions), 'method' => 'flaggedrevs-reject'), 'agent' => $this->user)); } # If this undid one edit by another logged-in user, update user tallies if ($status === true && $newRev->getParentId() == $oldRev->getId() && $newRev->getRawUser()) { if ($newRev->getRawUser() != $this->user->getId()) { // no self-reverts FRUserCounters::incCount($newRev->getRawUser(), 'revertedEdits'); } } } # Watch page if set to do so if ($status === true) { if ($this->user->getOption('flaggedrevswatch') && !$this->page->userIsWatching()) { $this->user->addWatch($this->page); } } return $status; }
/** * Increments a count for a user * @param int $uid User id * @param string $param Count name * @return string */ public static function incCount($uid, $param) { $p = self::getUserParams($uid, FR_FOR_UPDATE); if (!isset($p[$param])) { $p[$param] = 0; } $p[$param]++; FRUserCounters::saveUserParams($uid, $p); }
/** * Check an autopromote condition that is defined by FlaggedRevs * * Note: some unobtrusive caching is used to avoid DB hits. */ public static function checkAutoPromoteCond($cond, array $params, User $user, &$result) { global $wgMemc; switch ($cond) { case APCOND_FR_EDITSUMMARYCOUNT: $p = FRUserCounters::getParams($user); $result = $p && $p['editComments'] >= $params[0]; break; case APCOND_FR_NEVERBOCKED: if ($user->isBlocked()) { $result = false; // failed } else { $key = wfMemcKey('flaggedrevs', 'autopromote-notblocked', $user->getId()); $val = $wgMemc->get($key); if ($val === 'false') { $result = false; // failed } else { # Hit the DB if the result is not cached or if we need # to check if the user was blocked since the last check... $now_unix = time(); $last_checked = is_int($val) ? $val : 0; // TS_UNIX $result = !self::wasPreviouslyBlocked($user, $last_checked); $wgMemc->set($key, $result ? $now_unix : 'false', 7 * 86400); } } break; case APCOND_FR_UNIQUEPAGECOUNT: $p = FRUserCounters::getParams($user); $result = $p && $p['uniqueContentPages'] >= $params[0]; break; case APCOND_FR_EDITSPACING: $key = wfMemcKey('flaggedrevs', 'autopromote-editspacing', $user->getId(), $params[0], $params[1]); $val = $wgMemc->get($key); if ($val === 'true') { $result = true; // passed } elseif ($val === 'false') { $result = false; // failed } else { # Hit the DB only if the result is not cached... $pass = self::editSpacingCheck($user, $params[0], $params[1]); # Make a key to store the results if ($pass === true) { $wgMemc->set($key, 'true', 14 * 86400); } else { $wgMemc->set($key, 'false', $pass); } $result = $pass === true; } break; case APCOND_FR_EDITCOUNT: # $maxNew is the *most* edits that can be too recent $maxNew = $user->getEditCount() - $params[0]; if ($maxNew < 0) { $result = false; // doesn't meet count even *with* recent edits } elseif ($params[1] <= 0) { $result = true; // passed; we aren't excluding any recent edits } else { # Check all recent edits... $n = self::recentEditCount($user->getId(), $params[1], $maxNew); $result = $n <= $maxNew; } break; case APCOND_FR_CONTENTEDITCOUNT: $p = FRUserCounters::getParams($user); if (!$p) { $result = false; } else { # $maxNew is the *most* edits that can be too recent $maxNew = $p['totalContentEdits'] - $params[0]; if ($maxNew < 0) { $result = false; // doesn't meet count even *with* recent edits } elseif ($params[1] <= 0) { $result = true; // passed; we aren't excluding any recent edits } else { # Check all recent content edits... $n = self::recentContentEditCount($user->getId(), $params[1], $maxNew); $result = $n <= $maxNew; } } break; case APCOND_FR_CHECKEDEDITCOUNT: $key = wfMemcKey('flaggedrevs', 'autopromote-reviewededits', $user->getId(), $params[0], $params[1]); $val = $wgMemc->get($key); if ($val === 'true') { $result = true; // passed } elseif ($val === 'false') { $result = false; // failed } else { # Hit the DB only if the result is not cached... $result = self::reviewedEditsCheck($user, $params[0], $params[1]); if ($result) { $wgMemc->set($key, 'true', 7 * 86400); } else { $wgMemc->set($key, 'false', 3600); // briefly cache } } break; case APCOND_FR_USERPAGEBYTES: $result = !$params[0] || $user->getUserPage()->getLength() >= $params[0]; break; case APCOND_FR_MAXREVERTEDEDITRATIO: $p = FRUserCounters::getParams($user); $result = $p && $params[0] * $user->getEditCount() >= $p['revertedEdits']; break; case APCOND_FR_NEVERDEMOTED: // b/c $p = FRUserCounters::getParams($user); $result = $p && empty($p['demoted']); break; } return true; }
/** * Submit the form parameters for the page config to the DB. * * @return mixed (true on success, error string on failure) */ public function doSubmit() { # Double-check permissions if (!$this->isAllowed()) { return 'review_denied'; } # We can only approve actual revisions... if ($this->getAction() === 'approve') { $rev = Revision::newFromTitle($this->page, $this->oldid); # Check for archived/deleted revisions... if (!$rev || $rev->getVisibility()) { return 'review_bad_oldid'; } # Check for review conflicts... if ($this->lastChangeTime !== null) { // API uses null $lastChange = $this->oldFrev ? $this->oldFrev->getTimestamp() : ''; if ($lastChange !== $this->lastChangeTime) { return 'review_conflict_oldid'; } } $status = $this->approveRevision($rev, $this->oldFrev); # We can only unapprove approved revisions... } elseif ($this->getAction() === 'unapprove') { # Check for review conflicts... if ($this->lastChangeTime !== null) { // API uses null $lastChange = $this->oldFrev ? $this->oldFrev->getTimestamp() : ''; if ($lastChange !== $this->lastChangeTime) { return 'review_conflict_oldid'; } } # Check if we can find this flagged rev... if (!$this->oldFrev) { return 'review_not_flagged'; } $status = $this->unapproveRevision($this->oldFrev); } elseif ($this->getAction() === 'reject') { $newRev = Revision::newFromTitle($this->page, $this->oldid); $oldRev = Revision::newFromTitle($this->page, $this->refid); # Do not mess with archived/deleted revisions if (!$oldRev || $oldRev->isDeleted(Revision::DELETED_TEXT)) { return 'review_bad_oldid'; } elseif (!$newRev || $newRev->isDeleted(Revision::DELETED_TEXT)) { return 'review_bad_oldid'; } # Check that the revs are in order if ($oldRev->getTimestamp() > $newRev->getTimestamp()) { return 'review_cannot_undo'; } # Make sure we are only rejecting pending changes $srev = FlaggedRevision::newFromStable($this->page, FR_MASTER); if ($srev && $oldRev->getTimestamp() < $srev->getRevTimestamp()) { return 'review_cannot_reject'; // not really a use case } $article = new WikiPage($this->page); # Get text with changes after $oldRev up to and including $newRev removed $new_text = $article->getUndoText($newRev, $oldRev); if ($new_text === false) { return 'review_cannot_undo'; } $baseRevId = $newRev->isCurrent() ? $oldRev->getId() : 0; # Actually make the edit... $editStatus = $article->doEdit($new_text, $this->getComment(), 0, $baseRevId, $this->user); $status = $editStatus->isOK() ? true : 'review_cannot_undo'; # If this undid one edit by another logged-in user, update user tallies if ($status === true && $newRev->getParentId() == $oldRev->getId() && $newRev->getRawUser()) { if ($newRev->getRawUser() != $this->user->getId()) { // no self-reverts FRUserCounters::incCount($newRev->getRawUser(), 'revertedEdits'); } } } # Watch page if set to do so if ($status === true) { if ($this->user->getOption('flaggedrevswatch') && !$this->page->userIsWatching()) { $this->user->addWatch($this->page); } } return $status; }