/** * Render the contributions of user to page * @param ResultWrapper $res */ protected function showContributions(ResultWrapper $res) { $numRows = $res->numRows(); $rev = null; $out = $this->getOutput(); $revs = array(); $prevRevs = array(); foreach ($res as $row) { $rev = new Revision($row); $revs[] = $rev; if ($res->key() <= self::LIMIT + 1 && $rev->getParentId()) { $prevRevs[] = $rev->getParentId(); } } $this->prevLengths = Revision::getParentLengths(wfGetDB(DB_SLAVE), $prevRevs); if ($numRows > 0) { $count = 0; foreach ($revs as $rev) { if ($count++ < self::LIMIT) { $this->showContributionsRow($rev); } } $out->addHtml('</ul>'); // Captured 1 more than we should have done so if the number of // results is greater than the limit there are more to show. if ($numRows > self::LIMIT) { $out->addHtml($this->getMoreButton($rev->getTimestamp())); } } else { // For users who exist but have not made any edits $out->addHtml(MobileUI::warningBox($this->msg('mobile-frontend-history-no-results'))); } }
/** * Get the number of items in the list. * @return int */ public function length() { if (!$this->res) { return 0; } else { return $this->res->numRows(); } }
/** * Pre-fill the link cache * * @param DatabaseBase $db * @param ResultWrapper $res */ function preprocessResults($db, $res) { if ($res->numRows() > 0) { $linkBatch = new LinkBatch(); foreach ($res as $row) { $linkBatch->add($row->namespace, $row->title); } $res->seek(0); $linkBatch->execute(); } }
/** * Run a LinkBatch to pre-cache LinkCache information, * like page existence and information for stub color and redirect hints. * This should be done for live data and cached data. * * @param IDatabase $db * @param ResultWrapper $res */ public function preprocessResults($db, $res) { if (!$res->numRows()) { return; } $batch = new LinkBatch(); foreach ($res as $row) { $batch->add($row->namespace, $row->title); } $batch->execute(); $res->seek(0); }
/** * Fetch user page links and cache their existence * * @param IDatabase $db * @param ResultWrapper $res */ function preprocessResults($db, $res) { if (!$res->numRows()) { return; } $batch = new LinkBatch(); foreach ($res as $row) { $batch->add(NS_CATEGORY, $row->title); } $batch->execute(); // Back to start for display $res->seek(0); }
/** * @param IDatabase $db * @param ResultWrapper $res */ function preprocessResults($db, $res) { # There's no point doing a batch check if we aren't caching results; # the page must exist for it to have been pulled out of the table if (!$this->isCached() || !$res->numRows()) { return; } $batch = new LinkBatch(); foreach ($res as $row) { $batch->add($row->namespace, $row->title); } $batch->execute(); $res->seek(0); }
/** * Cache page existence for performance * * @param DatabaseBase $db * @param ResultWrapper $res */ function preprocessResults($db, $res) { $batch = new LinkBatch(); foreach ($res as $row) { $batch->add($row->namespace, $row->title); $batch->addObj($this->getRedirectTarget($row)); } $batch->execute(); // Back to start for display if ($res->numRows() > 0) { // If there are no rows we get an error seeking. $db->dataSeek($res, 0); } }
/** * Cache page existence for performance * * @param IDatabase $db * @param ResultWrapper $res */ function preprocessResults($db, $res) { if (!$res->numRows()) { return; } $batch = new LinkBatch(); foreach ($res as $row) { $batch->add($row->namespace, $row->title); $batch->addObj($this->getRedirectTarget($row)); } $batch->execute(); // Back to start for display $res->seek(0); }
/** * @return int */ public function count() { return $this->res->numRows(); }
/** * Generic list of deleted pages * * @param ResultWrapper $result * @return bool */ private function showList($result) { $out = $this->getOutput(); if ($result->numRows() == 0) { $out->addWikiMsg('undelete-no-results'); return false; } $out->addWikiMsg('undeletepagetext', $this->getLanguage()->formatNum($result->numRows())); $undelete = $this->getPageTitle(); $out->addHTML("<ul>\n"); foreach ($result as $row) { $title = Title::makeTitleSafe($row->ar_namespace, $row->ar_title); if ($title !== null) { $item = Linker::linkKnown($undelete, htmlspecialchars($title->getPrefixedText()), array(), array('target' => $title->getPrefixedText())); } else { // The title is no longer valid, show as text $item = Html::element('span', array('class' => 'mw-invalidtitle'), Linker::getInvalidTitleDescription($this->getContext(), $row->ar_namespace, $row->ar_title)); } $revs = $this->msg('undeleterevisions')->numParams($row->count)->parse(); $out->addHTML("<li>{$item} ({$revs})</li>\n"); } $result->free(); $out->addHTML("</ul>\n"); return true; }
/** * Cache page content model and gender distinction for performance * * @param IDatabase $db * @param ResultWrapper $res */ function preprocessResults($db, $res) { if (!$res->numRows()) { return; } $batch = new LinkBatch(); foreach ($res as $row) { $batch->add($row->namespace, $row->title); if (isset($row->nsb)) { // lazy loaded when using cached results $batch->add($row->nsb, $row->tb); } if (isset($row->iwc) && !$row->iwc) { // lazy loaded when using cached result, not added when interwiki link $batch->add($row->nsc, $row->tc); } } $batch->execute(); // Back to start for display $res->seek(0); }
/** * Creates a new LinkBatch object, adds all pages from the passed ResultWrapper (MUST include * title and optional the namespace field) and executes the batch. This operation will pre-cache * LinkCache information like page existence and information for stub color and redirect hints. * * @param ResultWrapper $res The ResultWrapper object to process. Needs to include the title * field and namespace field, if the $ns parameter isn't set. * @param null $ns Use this namespace for the given titles in the ResultWrapper object, * instead of the namespace value of $res. */ protected function executeLBFromResultWrapper(ResultWrapper $res, $ns = null) { if (!$res->numRows()) { return; } $batch = new LinkBatch(); foreach ($res as $row) { $batch->add($ns !== null ? $ns : $row->namespace, $row->title); } $batch->execute(); $res->seek(0); }
/** * Render the history list * @see showRow() * @see doQuery() * @param ResultWrapper $res The result of doQuery */ protected function showHistory(ResultWrapper $res) { $numRows = $res->numRows(); $rev1 = $rev2 = null; $out = $this->getOutput(); if ($numRows > 0) { foreach ($res as $row) { $rev1 = new Revision($row); if ($rev2) { $this->showRow($rev2, $rev1); } $rev2 = $rev1; } if ($rev1 && $numRows < self::LIMIT + 1) { $this->showRow($rev1, null); } $out->addHtml('</ul>'); // Captured 1 more than we should have done so if the number of // results is greater than the limit there are more to show. if ($numRows > self::LIMIT) { $out->addHtml($this->getMoreButton($rev1->getTimestamp())); } } else { // Edge case. // I suspect this is here because revisions may exist but may have been hidden. $out->addHtml(MobileUI::warningBox($this->msg('mobile-frontend-history-no-results'))); } }
/** * Partition a DB result with backlinks in it into batches * @param ResultWrapper $res Database result * @param int $batchSize * @param bool $isComplete Whether $res includes all the backlinks * @throws MWException * @return array */ protected function partitionResult($res, $batchSize, $isComplete = true) { $batches = array(); $numRows = $res->numRows(); $numBatches = ceil($numRows / $batchSize); for ($i = 0; $i < $numBatches; $i++) { if ($i == 0 && $isComplete) { $start = false; } else { $rowNum = $i * $batchSize; $res->seek($rowNum); $row = $res->fetchObject(); $start = (int) $row->page_id; } if ($i == $numBatches - 1 && $isComplete) { $end = false; } else { $rowNum = min($numRows - 1, ($i + 1) * $batchSize - 1); $res->seek($rowNum); $row = $res->fetchObject(); $end = (int) $row->page_id; } # Sanity check order if ($start && $end && $start > $end) { throw new MWException(__METHOD__ . ': Internal error: query result out of order'); } $batches[] = array($start, $end); } return array('numRows' => $numRows, 'batches' => $batches); }
/** * Get the number of rows in the result set * * @return Integer */ function getNumRows() { if ( !$this->mQueryDone ) { $this->doQuery(); } return $this->mResult->numRows(); }
/** * Extract some useful data from the result object for use by * the navigation bar, put it into $this */ function extractResultInfo($offset, $limit, ResultWrapper $res) { $numRows = $res->numRows(); if ($numRows) { $row = $res->fetchRow(); $firstIndex = $row[$this->mIndexField]; # Discard the extra result row if there is one if ($numRows > $this->mLimit && $numRows > 1) { $res->seek($numRows - 1); $this->mPastTheEndRow = $res->fetchObject(); $indexField = $this->mIndexField; $this->mPastTheEndIndex = $this->mPastTheEndRow->{$indexField}; $res->seek($numRows - 2); $row = $res->fetchRow(); $lastIndex = $row[$this->mIndexField]; } else { $this->mPastTheEndRow = null; # Setting indexes to an empty string means that they will be # omitted if they would otherwise appear in URLs. It just so # happens that this is the right thing to do in the standard # UI, in all the relevant cases. $this->mPastTheEndIndex = ''; $res->seek($numRows - 1); $row = $res->fetchRow(); $lastIndex = $row[$this->mIndexField]; } } else { $firstIndex = ''; $lastIndex = ''; $this->mPastTheEndRow = null; $this->mPastTheEndIndex = ''; } if ($this->mIsBackwards) { $this->mIsFirst = $numRows < $limit; $this->mIsLast = $offset == ''; $this->mLastShown = $firstIndex; $this->mFirstShown = $lastIndex; } else { $this->mIsFirst = $offset == ''; $this->mIsLast = $numRows < $limit; $this->mLastShown = $lastIndex; $this->mFirstShown = $firstIndex; } }
/** * Log a SQL statement. This function does not log every statement but samples * at the rate defined in self::QUERY_SAMPLE_RATE. * * @param string $sql the query * @param ResultWrapper|resource|bool $ret database results * @param string $fname the name of the function that made this query * @param bool $isMaster is this against the master * @return void */ protected function logSql($sql, $ret, $fname, $elapsedTime, $isMaster) { global $wgDBcluster; if ($ret instanceof ResultWrapper) { // NOP for MySQL driver $num_rows = $ret->numRows(); } elseif ($ret instanceof mysqli_result) { // for SELECT queries report how many rows are sent to the client // for INSERT, UPDATE, DELETE, DROP queries report affected rows $num_rows = $ret->num_rows ?: $this->affectedRows(); } elseif (is_resource($ret)) { // for SELECT queries report how many rows are sent to the client $num_rows = mysql_num_rows($ret); } elseif ($ret === true) { // for INSERT, UPDATE, DELETE, DROP queries report affected rows $num_rows = $this->affectedRows(); } else { // failed queries $num_rows = false; } if ($this->getSampler()->shouldSample()) { $this->getWikiaLogger()->info("SQL {$sql}", ['method' => $fname, 'elapsed' => $elapsedTime, 'num_rows' => $num_rows, 'cluster' => $wgDBcluster, 'server' => $this->mServer, 'server_role' => $isMaster ? 'master' : 'slave', 'db_name' => $this->mDBname, 'exception' => new Exception()]); } }
/** * Build and output the actual changes list. * * @param ResultWrapper $rows Database rows * @param FormOptions $opts */ public function outputChangesList($rows, $opts) { $dbr = $this->getDB(); $user = $this->getUser(); $output = $this->getOutput(); # Show a message about replica DB lag, if applicable $lag = wfGetLB()->safeGetLag($dbr); if ($lag > 0) { $output->showLagWarning($lag); } # If no rows to display, show message before try to render the list if ($rows->numRows() == 0) { $output->wrapWikiMsg("<div class='mw-changeslist-empty'>\n\$1\n</div>", 'recentchanges-noresult'); return; } $dbr->dataSeek($rows, 0); $list = ChangesList::newFromContext($this->getContext()); $list->setWatchlistDivs(); $list->initChangesListRows($rows); $dbr->dataSeek($rows, 0); if ($this->getConfig()->get('RCShowWatchingUsers') && $user->getOption('shownumberswatching')) { $watchedItemStore = MediaWikiServices::getInstance()->getWatchedItemStore(); } $s = $list->beginRecentChangesList(); $userShowHiddenCats = $this->getUser()->getBoolOption('showhiddencats'); $counter = 1; foreach ($rows as $obj) { # Make RC entry $rc = RecentChange::newFromRow($obj); # Skip CatWatch entries for hidden cats based on user preference if ($rc->getAttribute('rc_type') == RC_CATEGORIZE && !$userShowHiddenCats && $rc->getParam('hidden-cat')) { continue; } $rc->counter = $counter++; if ($this->getConfig()->get('ShowUpdatedMarker')) { $updated = $obj->wl_notificationtimestamp; } else { $updated = false; } if (isset($watchedItemStore)) { $rcTitleValue = new TitleValue((int) $obj->rc_namespace, $obj->rc_title); $rc->numberofWatchingusers = $watchedItemStore->countWatchers($rcTitleValue); } else { $rc->numberofWatchingusers = 0; } $changeLine = $list->recentChangesLine($rc, $updated, $counter); if ($changeLine !== false) { $s .= $changeLine; } } $s .= $list->endRecentChangesList(); $output->addHTML($s); }
/** * Invalidate a set of IDs, right now */ public function invalidateIDs(ResultWrapper $res) { global $wgUseFileCache, $wgUseSquid; if ($res->numRows() == 0) { return; } // sanity check $dbw = wfGetDB(DB_MASTER); $timestamp = $dbw->timestamp(); $done = false; while (!$done) { # Get all IDs in this query into an array $ids = array(); for ($i = 0; $i < $this->mRowsPerQuery; $i++) { $row = $res->fetchRow(); if ($row) { $ids[] = $row[0]; } else { $done = true; break; } } if (count($ids) == 0) { break; } # Update page_touched $dbw->update('page', array('page_touched' => $timestamp), array('page_id' => $ids), __METHOD__); # Update static caches if ($wgUseSquid || $wgUseFileCache) { $titles = Title::newFromIDs($ids); # Update squid cache if ($wgUseSquid) { $u = SquidUpdate::newFromTitles($titles); $u->doUpdate(); } # Update file cache if ($wgUseFileCache) { foreach ($titles as $title) { HTMLFileCache::clearFileCache($title); } } } } }
/** * @return bool */ public function isEmpty() { return $this->res->numRows() === 0; }
/** * Fill in member variables from a result wrapper */ function loadFromResult(ResultWrapper $res, $killExpired = true) { $ret = false; if (0 != $res->numRows()) { # Get first block $row = $res->fetchObject(); $this->initFromRow($row); if ($killExpired) { # If requested, delete expired rows do { $killed = $this->deleteIfExpired(); if ($killed) { $row = $res->fetchObject(); if ($row) { $this->initFromRow($row); } } } while ($killed && $row); # If there were any left after the killing finished, return true if ($row) { $ret = true; } } else { $ret = true; } } $res->free(); return $ret; }
/** * Send output to the OutputPage object, only called if not used feeds * * @param ResultWrapper $rows Database rows * @param FormOptions $opts */ public function webOutput($rows, $opts) { if (!$this->including()) { $this->outputFeedLinks(); $this->doHeader($opts, $rows->numRows()); } $this->outputChangesList($rows, $opts); }
/** * Invalidate a set of IDs, right now */ function invalidateIDs(ResultWrapper $res) { global $wgUseFileCache, $wgUseSquid; if ($res->numRows() == 0) { return; } $dbw =& wfGetDB(DB_MASTER); $timestamp = $dbw->timestamp(); $done = false; while (!$done) { # Get all IDs in this query into an array $ids = array(); for ($i = 0; $i < $this->mRowsPerQuery; $i++) { $row = $res->fetchRow(); if ($row) { $ids[] = $row[0]; } else { $done = true; break; } } if (!count($ids)) { break; } # Update page_touched $dbw->update('page', array('page_touched' => $timestamp), array('page_id IN (' . $dbw->makeList($ids) . ')'), __METHOD__); # Update squid if ($wgUseSquid || $wgUseFileCache) { $titles = Title::newFromIDs($ids); if ($wgUseSquid) { $u = SquidUpdate::newFromTitles($titles); $u->doUpdate(); } # Update file cache if ($wgUseFileCache) { foreach ($titles as $title) { $cm = new CacheManager($title); @unlink($cm->fileCacheName()); } } } } }
/** * Build and output the actual changes list. * * @param ResultWrapper $rows Database rows * @param FormOptions $opts */ public function outputChangesList($rows, $opts) { $dbr = $this->getDB(); $user = $this->getUser(); $output = $this->getOutput(); # Show a message about slave lag, if applicable $lag = wfGetLB()->safeGetLag($dbr); if ($lag > 0) { $output->showLagWarning($lag); } # If no rows to display, show message before try to render the list if ($rows->numRows() == 0) { $output->wrapWikiMsg("<div class='mw-changeslist-empty'>\n\$1\n</div>", 'recentchanges-noresult'); return; } $dbr->dataSeek($rows, 0); $list = ChangesList::newFromContext($this->getContext()); $list->setWatchlistDivs(); $list->initChangesListRows($rows); $dbr->dataSeek($rows, 0); $s = $list->beginRecentChangesList(); $counter = 1; foreach ($rows as $obj) { # Make RC entry $rc = RecentChange::newFromRow($obj); $rc->counter = $counter++; if ($this->getConfig()->get('ShowUpdatedMarker')) { $updated = $obj->wl_notificationtimestamp; } else { $updated = false; } if ($this->getConfig()->get('RCShowWatchingUsers') && $user->getOption('shownumberswatching')) { $rc->numberofWatchingusers = $dbr->selectField('watchlist', 'COUNT(*)', array('wl_namespace' => $obj->rc_namespace, 'wl_title' => $obj->rc_title), __METHOD__); } else { $rc->numberofWatchingusers = 0; } $changeLine = $list->recentChangesLine($rc, $updated, $counter); if ($changeLine !== false) { $s .= $changeLine; } } $s .= $list->endRecentChangesList(); $output->addHTML($s); }
/** * Render the history list * @see showRow() * @see doQuery() * @param ResultWrapper $res The result of doQuery */ protected function showHistory(ResultWrapper $res) { $numRows = $res->numRows(); $rev1 = $rev2 = null; $out = $this->getOutput(); if ($numRows > 0) { foreach ($res as $row) { $rev1 = new Revision($row); if ($rev2) { $this->showRow($rev2, $rev1); } $rev2 = $rev1; } if ($rev1 && $numRows < self::LIMIT + 1) { $this->showRow($rev1, null); } $out->addHtml('</ul>'); // Captured 1 more than we should have done so if the number of // results is greater than the limit there are more to show. if ($numRows > self::LIMIT) { $out->addHtml($this->getMoreButton($rev1->getTimestamp())); } } else { $out->addHtml(Html::element('div', array('class' => 'error alert'), $this->msg('mobile-frontend-history-no-results'))); } }