/**
  * Populates the search index with content from all pages
  */
 protected function populateSearchIndex()
 {
     $res = $this->db->select('page', 'MAX(page_id) AS count');
     $s = $this->db->fetchObject($res);
     $count = $s->count;
     $this->output("Rebuilding index fields for {$count} pages...\n");
     $n = 0;
     $fields = array_merge(Revision::selectPageFields(), Revision::selectFields(), Revision::selectTextFields());
     while ($n < $count) {
         if ($n) {
             $this->output($n . "\n");
         }
         $end = $n + self::RTI_CHUNK_SIZE - 1;
         $res = $this->db->select(['page', 'revision', 'text'], $fields, ["page_id BETWEEN {$n} AND {$end}", 'page_latest = rev_id', 'rev_text_id = old_id'], __METHOD__);
         foreach ($res as $s) {
             try {
                 $title = Title::makeTitle($s->page_namespace, $s->page_title);
                 $rev = new Revision($s);
                 $content = $rev->getContent();
                 $u = new SearchUpdate($s->page_id, $title, $content);
                 $u->doUpdate();
             } catch (MWContentSerializationException $ex) {
                 $this->output("Failed to deserialize content of revision {$s->rev_id} of page " . "`" . $title->getPrefixedDBkey() . "`!\n");
             }
         }
         $n += self::RTI_CHUNK_SIZE;
     }
 }
 /**
  * @param IDatabase $db
  * @return mixed
  */
 public function doQuery($db)
 {
     $ids = array_map('intval', $this->ids);
     $queryInfo = ['tables' => ['revision', 'user'], 'fields' => array_merge(Revision::selectFields(), Revision::selectUserFields()), 'conds' => ['rev_page' => $this->title->getArticleID(), 'rev_id' => $ids], 'options' => ['ORDER BY' => 'rev_id DESC'], 'join_conds' => ['page' => Revision::pageJoinCond(), 'user' => Revision::userJoinCond()]];
     ChangeTags::modifyDisplayQuery($queryInfo['tables'], $queryInfo['fields'], $queryInfo['conds'], $queryInfo['join_conds'], $queryInfo['options'], '');
     $live = $db->select($queryInfo['tables'], $queryInfo['fields'], $queryInfo['conds'], __METHOD__, $queryInfo['options'], $queryInfo['join_conds']);
     if ($live->numRows() >= count($ids)) {
         // All requested revisions are live, keeps things simple!
         return $live;
     }
     $archiveQueryInfo = ['tables' => ['archive'], 'fields' => Revision::selectArchiveFields(), 'conds' => ['ar_rev_id' => $ids], 'options' => ['ORDER BY' => 'ar_rev_id DESC'], 'join_conds' => []];
     ChangeTags::modifyDisplayQuery($archiveQueryInfo['tables'], $archiveQueryInfo['fields'], $archiveQueryInfo['conds'], $archiveQueryInfo['join_conds'], $archiveQueryInfo['options'], '');
     // Check if any requested revisions are available fully deleted.
     $archived = $db->select($archiveQueryInfo['tables'], $archiveQueryInfo['fields'], $archiveQueryInfo['conds'], __METHOD__, $archiveQueryInfo['options'], $archiveQueryInfo['join_conds']);
     if ($archived->numRows() == 0) {
         return $live;
     } elseif ($live->numRows() == 0) {
         return $archived;
     } else {
         // Combine the two! Whee
         $rows = [];
         foreach ($live as $row) {
             $rows[$row->rev_id] = $row;
         }
         foreach ($archived as $row) {
             $rows[$row->ar_rev_id] = $row;
         }
         krsort($rows);
         return new FakeResultWrapper(array_values($rows));
     }
 }
 /**
  * @param $db DatabaseBase
  * @return mixed
  */
 public function doQuery($db)
 {
     $ids = array_map('intval', $this->ids);
     $live = $db->select(array('revision', 'page', 'user'), array_merge(Revision::selectFields(), Revision::selectUserFields()), array('rev_page' => $this->title->getArticleID(), 'rev_id' => $ids), __METHOD__, array('ORDER BY' => 'rev_id DESC'), array('page' => Revision::pageJoinCond(), 'user' => Revision::userJoinCond()));
     if ($live->numRows() >= count($ids)) {
         // All requested revisions are live, keeps things simple!
         return $live;
     }
     // Check if any requested revisions are available fully deleted.
     $archived = $db->select(array('archive'), '*', array('ar_rev_id' => $ids), __METHOD__, array('ORDER BY' => 'ar_rev_id DESC'));
     if ($archived->numRows() == 0) {
         return $live;
     } elseif ($live->numRows() == 0) {
         return $archived;
     } else {
         // Combine the two! Whee
         $rows = array();
         foreach ($live as $row) {
             $rows[$row->rev_id] = $row;
         }
         foreach ($archived as $row) {
             $rows[$row->ar_rev_id] = $row;
         }
         krsort($rows);
         return new FakeResultWrapper(array_values($rows));
     }
 }
 function getQueryInfo()
 {
     $conds = $this->mConds;
     $conds['rev_page'] = $this->articleID;
     $conds[] = "rev_timestamp < " . $this->mDb->addQuotes($this->maxTimestamp);
     return ['tables' => ['revision', 'page', 'user'], 'fields' => array_merge(Revision::selectFields(), Revision::selectUserFields()), 'conds' => $conds, 'join_conds' => ['page' => Revision::pageJoinCond(), 'user' => Revision::userJoinCond()]];
 }
 /**
  * @param IDatabase $db
  * @return mixed
  */
 public function doQuery($db)
 {
     $ids = array_map('intval', $this->ids);
     $queryInfo = array('tables' => array('revision', 'user'), 'fields' => array_merge(Revision::selectFields(), Revision::selectUserFields()), 'conds' => array('rev_page' => $this->title->getArticleID(), 'rev_id' => $ids), 'options' => array('ORDER BY' => 'rev_id DESC'), 'join_conds' => array('page' => Revision::pageJoinCond(), 'user' => Revision::userJoinCond()));
     ChangeTags::modifyDisplayQuery($queryInfo['tables'], $queryInfo['fields'], $queryInfo['conds'], $queryInfo['join_conds'], $queryInfo['options'], '');
     return $db->select($queryInfo['tables'], $queryInfo['fields'], $queryInfo['conds'], __METHOD__, $queryInfo['options'], $queryInfo['join_conds']);
 }
 /**
  * @covers Revision::selectFields
  */
 public function testSelectFields()
 {
     $fields = Revision::selectFields();
     $this->assertTrue(in_array('rev_id', $fields), 'missing rev_id in list of fields');
     $this->assertTrue(in_array('rev_page', $fields), 'missing rev_page in list of fields');
     $this->assertTrue(in_array('rev_timestamp', $fields), 'missing rev_timestamp in list of fields');
     $this->assertTrue(in_array('rev_user', $fields), 'missing rev_user in list of fields');
     $this->assertFalse(in_array('rev_content_model', $fields), 'missing rev_content_model in list of fields');
     $this->assertFalse(in_array('rev_content_format', $fields), 'missing rev_content_format in list of fields');
 }
 public function execute()
 {
     $db = wfGetDB(DB_MASTER);
     if (!$db->tableExists('revision')) {
         $this->error("revision table does not exist", true);
     }
     $this->output("Populating rev_len column\n");
     $start = $db->selectField('revision', 'MIN(rev_id)', false, __FUNCTION__);
     $end = $db->selectField('revision', 'MAX(rev_id)', false, __FUNCTION__);
     if (is_null($start) || is_null($end)) {
         $this->output("...revision table seems to be empty.\n");
         $db->insert('updatelog', array('ul_key' => 'populate rev_len'), __METHOD__, 'IGNORE');
         return;
     }
     # Do remaining chunks
     $blockStart = intval($start);
     $blockEnd = intval($start) + $this->mBatchSize - 1;
     $count = 0;
     $missing = 0;
     while ($blockStart <= $end) {
         $this->output("...doing rev_id from {$blockStart} to {$blockEnd}\n");
         $res = $db->select('revision', Revision::selectFields(), array("rev_id >= {$blockStart}", "rev_id <= {$blockEnd}", "rev_len IS NULL"), __METHOD__);
         # Go through and update rev_len from these rows.
         foreach ($res as $row) {
             $rev = new Revision($row);
             $text = $rev->getRawText();
             if (!is_string($text)) {
                 # This should not happen, but sometimes does (bug 20757)
                 $this->output("Text of revision {$row->rev_id} unavailable!\n");
                 $missing++;
             } else {
                 # Update the row...
                 $db->update('revision', array('rev_len' => strlen($text)), array('rev_id' => $row->rev_id), __METHOD__);
                 $count++;
             }
         }
         $blockStart += $this->mBatchSize;
         $blockEnd += $this->mBatchSize;
         wfWaitForSlaves(5);
     }
     $logged = $db->insert('updatelog', array('ul_key' => 'populate rev_len'), __METHOD__, 'IGNORE');
     if ($logged) {
         $this->output("rev_len population complete ... {$count} rows changed ({$missing} missing)\n");
         return true;
     } else {
         $this->output("Could not insert rev_len population row.\n");
         return false;
     }
 }
 public function doDBUpdates()
 {
     $db = $this->getDB(DB_MASTER);
     if (!$db->tableExists('revision')) {
         $this->error("revision table does not exist", true);
     } else {
         if (!$db->fieldExists('revision', 'rev_sha1', __METHOD__)) {
             $this->output("rev_sha1 column does not exist\n\n", true);
             return false;
         }
     }
     $this->output("Populating rev_len column\n");
     $start = $db->selectField('revision', 'MIN(rev_id)', false, __METHOD__);
     $end = $db->selectField('revision', 'MAX(rev_id)', false, __METHOD__);
     if (!$start || !$end) {
         $this->output("...revision table seems to be empty.\n");
         return true;
     }
     # Do remaining chunks
     $blockStart = intval($start);
     $blockEnd = intval($start) + $this->mBatchSize - 1;
     $count = 0;
     $missing = 0;
     $fields = Revision::selectFields();
     while ($blockStart <= $end) {
         $this->output("...doing rev_id from {$blockStart} to {$blockEnd}\n");
         $res = $db->select('revision', $fields, array("rev_id >= {$blockStart}", "rev_id <= {$blockEnd}", "rev_len IS NULL"), __METHOD__);
         # Go through and update rev_len from these rows.
         foreach ($res as $row) {
             $rev = new Revision($row);
             $content = $rev->getContent();
             if (!$content) {
                 # This should not happen, but sometimes does (bug 20757)
                 $this->output("Content of revision {$row->rev_id} unavailable!\n");
                 $missing++;
             } else {
                 # Update the row...
                 $db->update('revision', array('rev_len' => $content->getSize()), array('rev_id' => $row->rev_id), __METHOD__);
                 $count++;
             }
         }
         $blockStart += $this->mBatchSize;
         $blockEnd += $this->mBatchSize;
         wfWaitForSlaves();
     }
     $this->output("rev_len population complete ... {$count} rows changed ({$missing} missing)\n");
     return true;
 }
 public function execute()
 {
     global $wgUser;
     $dbr = wfGetDB(DB_SLAVE);
     $ret = $dbr->select(array('flaggedpages', 'revision', 'page'), array_merge(Revision::selectFields(), array($dbr->tableName('page') . '.*')), array('fp_pending_since IS NOT NULL', 'page_id = fp_page_id', 'rev_page = fp_page_id', 'rev_timestamp >= fp_pending_since'), __METHOD__, array('ORDER BY' => 'fp_pending_since DESC'));
     foreach ($ret as $row) {
         $title = Title::newFromRow($row);
         $article = new Article($title);
         $rev = new Revision($row);
         // Trigger cache regeneration
         $start = microtime(true);
         FRInclusionCache::getRevIncludes($article, $rev, $wgUser, 'regen');
         $elapsed = intval((microtime(true) - $start) * 1000);
         $this->cachePendingRevsLog($title->getPrefixedDBkey() . " rev:" . $rev->getId() . " {$elapsed}ms");
     }
 }
 public function run()
 {
     $page = WikiPage::newFromID($this->params['pageId'], WikiPage::READ_LATEST);
     if (!$page) {
         $this->setLastError("Could not find page #{$this->params['pageId']}");
         return false;
         // deleted?
     }
     $dbw = wfGetDB(DB_MASTER);
     // Use a named lock so that jobs for this page see each others' changes
     $lockKey = "CategoryMembershipUpdates:{$page->getId()}";
     $scopedLock = $dbw->getScopedLockAndFlush($lockKey, __METHOD__, 10);
     if (!$scopedLock) {
         $this->setLastError("Could not acquire lock '{$lockKey}'");
         return false;
     }
     $dbr = wfGetDB(DB_SLAVE, ['recentchanges']);
     // Wait till the slave is caught up so that jobs for this page see each others' changes
     if (!wfGetLB()->safeWaitForMasterPos($dbr)) {
         $this->setLastError("Timed out while waiting for slave to catch up");
         return false;
     }
     // Clear any stale REPEATABLE-READ snapshot
     $dbr->commit(__METHOD__, 'flush');
     $cutoffUnix = wfTimestamp(TS_UNIX, $this->params['revTimestamp']);
     // Using ENQUEUE_FUDGE_SEC handles jobs inserted out of revision order due to the delay
     // between COMMIT and actual enqueueing of the CategoryMembershipChangeJob job.
     $cutoffUnix -= self::ENQUEUE_FUDGE_SEC;
     // Get the newest revision that has a SRC_CATEGORIZE row...
     $row = $dbr->selectRow(['revision', 'recentchanges'], ['rev_timestamp', 'rev_id'], ['rev_page' => $page->getId(), 'rev_timestamp >= ' . $dbr->addQuotes($dbr->timestamp($cutoffUnix))], __METHOD__, ['ORDER BY' => 'rev_timestamp DESC, rev_id DESC'], ['recentchanges' => ['INNER JOIN', ['rc_this_oldid = rev_id', 'rc_source' => RecentChange::SRC_CATEGORIZE, 'rc_cur_id = rev_page', 'rc_timestamp >= rev_timestamp']]]);
     // Only consider revisions newer than any such revision
     if ($row) {
         $cutoffUnix = wfTimestamp(TS_UNIX, $row->rev_timestamp);
         $lastRevId = (int) $row->rev_id;
     } else {
         $lastRevId = 0;
     }
     // Find revisions to this page made around and after this revision which lack category
     // notifications in recent changes. This lets jobs pick up were the last one left off.
     $encCutoff = $dbr->addQuotes($dbr->timestamp($cutoffUnix));
     $res = $dbr->select('revision', Revision::selectFields(), ['rev_page' => $page->getId(), "rev_timestamp > {$encCutoff}" . " OR (rev_timestamp = {$encCutoff} AND rev_id > {$lastRevId})"], __METHOD__, ['ORDER BY' => 'rev_timestamp ASC, rev_id ASC']);
     // Apply all category updates in revision timestamp order
     foreach ($res as $row) {
         $this->notifyUpdatesForRevision($page, Revision::newFromRow($row));
     }
     return true;
 }
 public function run()
 {
     $page = WikiPage::newFromID($this->params['pageId'], WikiPage::READ_LATEST);
     if (!$page) {
         $this->setLastError("Could not find page #{$this->params['pageId']}");
         return false;
         // deleted?
     }
     $dbw = wfGetDB(DB_MASTER);
     // Use a named lock so that jobs for this page see each others' changes
     $fname = __METHOD__;
     $lockKey = "CategoryMembershipUpdates:{$page->getId()}";
     if (!$dbw->lock($lockKey, $fname, 10)) {
         $this->setLastError("Could not acquire lock '{$lockKey}'");
         return false;
     }
     $unlocker = new ScopedCallback(function () use($dbw, $lockKey, $fname) {
         $dbw->unlock($lockKey, $fname);
     });
     // Sanity: clear any DB transaction snapshot
     $dbw->commit(__METHOD__, 'flush');
     $cutoffUnix = wfTimestamp(TS_UNIX, $this->params['revTimestamp']);
     // Using ENQUEUE_FUDGE_SEC handles jobs inserted out of revision order due to the delay
     // between COMMIT and actual enqueueing of the CategoryMembershipChangeJob job.
     $cutoffUnix -= self::ENQUEUE_FUDGE_SEC;
     // Get the newest revision that has a SRC_CATEGORIZE row...
     $row = $dbw->selectRow(array('revision', 'recentchanges'), array('rev_timestamp', 'rev_id'), array('rev_page' => $page->getId(), 'rev_timestamp >= ' . $dbw->addQuotes($dbw->timestamp($cutoffUnix))), __METHOD__, array('ORDER BY' => 'rev_timestamp DESC, rev_id DESC'), array('recentchanges' => array('INNER JOIN', array('rc_this_oldid = rev_id', 'rc_source' => RecentChange::SRC_CATEGORIZE, 'rc_cur_id = rev_page', 'rc_timestamp >= rev_timestamp'))));
     // Only consider revisions newer than any such revision
     if ($row) {
         $cutoffUnix = wfTimestamp(TS_UNIX, $row->rev_timestamp);
         $lastRevId = (int) $row->rev_id;
     } else {
         $lastRevId = 0;
     }
     // Find revisions to this page made around and after this revision which lack category
     // notifications in recent changes. This lets jobs pick up were the last one left off.
     $encCutoff = $dbw->addQuotes($dbw->timestamp($cutoffUnix));
     $res = $dbw->select('revision', Revision::selectFields(), array('rev_page' => $page->getId(), "rev_timestamp > {$encCutoff}" . " OR (rev_timestamp = {$encCutoff} AND rev_id > {$lastRevId})"), __METHOD__, array('ORDER BY' => 'rev_timestamp ASC, rev_id ASC'));
     // Apply all category updates in revision timestamp order
     foreach ($res as $row) {
         $this->notifyUpdatesForRevision($page, Revision::newFromRow($row));
     }
     ScopedCallback::consume($unlocker);
     return true;
 }
 public function doDBUpdates()
 {
     $db = $this->getDB(DB_MASTER);
     if (!$db->tableExists('revision')) {
         $this->error("revision table does not exist", true);
     } elseif (!$db->tableExists('archive')) {
         $this->error("archive table does not exist", true);
     } elseif (!$db->fieldExists('revision', 'rev_len', __METHOD__)) {
         $this->output("rev_len column does not exist\n\n", true);
         return false;
     }
     $this->output("Populating rev_len column\n");
     $rev = $this->doLenUpdates('revision', 'rev_id', 'rev', Revision::selectFields());
     $this->output("Populating ar_len column\n");
     $ar = $this->doLenUpdates('archive', 'ar_id', 'ar', Revision::selectArchiveFields());
     $this->output("rev_len and ar_len population complete [{$rev} revision rows, {$ar} archive rows].\n");
     return true;
 }
function EditOwn($title, $user, $action, &$result)
{
    static $cache = array();
    global $wgEditOwnExcludedNamespaces, $wgEditOwnActions;
    if (!is_array($wgEditOwnExcludedNamespaces)) {
        // Prevent PHP from whining
        $wgEditOwnExcludedNamespaces = array();
    }
    if (!in_array($action, $wgEditOwnActions) || $user->isAllowed('editall') || in_array($title->getNamespace(), $wgEditOwnExcludedNamespaces)) {
        $result = null;
        return true;
    }
    if (isset($cache[$user->getName()][$title->getArticleId()])) {
        $result = $cache[$user->getName()][$title->getArticleId()];
        return is_null($result);
    }
    if (!$title->exists()) {
        // Creation is allowed
        $cache[$user->getName()][$title->getArticleId()] = null;
        $result = null;
        return true;
    }
    // Since there's no easy way to get the first revision,
    // we'll just do a DB query
    $dbr = wfGetDb(DB_SLAVE);
    $res = $dbr->select('revision', Revision::selectFields(), array('rev_page' => $title->getArticleId()), __METHOD__, array('ORDER BY' => 'rev_timestamp', 'LIMIT' => 1));
    $row = $dbr->fetchObject($res);
    if (!$row) {
        // Title with no revs, weird... allow creation
        $cache[$user->getName()][$title->getArticleId()] = null;
        $result = null;
        return true;
    }
    $rev = new Revision($row);
    if ($user->getName() == $rev->getRawUserText()) {
        $cache[$user->getName()][$title->getArticleId()] = null;
        $result = null;
        return true;
    }
    $cache[$user->getName()][$title->getArticleId()] = false;
    $result = false;
    return false;
}
Exemple #14
0
 /**
  * @param IDatabase $db
  * @return mixed
  */
 public function doQuery($db)
 {
     $conds = ['rev_page' => $this->title->getArticleID()];
     if ($this->ids !== null) {
         $conds['rev_id'] = array_map('intval', $this->ids);
     }
     return $db->select(['revision', 'page', 'user'], array_merge(Revision::selectFields(), Revision::selectUserFields()), $conds, __METHOD__, ['ORDER BY' => 'rev_id DESC'], ['page' => Revision::pageJoinCond(), 'user' => Revision::userJoinCond()]);
 }
Exemple #15
0
 /**
  * Back-end article deletion
  * Deletes the article with database consistency, writes logs, purges caches
  *
  * @since 1.19
  *
  * @param string $reason Delete reason for deletion log
  * @param bool $suppress Suppress all revisions and log the deletion in
  *   the suppression log instead of the deletion log
  * @param int $u1 Unused
  * @param bool $u2 Unused
  * @param array|string &$error Array of errors to append to
  * @param User $user The deleting user
  * @param array $tags Tags to apply to the deletion action
  * @return Status Status object; if successful, $status->value is the log_id of the
  *   deletion log entry. If the page couldn't be deleted because it wasn't
  *   found, $status is a non-fatal 'cannotdelete' error
  */
 public function doDeleteArticleReal($reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null, $tags = [])
 {
     global $wgUser, $wgContentHandlerUseDB;
     wfDebug(__METHOD__ . "\n");
     $status = Status::newGood();
     if ($this->mTitle->getDBkey() === '') {
         $status->error('cannotdelete', wfEscapeWikiText($this->getTitle()->getPrefixedText()));
         return $status;
     }
     $user = is_null($user) ? $wgUser : $user;
     if (!Hooks::run('ArticleDelete', [&$this, &$user, &$reason, &$error, &$status, $suppress])) {
         if ($status->isOK()) {
             // Hook aborted but didn't set a fatal status
             $status->fatal('delete-hook-aborted');
         }
         return $status;
     }
     $dbw = wfGetDB(DB_MASTER);
     $dbw->startAtomic(__METHOD__);
     $this->loadPageData(self::READ_LATEST);
     $id = $this->getId();
     // T98706: lock the page from various other updates but avoid using
     // WikiPage::READ_LOCKING as that will carry over the FOR UPDATE to
     // the revisions queries (which also JOIN on user). Only lock the page
     // row and CAS check on page_latest to see if the trx snapshot matches.
     $lockedLatest = $this->lockAndGetLatest();
     if ($id == 0 || $this->getLatest() != $lockedLatest) {
         $dbw->endAtomic(__METHOD__);
         // Page not there or trx snapshot is stale
         $status->error('cannotdelete', wfEscapeWikiText($this->getTitle()->getPrefixedText()));
         return $status;
     }
     // Given the lock above, we can be confident in the title and page ID values
     $namespace = $this->getTitle()->getNamespace();
     $dbKey = $this->getTitle()->getDBkey();
     // At this point we are now comitted to returning an OK
     // status unless some DB query error or other exception comes up.
     // This way callers don't have to call rollback() if $status is bad
     // unless they actually try to catch exceptions (which is rare).
     // we need to remember the old content so we can use it to generate all deletion updates.
     $revision = $this->getRevision();
     try {
         $content = $this->getContent(Revision::RAW);
     } catch (Exception $ex) {
         wfLogWarning(__METHOD__ . ': failed to load content during deletion! ' . $ex->getMessage());
         $content = null;
     }
     $fields = Revision::selectFields();
     $bitfield = false;
     // Bitfields to further suppress the content
     if ($suppress) {
         $bitfield = Revision::SUPPRESSED_ALL;
         $fields = array_diff($fields, ['rev_deleted']);
     }
     // For now, shunt the revision data into the archive table.
     // Text is *not* removed from the text table; bulk storage
     // is left intact to avoid breaking block-compression or
     // immutable storage schemes.
     // In the future, we may keep revisions and mark them with
     // the rev_deleted field, which is reserved for this purpose.
     // Get all of the page revisions
     $res = $dbw->select('revision', $fields, ['rev_page' => $id], __METHOD__, 'FOR UPDATE');
     // Build their equivalent archive rows
     $rowsInsert = [];
     foreach ($res as $row) {
         $rowInsert = ['ar_namespace' => $namespace, 'ar_title' => $dbKey, 'ar_comment' => $row->rev_comment, 'ar_user' => $row->rev_user, 'ar_user_text' => $row->rev_user_text, 'ar_timestamp' => $row->rev_timestamp, 'ar_minor_edit' => $row->rev_minor_edit, 'ar_rev_id' => $row->rev_id, 'ar_parent_id' => $row->rev_parent_id, 'ar_text_id' => $row->rev_text_id, 'ar_text' => '', 'ar_flags' => '', 'ar_len' => $row->rev_len, 'ar_page_id' => $id, 'ar_deleted' => $suppress ? $bitfield : $row->rev_deleted, 'ar_sha1' => $row->rev_sha1];
         if ($wgContentHandlerUseDB) {
             $rowInsert['ar_content_model'] = $row->rev_content_model;
             $rowInsert['ar_content_format'] = $row->rev_content_format;
         }
         $rowsInsert[] = $rowInsert;
     }
     // Copy them into the archive table
     $dbw->insert('archive', $rowsInsert, __METHOD__);
     // Save this so we can pass it to the ArticleDeleteComplete hook.
     $archivedRevisionCount = $dbw->affectedRows();
     // Clone the title and wikiPage, so we have the information we need when
     // we log and run the ArticleDeleteComplete hook.
     $logTitle = clone $this->mTitle;
     $wikiPageBeforeDelete = clone $this;
     // Now that it's safely backed up, delete it
     $dbw->delete('page', ['page_id' => $id], __METHOD__);
     $dbw->delete('revision', ['rev_page' => $id], __METHOD__);
     // Log the deletion, if the page was suppressed, put it in the suppression log instead
     $logtype = $suppress ? 'suppress' : 'delete';
     $logEntry = new ManualLogEntry($logtype, 'delete');
     $logEntry->setPerformer($user);
     $logEntry->setTarget($logTitle);
     $logEntry->setComment($reason);
     $logEntry->setTags($tags);
     $logid = $logEntry->insert();
     $dbw->onTransactionPreCommitOrIdle(function () use($dbw, $logEntry, $logid) {
         // Bug 56776: avoid deadlocks (especially from FileDeleteForm)
         $logEntry->publish($logid);
     }, __METHOD__);
     $dbw->endAtomic(__METHOD__);
     $this->doDeleteUpdates($id, $content, $revision);
     Hooks::run('ArticleDeleteComplete', [&$wikiPageBeforeDelete, &$user, $reason, $id, $content, $logEntry, $archivedRevisionCount]);
     $status->value = $logid;
     // Show log excerpt on 404 pages rather than just a link
     $cache = ObjectCache::getMainStashInstance();
     $key = wfMemcKey('page-recent-delete', md5($logTitle->getPrefixedText()));
     $cache->set($key, 1, $cache::TTL_DAY);
     return $status;
 }
 function getQueryInfo()
 {
     $queryInfo = array('tables' => array('revision', 'user'), 'fields' => array_merge(Revision::selectFields(), Revision::selectUserFields()), 'conds' => array_merge(array('rev_page' => $this->getWikiPage()->getId()), $this->conds), 'options' => array('USE INDEX' => array('revision' => 'page_timestamp')), 'join_conds' => array('user' => Revision::userJoinCond()));
     ChangeTags::modifyDisplayQuery($queryInfo['tables'], $queryInfo['fields'], $queryInfo['conds'], $queryInfo['join_conds'], $queryInfo['options'], $this->tagFilter);
     wfRunHooks('PageHistoryPager::getQueryInfo', array(&$this, &$queryInfo));
     return $queryInfo;
 }
 function getQueryInfo()
 {
     list($tables, $index, $userCond, $join_cond) = $this->getUserCond();
     $user = $this->getUser();
     $conds = array_merge($userCond, $this->getNamespaceCond());
     // Paranoia: avoid brute force searches (bug 17342)
     if (!$user->isAllowed('deletedhistory')) {
         $conds[] = $this->mDb->bitAnd('rev_deleted', Revision::DELETED_USER) . ' = 0';
     } elseif (!$user->isAllowed('suppressrevision')) {
         $conds[] = $this->mDb->bitAnd('rev_deleted', Revision::SUPPRESSED_USER) . ' != ' . Revision::SUPPRESSED_USER;
     }
     # Don't include orphaned revisions
     $join_cond['page'] = Revision::pageJoinCond();
     # Get the current user name for accounts
     $join_cond['user'] = Revision::userJoinCond();
     $queryInfo = array('tables' => $tables, 'fields' => array_merge(Revision::selectFields(), Revision::selectUserFields(), array('page_namespace', 'page_title', 'page_is_new', 'page_latest', 'page_is_redirect', 'page_len')), 'conds' => $conds, 'options' => array('USE INDEX' => array('revision' => $index)), 'join_conds' => $join_cond);
     ChangeTags::modifyDisplayQuery($queryInfo['tables'], $queryInfo['fields'], $queryInfo['conds'], $queryInfo['join_conds'], $queryInfo['options'], $this->tagFilter);
     wfRunHooks('ContribsPager::getQueryInfo', array(&$this, &$queryInfo));
     return $queryInfo;
 }
Exemple #18
0
function loadFromDB($dbname, $page_id, $rev_id)
{
    $db = wfGetDB(DB_SLAVE, 'stats', $dbname);
    $fields = Revision::selectPageFields();
    $fields = array_merge($fields, Revision::selectFields());
    $fields[] = " date_format(rev_timestamp, '%Y-%m-%d %H:%i:%s') as ts ";
    $oRow = $db->selectRow(array('revision', 'page'), $fields, array("rev_page = page_id", 'page_id' => $page_id, 'rev_id' => $rev_id), __METHOD__);
    if (empty($oRow)) {
        $fields = array('ar_namespace as page_namespace', 'ar_title as page_title', 'ar_comment as rev_comment', 'ar_user as rev_user', 'ar_user_text as rev_user_text', 'ar_timestamp as rev_timestamp', 'ar_minor_edit as rev_minor_edit', 'ar_rev_id as rev_id', 'ar_text_id as rev_text_id', 'ar_len as rev_len', 'ar_page_id as page_id', 'ar_page_id as rev_page', 'ar_deleted as rev_deleted', '0 as rev_parent_id', 'date_format(ar_timestamp, \'%Y-%m-%d %H:%i:%s\') as ts');
        $conditions = array('ar_page_id' => $page_id, 'ar_rev_id' => $rev_id);
        $oRow = $db->selectRow('archive', $fields, $conditions, __METHOD__);
    }
    return $oRow;
}
 /**
  * @param ApiPageSet $resultPageSet
  * @return void
  */
 protected function run(ApiPageSet $resultPageSet = null)
 {
     $db = $this->getDB();
     $params = $this->extractRequestParams(false);
     $result = $this->getResult();
     $this->requireMaxOneParameter($params, 'user', 'excludeuser');
     // Namespace check is likely to be desired, but can't be done
     // efficiently in SQL.
     $miser_ns = null;
     $needPageTable = false;
     if ($params['namespace'] !== null) {
         $params['namespace'] = array_unique($params['namespace']);
         sort($params['namespace']);
         if ($params['namespace'] != MWNamespace::getValidNamespaces()) {
             $needPageTable = true;
             if ($this->getConfig()->get('MiserMode')) {
                 $miser_ns = $params['namespace'];
             } else {
                 $this->addWhere(array('page_namespace' => $params['namespace']));
             }
         }
     }
     $this->addTables('revision');
     if ($resultPageSet === null) {
         $this->parseParameters($params);
         $this->addTables('page');
         $this->addJoinConds(array('page' => array('INNER JOIN', array('rev_page = page_id'))));
         $this->addFields(Revision::selectFields());
         $this->addFields(Revision::selectPageFields());
         // Review this depeneding on the outcome of T113901
         $this->addOption('STRAIGHT_JOIN');
     } else {
         $this->limit = $this->getParameter('limit') ?: 10;
         $this->addFields(array('rev_timestamp', 'rev_id'));
         if ($params['generatetitles']) {
             $this->addFields(array('rev_page'));
         }
         if ($needPageTable) {
             $this->addTables('page');
             $this->addJoinConds(array('page' => array('INNER JOIN', array('rev_page = page_id'))));
             $this->addFieldsIf(array('page_namespace'), (bool) $miser_ns);
             // Review this depeneding on the outcome of T113901
             $this->addOption('STRAIGHT_JOIN');
         }
     }
     if ($this->fld_tags) {
         $this->addTables('tag_summary');
         $this->addJoinConds(array('tag_summary' => array('LEFT JOIN', array('rev_id=ts_rev_id'))));
         $this->addFields('ts_tags');
     }
     if ($this->fetchContent) {
         $this->addTables('text');
         $this->addJoinConds(array('text' => array('INNER JOIN', array('rev_text_id=old_id'))));
         $this->addFields('old_id');
         $this->addFields(Revision::selectTextFields());
     }
     if ($params['user'] !== null) {
         $id = User::idFromName($params['user']);
         if ($id) {
             $this->addWhereFld('rev_user', $id);
         } else {
             $this->addWhereFld('rev_user_text', $params['user']);
         }
     } elseif ($params['excludeuser'] !== null) {
         $id = User::idFromName($params['excludeuser']);
         if ($id) {
             $this->addWhere('rev_user != ' . $id);
         } else {
             $this->addWhere('rev_user_text != ' . $db->addQuotes($params['excludeuser']));
         }
     }
     if ($params['user'] !== null || $params['excludeuser'] !== null) {
         // Paranoia: avoid brute force searches (bug 17342)
         if (!$this->getUser()->isAllowed('deletedhistory')) {
             $bitmask = Revision::DELETED_USER;
         } elseif (!$this->getUser()->isAllowedAny('suppressrevision', 'viewsuppressed')) {
             $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
         } else {
             $bitmask = 0;
         }
         if ($bitmask) {
             $this->addWhere($db->bitAnd('rev_deleted', $bitmask) . " != {$bitmask}");
         }
     }
     $dir = $params['dir'];
     if ($params['continue'] !== null) {
         $op = $dir == 'newer' ? '>' : '<';
         $cont = explode('|', $params['continue']);
         $this->dieContinueUsageIf(count($cont) != 2);
         $ts = $db->addQuotes($db->timestamp($cont[0]));
         $rev_id = (int) $cont[1];
         $this->dieContinueUsageIf(strval($rev_id) !== $cont[1]);
         $this->addWhere("rev_timestamp {$op} {$ts} OR " . "(rev_timestamp = {$ts} AND " . "rev_id {$op}= {$rev_id})");
     }
     $this->addOption('LIMIT', $this->limit + 1);
     $sort = $dir == 'newer' ? '' : ' DESC';
     $orderby = array();
     // Targeting index rev_timestamp, user_timestamp, or usertext_timestamp
     // But 'user' is always constant for the latter two, so it doesn't matter here.
     $orderby[] = "rev_timestamp {$sort}";
     $orderby[] = "rev_id {$sort}";
     $this->addOption('ORDER BY', $orderby);
     $res = $this->select(__METHOD__);
     $pageMap = array();
     // Maps rev_page to array index
     $count = 0;
     $nextIndex = 0;
     $generated = array();
     foreach ($res as $row) {
         if (++$count > $this->limit) {
             // We've had enough
             $this->setContinueEnumParameter('continue', "{$row->rev_timestamp}|{$row->rev_id}");
             break;
         }
         // Miser mode namespace check
         if ($miser_ns !== null && !in_array($row->page_namespace, $miser_ns)) {
             continue;
         }
         if ($resultPageSet !== null) {
             if ($params['generatetitles']) {
                 $generated[$row->rev_page] = $row->rev_page;
             } else {
                 $generated[] = $row->rev_id;
             }
         } else {
             $revision = Revision::newFromRow($row);
             $rev = $this->extractRevisionInfo($revision, $row);
             if (!isset($pageMap[$row->rev_page])) {
                 $index = $nextIndex++;
                 $pageMap[$row->rev_page] = $index;
                 $title = $revision->getTitle();
                 $a = array('pageid' => $title->getArticleID(), 'revisions' => array($rev));
                 ApiResult::setIndexedTagName($a['revisions'], 'rev');
                 ApiQueryBase::addTitleInfo($a, $title);
                 $fit = $result->addValue(array('query', $this->getModuleName()), $index, $a);
             } else {
                 $index = $pageMap[$row->rev_page];
                 $fit = $result->addValue(array('query', $this->getModuleName(), $index, 'revisions'), null, $rev);
             }
             if (!$fit) {
                 $this->setContinueEnumParameter('continue', "{$row->rev_timestamp}|{$row->rev_id}");
                 break;
             }
         }
     }
     if ($resultPageSet !== null) {
         if ($params['generatetitles']) {
             $resultPageSet->populateFromPageIDs($generated);
         } else {
             $resultPageSet->populateFromRevisionIDs($generated);
         }
     } else {
         $result->addIndexedTagName(array('query', $this->getModuleName()), 'page');
     }
 }
Exemple #20
0
 function getQueryInfo()
 {
     $queryInfo = array('tables' => array('revision'), 'fields' => Revision::selectFields(), 'conds' => array_merge(array('rev_page' => $this->historyPage->getTitle()->getArticleID()), $this->conds), 'options' => array('USE INDEX' => array('revision' => 'page_timestamp')), 'join_conds' => array('tag_summary' => array('LEFT JOIN', 'ts_rev_id=rev_id')));
     ChangeTags::modifyDisplayQuery($queryInfo['tables'], $queryInfo['fields'], $queryInfo['conds'], $queryInfo['join_conds'], $queryInfo['options'], $this->tagFilter);
     wfRunHooks('PageHistoryPager::getQueryInfo', array(&$this, &$queryInfo));
     return $queryInfo;
 }
 /**
  * Get select fields for FlaggedRevision DB row (flaggedrevs/revision tables)
  * @return array
  */
 public static function selectFields()
 {
     return array_merge(Revision::selectFields(), array('fr_rev_id', 'fr_page_id', 'fr_rev_timestamp', 'fr_user', 'fr_timestamp', 'fr_quality', 'fr_tags', 'fr_flags', 'fr_img_name', 'fr_img_sha1', 'fr_img_timestamp'));
 }
	function getQueryInfo() {
		$conds = $this->mConds;
		$conds['rev_page'] = $this->articleID;
		$conds[] = "rev_timestamp < {$this->maxTimestamp}";

		return array(
			'tables' => array( 'revision', 'page', 'user' ),
			'fields' => array_merge( Revision::selectFields(), Revision::selectUserFields() ),
			'conds' => $conds,
			'join_conds' => array(
				'page' => Revision::pageJoinCond(),
				'user' => Revision::userJoinCond() )
		);
	}
 function getQueryInfo()
 {
     return array('tables' => 'revision', 'fields' => Revision::selectFields(), 'conds' => array('rev_page' => $this->mPageHistory->mTitle->getArticleID()), 'options' => array('USE INDEX' => 'page_timestamp'));
 }
 protected function run(ApiPageSet $resultPageSet = null)
 {
     $params = $this->extractRequestParams(false);
     // If any of those parameters are used, work in 'enumeration' mode.
     // Enum mode can only be used when exactly one page is provided.
     // Enumerating revisions on multiple pages make it extremely
     // difficult to manage continuations and require additional SQL indexes
     $enumRevMode = !is_null($params['user']) || !is_null($params['excludeuser']) || !is_null($params['limit']) || !is_null($params['startid']) || !is_null($params['endid']) || $params['dir'] === 'newer' || !is_null($params['start']) || !is_null($params['end']);
     $pageSet = $this->getPageSet();
     $pageCount = $pageSet->getGoodTitleCount();
     $revCount = $pageSet->getRevisionCount();
     // Optimization -- nothing to do
     if ($revCount === 0 && $pageCount === 0) {
         // Nothing to do
         return;
     }
     if ($revCount > 0 && count($pageSet->getLiveRevisionIDs()) === 0) {
         // We're in revisions mode but all given revisions are deleted
         return;
     }
     if ($revCount > 0 && $enumRevMode) {
         $this->dieUsage('The revids= parameter may not be used with the list options ' . '(limit, startid, endid, dirNewer, start, end).', 'revids');
     }
     if ($pageCount > 1 && $enumRevMode) {
         $this->dieUsage('titles, pageids or a generator was used to supply multiple pages, ' . 'but the limit, startid, endid, dirNewer, user, excludeuser, start ' . 'and end parameters may only be used on a single page.', 'multpages');
     }
     // In non-enum mode, rvlimit can't be directly used. Use the maximum
     // allowed value.
     if (!$enumRevMode) {
         $this->setParsedLimit = false;
         $params['limit'] = 'max';
     }
     $db = $this->getDB();
     $this->addTables(array('revision', 'page'));
     $this->addJoinConds(array('page' => array('INNER JOIN', array('page_id = rev_page'))));
     if ($resultPageSet === null) {
         $this->parseParameters($params);
         $this->token = $params['token'];
         $this->addFields(Revision::selectFields());
         if ($this->token !== null || $pageCount > 0) {
             $this->addFields(Revision::selectPageFields());
         }
     } else {
         $this->limit = $this->getParameter('limit') ?: 10;
         $this->addFields(array('rev_id', 'rev_page'));
     }
     if ($this->fld_tags) {
         $this->addTables('tag_summary');
         $this->addJoinConds(array('tag_summary' => array('LEFT JOIN', array('rev_id=ts_rev_id'))));
         $this->addFields('ts_tags');
     }
     if (!is_null($params['tag'])) {
         $this->addTables('change_tag');
         $this->addJoinConds(array('change_tag' => array('INNER JOIN', array('rev_id=ct_rev_id'))));
         $this->addWhereFld('ct_tag', $params['tag']);
     }
     if ($this->fetchContent) {
         // For each page we will request, the user must have read rights for that page
         $user = $this->getUser();
         /** @var $title Title */
         foreach ($pageSet->getGoodTitles() as $title) {
             if (!$title->userCan('read', $user)) {
                 $this->dieUsage('The current user is not allowed to read ' . $title->getPrefixedText(), 'accessdenied');
             }
         }
         $this->addTables('text');
         $this->addJoinConds(array('text' => array('INNER JOIN', array('rev_text_id=old_id'))));
         $this->addFields('old_id');
         $this->addFields(Revision::selectTextFields());
     }
     // add user name, if needed
     if ($this->fld_user) {
         $this->addTables('user');
         $this->addJoinConds(array('user' => Revision::userJoinCond()));
         $this->addFields(Revision::selectUserFields());
     }
     if ($enumRevMode) {
         // This is mostly to prevent parameter errors (and optimize SQL?)
         if (!is_null($params['startid']) && !is_null($params['start'])) {
             $this->dieUsage('start and startid cannot be used together', 'badparams');
         }
         if (!is_null($params['endid']) && !is_null($params['end'])) {
             $this->dieUsage('end and endid cannot be used together', 'badparams');
         }
         if (!is_null($params['user']) && !is_null($params['excludeuser'])) {
             $this->dieUsage('user and excludeuser cannot be used together', 'badparams');
         }
         // Continuing effectively uses startid. But we can't use rvstartid
         // directly, because there is no way to tell the client to ''not''
         // send rvstart if it sent it in the original query. So instead we
         // send the continuation startid as rvcontinue, and ignore both
         // rvstart and rvstartid when that is supplied.
         if (!is_null($params['continue'])) {
             $params['startid'] = $params['continue'];
             $params['start'] = null;
         }
         // This code makes an assumption that sorting by rev_id and rev_timestamp produces
         // the same result. This way users may request revisions starting at a given time,
         // but to page through results use the rev_id returned after each page.
         // Switching to rev_id removes the potential problem of having more than
         // one row with the same timestamp for the same page.
         // The order needs to be the same as start parameter to avoid SQL filesort.
         if (is_null($params['startid']) && is_null($params['endid'])) {
             $this->addTimestampWhereRange('rev_timestamp', $params['dir'], $params['start'], $params['end']);
         } else {
             $this->addWhereRange('rev_id', $params['dir'], $params['startid'], $params['endid']);
             // One of start and end can be set
             // If neither is set, this does nothing
             $this->addTimestampWhereRange('rev_timestamp', $params['dir'], $params['start'], $params['end'], false);
         }
         // There is only one ID, use it
         $ids = array_keys($pageSet->getGoodTitles());
         $this->addWhereFld('rev_page', reset($ids));
         if (!is_null($params['user'])) {
             $this->addWhereFld('rev_user_text', $params['user']);
         } elseif (!is_null($params['excludeuser'])) {
             $this->addWhere('rev_user_text != ' . $db->addQuotes($params['excludeuser']));
         }
         if (!is_null($params['user']) || !is_null($params['excludeuser'])) {
             // Paranoia: avoid brute force searches (bug 17342)
             if (!$this->getUser()->isAllowed('deletedhistory')) {
                 $bitmask = Revision::DELETED_USER;
             } elseif (!$this->getUser()->isAllowedAny('suppressrevision', 'viewsuppressed')) {
                 $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
             } else {
                 $bitmask = 0;
             }
             if ($bitmask) {
                 $this->addWhere($db->bitAnd('rev_deleted', $bitmask) . " != {$bitmask}");
             }
         }
     } elseif ($revCount > 0) {
         $revs = $pageSet->getLiveRevisionIDs();
         // Get all revision IDs
         $this->addWhereFld('rev_id', array_keys($revs));
         if (!is_null($params['continue'])) {
             $this->addWhere('rev_id >= ' . intval($params['continue']));
         }
         $this->addOption('ORDER BY', 'rev_id');
     } elseif ($pageCount > 0) {
         $titles = $pageSet->getGoodTitles();
         // When working in multi-page non-enumeration mode,
         // limit to the latest revision only
         $this->addWhere('page_latest=rev_id');
         // Get all page IDs
         $this->addWhereFld('page_id', array_keys($titles));
         // Every time someone relies on equality propagation, god kills a kitten :)
         $this->addWhereFld('rev_page', array_keys($titles));
         if (!is_null($params['continue'])) {
             $cont = explode('|', $params['continue']);
             $this->dieContinueUsageIf(count($cont) != 2);
             $pageid = intval($cont[0]);
             $revid = intval($cont[1]);
             $this->addWhere("rev_page > {$pageid} OR " . "(rev_page = {$pageid} AND " . "rev_id >= {$revid})");
         }
         $this->addOption('ORDER BY', array('rev_page', 'rev_id'));
     } else {
         ApiBase::dieDebug(__METHOD__, 'param validation?');
     }
     $this->addOption('LIMIT', $this->limit + 1);
     $count = 0;
     $generated = array();
     $res = $this->select(__METHOD__);
     foreach ($res as $row) {
         if (++$count > $this->limit) {
             // We've reached the one extra which shows that there are
             // additional pages to be had. Stop here...
             if ($enumRevMode) {
                 $this->setContinueEnumParameter('continue', intval($row->rev_id));
             } elseif ($revCount > 0) {
                 $this->setContinueEnumParameter('continue', intval($row->rev_id));
             } else {
                 $this->setContinueEnumParameter('continue', intval($row->rev_page) . '|' . intval($row->rev_id));
             }
             break;
         }
         if ($resultPageSet !== null) {
             $generated[] = $row->rev_id;
         } else {
             $revision = new Revision($row);
             $rev = $this->extractRevisionInfo($revision, $row);
             if ($this->token !== null) {
                 $title = $revision->getTitle();
                 $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 {
                         $rev[$t . 'token'] = $val;
                     }
                 }
             }
             $fit = $this->addPageSubItem($row->rev_page, $rev, 'rev');
             if (!$fit) {
                 if ($enumRevMode) {
                     $this->setContinueEnumParameter('continue', intval($row->rev_id));
                 } elseif ($revCount > 0) {
                     $this->setContinueEnumParameter('continue', intval($row->rev_id));
                 } else {
                     $this->setContinueEnumParameter('continue', intval($row->rev_page) . '|' . intval($row->rev_id));
                 }
                 break;
             }
         }
     }
     if ($resultPageSet !== null) {
         $resultPageSet->populateFromRevisionIDs($generated);
     }
 }
 private function buildRevisionList($title, $errMsg = '')
 {
     // Builds a form listing the last 50 revisions of $pagename
     // that allows changing authors
     // $pagename: Title object
     // $errMsg: Error message
     // Returns: HTML.
     global $wgScript;
     $dbr = wfGetDb(DB_SLAVE);
     $res = $dbr->select('revision', Revision::selectFields(), array('rev_page' => $title->getArticleId()), __METHOD__, array('ORDER BY' => 'rev_timestamp DESC', 'LIMIT' => 50));
     $revs = array();
     while ($r = $dbr->fetchObject($res)) {
         $revs[] = new Revision($r);
     }
     if (empty($revs)) {
         // That's *very* weird
         return wfMsg('changeauthor-weirderror');
     }
     $retval = Xml::openElement('form', array('method' => 'POST', 'action' => $wgScript));
     $retval .= Xml::hidden('title', $this->selfTitle->getPrefixedDbKey());
     $retval .= Xml::hidden('action', 'change');
     $retval .= Xml::hidden('targetpage', $title->getPrefixedDbKey());
     $retval .= Xml::openElement('fieldset');
     $retval .= Xml::element('p', array(), wfMsg('changeauthor-explanation-multi'));
     $retval .= Xml::inputLabel(wfMsg('changeauthor-comment'), 'comment', 'comment', 50);
     $retval .= Xml::submitButton(wfMsg('changeauthor-changeauthors-multi'));
     if ($errMsg != '') {
         $retval .= Xml::openElement('p') . Xml::openElement('b');
         $retval .= Xml::element('font', array('color' => 'red'), $errMsg);
         $retval .= Xml::closeElement('b') . Xml::closeElement('p');
     }
     $retval .= Xml::element('h2', array(), $title->getPrefixedText());
     $retval .= Xml::openElement('ul');
     $count = count($revs);
     foreach ($revs as $i => $rev) {
         $retval .= $this->buildRevisionLine($rev, $title, $i == 0, $i == $count - 1);
     }
     $retval .= Xml::closeElement('ul');
     $retval .= Xml::closeElement('fieldset');
     $retval .= Xml::closeElement('form');
     return $retval;
 }
 /**
  * @covers Revision::selectFields
  */
 public function testSelectFields()
 {
     global $wgContentHandlerUseDB;
     $fields = Revision::selectFields();
     $this->assertTrue(in_array('rev_id', $fields), 'missing rev_id in list of fields');
     $this->assertTrue(in_array('rev_page', $fields), 'missing rev_page in list of fields');
     $this->assertTrue(in_array('rev_timestamp', $fields), 'missing rev_timestamp in list of fields');
     $this->assertTrue(in_array('rev_user', $fields), 'missing rev_user in list of fields');
     if ($wgContentHandlerUseDB) {
         $this->assertTrue(in_array('rev_content_model', $fields), 'missing rev_content_model in list of fields');
         $this->assertTrue(in_array('rev_content_format', $fields), 'missing rev_content_format in list of fields');
     }
 }
Exemple #27
0
 /**
  * Get the first revision of the page
  *
  * @param int $flags Title::GAID_FOR_UPDATE
  * @return Revision|null If page doesn't exist
  */
 public function getFirstRevision($flags = 0)
 {
     $pageId = $this->getArticleID($flags);
     if ($pageId) {
         $db = $flags & self::GAID_FOR_UPDATE ? wfGetDB(DB_MASTER) : wfGetDB(DB_SLAVE);
         $row = $db->selectRow('revision', Revision::selectFields(), array('rev_page' => $pageId), __METHOD__, array('ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 1));
         if ($row) {
             return new Revision($row);
         }
     }
     return null;
 }
 /**
  * Builds a form listing the last 50 revisions of $title that allows changing authors
  * @param $title Title object
  * @param $errMsg String: error message
  * @return HTML
  */
 private function buildRevisionList($title, $errMsg = '')
 {
     global $wgScript;
     $dbr = wfGetDB(DB_SLAVE);
     $res = $dbr->select('revision', Revision::selectFields(), array('rev_page' => $title->getArticleId()), __METHOD__, array('ORDER BY' => 'rev_timestamp DESC', 'LIMIT' => 50));
     $revs = array();
     while ($r = $dbr->fetchObject($res)) {
         $revs[] = new Revision($r);
     }
     if (empty($revs)) {
         // That's *very* weird
         return wfMsg('changeauthor-weirderror');
     }
     $retval = Xml::openElement('form', array('method' => 'post', 'action' => $wgScript));
     $retval .= Html::Hidden('title', $this->selfTitle->getPrefixedDBkey());
     $retval .= Html::Hidden('action', 'change');
     $retval .= Html::Hidden('targetpage', $title->getPrefixedDBkey());
     $retval .= Xml::openElement('fieldset');
     $retval .= Xml::element('p', array(), wfMsg('changeauthor-explanation-multi'));
     $retval .= Xml::inputLabel(wfMsg('changeauthor-comment'), 'comment', 'comment', 50);
     $retval .= Xml::submitButton(wfMsgExt('changeauthor-changeauthors-multi', array('parsemag', 'escape'), count($revs)));
     if ($errMsg != '') {
         $retval .= Xml::openElement('p') . Xml::openElement('b');
         $retval .= Xml::element('font', array('color' => 'red'), $errMsg);
         $retval .= Xml::closeElement('b') . Xml::closeElement('p');
     }
     $retval .= Xml::element('h2', array(), $title->getPrefixedText());
     $retval .= Xml::openElement('ul');
     $count = count($revs);
     foreach ($revs as $i => $rev) {
         $retval .= $this->buildRevisionLine($rev, $title, $i == 0, $i == $count - 1);
     }
     $retval .= Xml::closeElement('ul');
     $retval .= Xml::closeElement('fieldset');
     $retval .= Xml::closeElement('form');
     return $retval;
 }
Exemple #29
0
	/**
	 * Get the Revision object of the oldest revision
	 * @return Revision|null
	 */
	public function getOldestRevision() {
		wfProfileIn( __METHOD__ );

		// Try using the slave database first, then try the master
		$continue = 2;
		$db = wfGetDB( DB_SLAVE );
		$revSelectFields = Revision::selectFields();

		$row = null;
		while ( $continue ) {
			$row = $db->selectRow(
				array( 'page', 'revision' ),
				$revSelectFields,
				array(
					'page_namespace' => $this->mTitle->getNamespace(),
					'page_title' => $this->mTitle->getDBkey(),
					'rev_page = page_id'
				),
				__METHOD__,
				array(
					'ORDER BY' => 'rev_timestamp ASC'
				)
			);

			if ( $row ) {
				$continue = 0;
			} else {
				$db = wfGetDB( DB_MASTER );
				$continue--;
			}
		}

		wfProfileOut( __METHOD__ );
		return $row ? Revision::newFromRow( $row ) : null;
	}
 public function execute()
 {
     $params = $this->extractRequestParams(false);
     // If any of those parameters are used, work in 'enumeration' mode.
     // Enum mode can only be used when exactly one page is provided.
     // Enumerating revisions on multiple pages make it extremely
     // difficult to manage continuations and require additional SQL indexes
     $enumRevMode = !is_null($params['user']) || !is_null($params['excludeuser']) || !is_null($params['limit']) || !is_null($params['startid']) || !is_null($params['endid']) || $params['dir'] === 'newer' || !is_null($params['start']) || !is_null($params['end']);
     $pageSet = $this->getPageSet();
     $pageCount = $pageSet->getGoodTitleCount();
     $revCount = $pageSet->getRevisionCount();
     // Optimization -- nothing to do
     if ($revCount === 0 && $pageCount === 0) {
         return;
     }
     if ($revCount > 0 && $enumRevMode) {
         $this->dieUsage('The revids= parameter may not be used with the list options (limit, startid, endid, dirNewer, start, end).', 'revids');
     }
     if ($pageCount > 1 && $enumRevMode) {
         $this->dieUsage('titles, pageids or a generator was used to supply multiple pages, but the limit, startid, endid, dirNewer, user, excludeuser, start and end parameters may only be used on a single page.', 'multpages');
     }
     if (!is_null($params['difftotext'])) {
         $this->difftotext = $params['difftotext'];
     } elseif (!is_null($params['diffto'])) {
         if ($params['diffto'] == 'cur') {
             $params['diffto'] = 0;
         }
         if ((!ctype_digit($params['diffto']) || $params['diffto'] < 0) && $params['diffto'] != 'prev' && $params['diffto'] != 'next') {
             $this->dieUsage('rvdiffto must be set to a non-negative number, "prev", "next" or "cur"', 'diffto');
         }
         // Check whether the revision exists and is readable,
         // DifferenceEngine returns a rather ambiguous empty
         // string if that's not the case
         if ($params['diffto'] != 0) {
             $difftoRev = Revision::newFromID($params['diffto']);
             if (!$difftoRev) {
                 $this->dieUsageMsg(array('nosuchrevid', $params['diffto']));
             }
             if ($difftoRev->isDeleted(Revision::DELETED_TEXT)) {
                 $this->setWarning("Couldn't diff to r{$difftoRev->getID()}: content is hidden");
                 $params['diffto'] = null;
             }
         }
         $this->diffto = $params['diffto'];
     }
     $db = $this->getDB();
     $this->addTables('page');
     $this->addFields(Revision::selectFields());
     $this->addWhere('page_id = rev_page');
     $prop = array_flip($params['prop']);
     // Optional fields
     $this->fld_ids = isset($prop['ids']);
     // $this->addFieldsIf('rev_text_id', $this->fld_ids); // should this be exposed?
     $this->fld_flags = isset($prop['flags']);
     $this->fld_timestamp = isset($prop['timestamp']);
     $this->fld_comment = isset($prop['comment']);
     $this->fld_parsedcomment = isset($prop['parsedcomment']);
     $this->fld_size = isset($prop['size']);
     $this->fld_sha1 = isset($prop['sha1']);
     $this->fld_userid = isset($prop['userid']);
     $this->fld_user = isset($prop['user']);
     $this->token = $params['token'];
     // Possible indexes used
     $index = array();
     $userMax = $this->fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1;
     $botMax = $this->fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2;
     $limit = $params['limit'];
     if ($limit == 'max') {
         $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
         $this->getResult()->setParsedLimit($this->getModuleName(), $limit);
     }
     if (!is_null($this->token) || $pageCount > 0) {
         $this->addFields(Revision::selectPageFields());
     }
     if (isset($prop['tags'])) {
         $this->fld_tags = true;
         $this->addTables('tag_summary');
         $this->addJoinConds(array('tag_summary' => array('LEFT JOIN', array('rev_id=ts_rev_id'))));
         $this->addFields('ts_tags');
     }
     if (!is_null($params['tag'])) {
         $this->addTables('change_tag');
         $this->addJoinConds(array('change_tag' => array('INNER JOIN', array('rev_id=ct_rev_id'))));
         $this->addWhereFld('ct_tag', $params['tag']);
         global $wgOldChangeTagsIndex;
         $index['change_tag'] = $wgOldChangeTagsIndex ? 'ct_tag' : 'change_tag_tag_id';
     }
     if (isset($prop['content']) || !is_null($this->difftotext)) {
         // For each page we will request, the user must have read rights for that page
         foreach ($pageSet->getGoodTitles() as $title) {
             if (!$title->userCan('read')) {
                 $this->dieUsage('The current user is not allowed to read ' . $title->getPrefixedText(), 'accessdenied');
             }
         }
         $this->addTables('text');
         $this->addWhere('rev_text_id=old_id');
         $this->addFields('old_id');
         $this->addFields(Revision::selectTextFields());
         $this->fld_content = isset($prop['content']);
         $this->expandTemplates = $params['expandtemplates'];
         $this->generateXML = $params['generatexml'];
         $this->parseContent = $params['parse'];
         if ($this->parseContent) {
             // Must manually initialize unset limit
             if (is_null($limit)) {
                 $limit = 1;
             }
             // We are only going to parse 1 revision per request
             $this->validateLimit('limit', $limit, 1, 1, 1);
         }
         if (isset($params['section'])) {
             $this->section = $params['section'];
         } else {
             $this->section = false;
         }
     }
     // add user name, if needed
     if ($this->fld_user) {
         $this->addTables('user');
         $this->addJoinConds(array('user' => Revision::userJoinCond()));
         $this->addFields(Revision::selectUserFields());
     }
     // Bug 24166 - API error when using rvprop=tags
     $this->addTables('revision');
     if ($enumRevMode) {
         // This is mostly to prevent parameter errors (and optimize SQL?)
         if (!is_null($params['startid']) && !is_null($params['start'])) {
             $this->dieUsage('start and startid cannot be used together', 'badparams');
         }
         if (!is_null($params['endid']) && !is_null($params['end'])) {
             $this->dieUsage('end and endid cannot be used together', 'badparams');
         }
         if (!is_null($params['user']) && !is_null($params['excludeuser'])) {
             $this->dieUsage('user and excludeuser cannot be used together', 'badparams');
         }
         // Continuing effectively uses startid. But we can't use rvstartid
         // directly, because there is no way to tell the client to ''not''
         // send rvstart if it sent it in the original query. So instead we
         // send the continuation startid as rvcontinue, and ignore both
         // rvstart and rvstartid when that is supplied.
         if (!is_null($params['continue'])) {
             $params['startid'] = $params['continue'];
             unset($params['start']);
         }
         // This code makes an assumption that sorting by rev_id and rev_timestamp produces
         // the same result. This way users may request revisions starting at a given time,
         // but to page through results use the rev_id returned after each page.
         // Switching to rev_id removes the potential problem of having more than
         // one row with the same timestamp for the same page.
         // The order needs to be the same as start parameter to avoid SQL filesort.
         if (is_null($params['startid']) && is_null($params['endid'])) {
             $this->addTimestampWhereRange('rev_timestamp', $params['dir'], $params['start'], $params['end']);
         } else {
             $this->addWhereRange('rev_id', $params['dir'], $params['startid'], $params['endid']);
             // One of start and end can be set
             // If neither is set, this does nothing
             $this->addTimestampWhereRange('rev_timestamp', $params['dir'], $params['start'], $params['end'], false);
         }
         // must manually initialize unset limit
         if (is_null($limit)) {
             $limit = 10;
         }
         $this->validateLimit('limit', $limit, 1, $userMax, $botMax);
         // There is only one ID, use it
         $ids = array_keys($pageSet->getGoodTitles());
         $this->addWhereFld('rev_page', reset($ids));
         if (!is_null($params['user'])) {
             $this->addWhereFld('rev_user_text', $params['user']);
         } elseif (!is_null($params['excludeuser'])) {
             $this->addWhere('rev_user_text != ' . $db->addQuotes($params['excludeuser']));
         }
         if (!is_null($params['user']) || !is_null($params['excludeuser'])) {
             // Paranoia: avoid brute force searches (bug 17342)
             $this->addWhere($db->bitAnd('rev_deleted', Revision::DELETED_USER) . ' = 0');
         }
     } elseif ($revCount > 0) {
         $max = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
         $revs = $pageSet->getRevisionIDs();
         if (self::truncateArray($revs, $max)) {
             $this->setWarning("Too many values supplied for parameter 'revids': the limit is {$max}");
         }
         // Get all revision IDs
         $this->addWhereFld('rev_id', array_keys($revs));
         if (!is_null($params['continue'])) {
             $this->addWhere('rev_id >= ' . intval($params['continue']));
         }
         $this->addOption('ORDER BY', 'rev_id');
         // assumption testing -- we should never get more then $revCount rows.
         $limit = $revCount;
     } elseif ($pageCount > 0) {
         $max = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
         $titles = $pageSet->getGoodTitles();
         if (self::truncateArray($titles, $max)) {
             $this->setWarning("Too many values supplied for parameter 'titles': the limit is {$max}");
         }
         // When working in multi-page non-enumeration mode,
         // limit to the latest revision only
         $this->addWhere('page_id=rev_page');
         $this->addWhere('page_latest=rev_id');
         // Get all page IDs
         $this->addWhereFld('page_id', array_keys($titles));
         // Every time someone relies on equality propagation, god kills a kitten :)
         $this->addWhereFld('rev_page', array_keys($titles));
         if (!is_null($params['continue'])) {
             $cont = explode('|', $params['continue']);
             if (count($cont) != 2) {
                 $this->dieUsage('Invalid continue param. You should pass the original ' . 'value returned by the previous query', '_badcontinue');
             }
             $pageid = intval($cont[0]);
             $revid = intval($cont[1]);
             $this->addWhere("rev_page > {$pageid} OR " . "(rev_page = {$pageid} AND " . "rev_id >= {$revid})");
         }
         $this->addOption('ORDER BY', array('rev_page', 'rev_id'));
         // assumption testing -- we should never get more then $pageCount rows.
         $limit = $pageCount;
     } else {
         ApiBase::dieDebug(__METHOD__, 'param validation?');
     }
     $this->addOption('LIMIT', $limit + 1);
     $this->addOption('USE INDEX', $index);
     $count = 0;
     $res = $this->select(__METHOD__);
     foreach ($res as $row) {
         if (++$count > $limit) {
             // We've reached the one extra which shows that there are additional pages to be had. Stop here...
             if (!$enumRevMode) {
                 ApiBase::dieDebug(__METHOD__, 'Got more rows then expected');
                 // bug report
             }
             $this->setContinueEnumParameter('continue', intval($row->rev_id));
             break;
         }
         $fit = $this->addPageSubItem($row->rev_page, $this->extractRowInfo($row), 'rev');
         if (!$fit) {
             if ($enumRevMode) {
                 $this->setContinueEnumParameter('continue', intval($row->rev_id));
             } elseif ($revCount > 0) {
                 $this->setContinueEnumParameter('continue', intval($row->rev_id));
             } else {
                 $this->setContinueEnumParameter('continue', intval($row->rev_page) . '|' . intval($row->rev_id));
             }
             break;
         }
     }
 }