Esempio n. 1
0
 public static function reconcile()
 {
     global $wgLanguageCode;
     // Add titles missing from our system with associated keywords
     $dbr = wfGetDB(DB_SLAVE);
     $sql = "select page.* from page left join dedup.title_query on tq_page_id=page_id AND tq_lang=" . $dbr->addQuotes($wgLanguageCode) . " where page_namespace=0 and page_is_redirect=0 and tq_title is NULL group by page.page_id";
     $res = $dbr->query($sql, __METHOD__);
     $missingTitles = array();
     foreach ($res as $row) {
         $missingTitles[] = $row;
     }
     foreach ($missingTitles as $title) {
         print "Adding title to system " . $title->page_title . "\n";
         $t = Title::newFromRow($title);
         DedupQuery::addTitle($t, $wgLanguageCode);
     }
     //Deal with titles turned into deletes or redirects
     $dbr = wfGetDB(DB_SLAVE);
     $sql = "select tq_title from dedup.title_query left join page on tq_page_id=page_id where page_namespace=0 and page_is_redirect=0 and page_title is NULL";
     $res = $dbr->query($sql, __METHOD__);
     $deletedTitles = array();
     foreach ($res as $row) {
         $deletedTitles[] = $row->page_title;
     }
     foreach ($deletedTitles as $title) {
         print "Removing title from system " . $row->page_title . "\n";
         DedupQuery::removeTitle($row->page_title, $wgLanguageCode);
     }
 }
 /**
  * Grab all the articles
  */
 private static function cycleThroughAllArticles()
 {
     $csv = fopen(self::CSV_FILE, 'a');
     if (!$csv) {
         print "error: opening a file\n";
         exit;
     }
     fputcsv($csv, array('url', 'type', 'old', 'new'), chr(9));
     $res = DatabaseHelper::batchSelect('page', array('page_title'), array('page_namespace' => NS_MAIN, 'page_is_redirect' => 0));
     print 'articles: ' . count($res) . "\n";
     $count = 0;
     $num = 1;
     foreach ($res as $row) {
         //print $num.' - ';
         $title = Title::newFromRow($row);
         if ($title) {
             //print $title->getDBKey();
             if (self::processSubheaders($title, $csv)) {
                 $count++;
             }
             if ($count >= 5000) {
                 break;
             }
         }
         //print "\n";
         $num++;
     }
     print "\nchanged: " . $count . "\n";
     return;
 }
 public function formatRow($row)
 {
     $title = Title::newFromRow($row);
     $link = Linker::link($title);
     # Link to page
     $dirmark = $this->getLanguage()->getDirMark();
     # Direction mark
     $stxt = '';
     # Size (bytes)
     if (!is_null($size = $row->page_len)) {
         if ($size == 0) {
             $stxt = ' <small>' . $this->msg('historyempty')->escaped() . '</small>';
         } else {
             $stxt = ' <small>' . $this->msg('historysize')->numParams($size)->escaped() . '</small>';
         }
     }
     # Link to list of reviewed versions for page
     $list = Linker::linkKnown(SpecialPage::getTitleFor('ReviewedVersions'), $this->msg('reviewedpages-all')->escaped(), array(), 'page=' . $title->getPrefixedUrl());
     # Link to highest tier rev
     $best = '';
     if (FlaggedRevs::qualityVersions()) {
         $best = Linker::linkKnown($title, $this->msg('reviewedpages-best')->escaped(), array(), 'stableid=best');
         $best = " [{$best}]";
     }
     return "<li>{$link} {$dirmark} {$stxt} ({$list}){$best}</li>";
 }
 public function formatRow($row)
 {
     $title = Title::newFromRow($row);
     # Link to page
     $link = Linker::link($title);
     # Link to page configuration
     $config = Linker::linkKnown(SpecialPage::getTitleFor('Stabilization'), wfMsgHtml('configuredpages-config'), array(), 'page=' . $title->getPrefixedUrl());
     # Show which version is the default (stable or draft)
     if (intval($row->fpc_override)) {
         $default = wfMsgHtml('configuredpages-def-stable');
     } else {
         $default = wfMsgHtml('configuredpages-def-draft');
     }
     # Autoreview/review restriction level
     $restr = '';
     if ($row->fpc_level != '') {
         $restr = 'autoreview=' . htmlspecialchars($row->fpc_level);
         $restr = "[{$restr}]";
     }
     # When these configuration settings expire
     if ($row->fpc_expiry != 'infinity' && strlen($row->fpc_expiry)) {
         $expiry_description = " (" . wfMsgForContent('protect-expiring', $this->getLang()->timeanddate($row->fpc_expiry), $this->getLang()->date($row->fpc_expiry), $this->getLang()->time($row->fpc_expiry)) . ")";
     } else {
         $expiry_description = "";
     }
     return "<li>{$link} ({$config}) <b>[{$default}]</b> " . "{$restr}<i>{$expiry_description}</i></li>";
 }
 function execute()
 {
     global $wgParser;
     $wgParser->parse(' ', Title::newMainPage(), new ParserOptions());
     $dbr = wfGetDB(DB_SLAVE);
     $res = $dbr->select('page', '*', array('page_namespace' => HACL_NS_ACL), __METHOD__);
     $titles = array();
     foreach ($res as $row) {
         $titles[] = Title::newFromRow($row);
     }
     $quiet = $this->hasOption('quiet');
     $seen = array();
     foreach ($titles as $title) {
         $page = new WikiPage($title);
         $pf = IACLParserFunctions::instance($title);
         IACLParserFunctions::parse($page->getText(), $title);
         $errors = $pf->consistencyCheckStatus(false);
         $errors = array_merge($errors, $pf->errors);
         if ($pf->def) {
             if (isset($seen[$pf->def['key']])) {
                 $errors[] = "Duplicate definition! Previous one is " . $seen[$pf->def['key']];
             }
             $seen[$pf->def['key']] = "{$title}";
         }
         IACLParserFunctions::destroyInstance($pf);
         if ($errors) {
             print "Errors on {$title}:\n";
             foreach ($errors as $e) {
                 print "\t{$e}\n";
             }
         } elseif (!$quiet) {
             print "OK {$title}\n";
         }
     }
 }
 /**
  * @param $row ResultWrapper
  * @return void
  */
 protected function setCurrent($row)
 {
     if ($row === false) {
         $this->current = false;
     } else {
         $this->current = Title::newFromRow($row);
     }
 }
 protected function makeTitle($name)
 {
     $t = Title::newFromText($name);
     $row = new stdClass();
     $row->page_id = 1;
     $row->page_title = $t->getDBkey();
     $row->page_namespace = $t->getNamespace();
     return Title::newFromRow($row);
 }
 public function execute()
 {
     global $wgGlobalUsageDatabase;
     $dbr = wfGetDB(DB_SLAVE);
     $dbw = wfGetDB(DB_MASTER, array(), $wgGlobalUsageDatabase);
     $gu = new GlobalUsage(wfWikiId(), $dbw);
     $lastPageId = intval($this->getOption('start-page', 0));
     $lastIlTo = $this->getOption('start-image');
     $limit = 500;
     $maxlag = intval($this->getOption('maxlag', 5));
     do {
         $this->output("Querying links after (page_id, il_to) = ({$lastPageId}, {$lastIlTo})\n");
         # Query all pages and any imagelinks associated with that
         $quotedLastIlTo = $dbr->addQuotes($lastIlTo);
         $res = $dbr->select(array('page', 'imagelinks', 'image'), array('page_id', 'page_namespace', 'page_title', 'il_to', 'img_name'), "(page_id = {$lastPageId} AND il_to > {$quotedLastIlTo})" . " OR page_id > {$lastPageId}", __METHOD__, array('ORDER BY' => $dbr->implicitOrderBy() ? 'page_id' : 'page_id, il_to', 'LIMIT' => $limit), array('imagelinks' => array('LEFT JOIN', 'page_id = il_from'), 'image' => array('LEFT JOIN', 'il_to = img_name')));
         # Build up a tree per pages
         $pages = array();
         $lastRow = null;
         foreach ($res as $row) {
             if (!isset($pages[$row->page_id])) {
                 $pages[$row->page_id] = array();
             }
             # Add the imagelinks entry to the pages array if the image
             # does not exist locally
             if (!is_null($row->il_to) && is_null($row->img_name)) {
                 $pages[$row->page_id][$row->il_to] = $row;
             }
             $lastRow = $row;
         }
         # Insert the imagelinks data to the global table
         foreach ($pages as $pageId => $rows) {
             # Delete all original links if this page is not a continuation
             # of last iteration.
             if ($pageId != $lastPageId) {
                 $gu->deleteLinksFromPage($pageId);
             }
             if ($rows) {
                 $title = Title::newFromRow(reset($rows));
                 $images = array_keys($rows);
                 # Since we have a pretty accurate page_id, don't specify
                 # Title::GAID_FOR_UPDATE
                 $gu->insertLinks($title, $images, 0);
             }
         }
         if ($lastRow) {
             # We've processed some rows in this iteration, so save
             # continuation variables
             $lastPageId = $lastRow->page_id;
             $lastIlTo = $lastRow->il_to;
             # Be nice to the database
             $dbw->commit();
             wfWaitForSlaves($maxlag, $wgGlobalUsageDatabase);
         }
     } while (!is_null($lastRow));
 }
 protected function autoreview_current(User $user)
 {
     $this->output("Auto-reviewing all current page versions...\n");
     if (!$user->getID()) {
         $this->output("Invalid user specified.\n");
         return;
     } elseif (!$user->isAllowed('review')) {
         $this->output("User specified (id: {$user->getID()}) does not have \"review\" rights.\n");
         return;
     }
     $db = wfGetDB(DB_MASTER);
     $this->output("Reviewer username: "******"\n");
     $start = $db->selectField('page', 'MIN(page_id)', false, __METHOD__);
     $end = $db->selectField('page', 'MAX(page_id)', false, __METHOD__);
     if (is_null($start) || is_null($end)) {
         $this->output("...page table seems to be empty.\n");
         return;
     }
     # Do remaining chunk
     $end += $this->mBatchSize - 1;
     $blockStart = $start;
     $blockEnd = $start + $this->mBatchSize - 1;
     $count = 0;
     $changed = 0;
     $flags = FlaggedRevs::quickTags(FR_CHECKED);
     // Assume basic level
     while ($blockEnd <= $end) {
         $this->output("...doing page_id from {$blockStart} to {$blockEnd}\n");
         $res = $db->select(array('page', 'revision'), '*', array("page_id BETWEEN {$blockStart} AND {$blockEnd}", 'page_namespace' => FlaggedRevs::getReviewNamespaces(), 'rev_id = page_latest'), __METHOD__);
         # Go through and autoreview the current version of every page...
         foreach ($res as $row) {
             $title = Title::newFromRow($row);
             $rev = Revision::newFromRow($row);
             # Is it already reviewed?
             $frev = FlaggedRevision::newFromTitle($title, $row->page_latest, FR_MASTER);
             # Rev should exist, but to be safe...
             if (!$frev && $rev) {
                 $article = new Article($title);
                 $db->begin();
                 FlaggedRevs::autoReviewEdit($article, $user, $rev, $flags, true);
                 FlaggedRevs::HTMLCacheUpdates($article->getTitle());
                 $db->commit();
                 $changed++;
             }
             $count++;
         }
         $db->freeResult($res);
         $blockStart += $this->mBatchSize - 1;
         $blockEnd += $this->mBatchSize - 1;
         // XXX: Don't let deferred jobs array get absurdly large (bug 24375)
         DeferredUpdates::doUpdates('commit');
         wfWaitForSlaves(5);
     }
     $this->output("Auto-reviewing of all pages complete ..." . "{$count} rows [{$changed} changed]\n");
 }
 private function run($resultPageSet = null)
 {
     $params = $this->extractRequestParams();
     // Construct SQL Query
     $this->addTables(array('page', 'flaggedpage_config', 'flaggedpages'));
     if (isset($params['namespace'])) {
         $this->addWhereFld('page_namespace', $params['namespace']);
     }
     if (isset($params['default'])) {
         // Convert readable 'stable'/'latest' to 0/1 (DB format)
         $override = $params['default'] === 'stable' ? 1 : 0;
         $this->addWhereFld('fpc_override', $override);
     }
     if (isset($params['autoreview'])) {
         // Convert readable 'none' to '' (DB format)
         $level = $params['autoreview'] === 'none' ? '' : $params['autoreview'];
         $this->addWhereFld('fpc_level', $level);
     }
     $this->addWhereRange('fpc_page_id', $params['dir'], $params['start'], $params['end']);
     $this->addJoinConds(array('flaggedpage_config' => array('INNER JOIN', 'page_id=fpc_page_id'), 'flaggedpages' => array('LEFT JOIN', 'page_id=fp_page_id')));
     $this->addOption('USE INDEX', array('flaggedpage_config' => 'PRIMARY'));
     if (is_null($resultPageSet)) {
         $this->addFields(array('page_id', 'page_namespace', 'page_title', 'page_len', 'page_latest', 'fpc_page_id', 'fpc_override', 'fpc_level', 'fpc_expiry', 'fp_stable'));
     } else {
         $this->addFields($resultPageSet->getPageTableFields());
         $this->addFields('fpc_page_id');
     }
     $limit = $params['limit'];
     $this->addOption('LIMIT', $limit + 1);
     $res = $this->select(__METHOD__);
     $data = array();
     $count = 0;
     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...
             $this->setContinueEnumParameter('start', $row->fpc_page_id);
             break;
         }
         if (is_null($resultPageSet)) {
             $title = Title::newFromRow($row);
             $data[] = array('pageid' => intval($row->page_id), 'ns' => intval($row->page_namespace), 'title' => $title->getPrefixedText(), 'last_revid' => intval($row->page_latest), 'stable_revid' => intval($row->fp_stable), 'stable_is_default' => intval($row->fpc_override), 'autoreview' => $row->fpc_level, 'expiry' => $row->fpc_expiry === 'infinity' ? 'infinity' : wfTimestamp(TS_ISO_8601, $row->fpc_expiry));
         } else {
             $resultPageSet->processDbRow($row);
         }
     }
     if (is_null($resultPageSet)) {
         $result = $this->getResult();
         $result->setIndexedTagName($data, 'p');
         $result->addValue('query', $this->getModuleName(), $data);
     }
 }
Esempio n. 11
0
 private function run($resultPageSet = null)
 {
     $params = $this->extractRequestParams();
     // Construct SQL Query
     $this->addTables(array('page', 'flaggedpages'));
     $this->addWhereFld('page_namespace', $params['namespace']);
     if ($params['filterredir'] == 'redirects') {
         $this->addWhereFld('page_is_redirect', 1);
     }
     if ($params['filterredir'] == 'nonredirects') {
         $this->addWhereFld('page_is_redirect', 0);
     }
     if ($params['filterlevel'] !== null) {
         $this->addWhereFld('fp_quality', $params['filterlevel']);
     }
     $this->addWhereRange('fp_page_id', $params['dir'], $params['start'], $params['end']);
     $this->addWhere('page_id=fp_page_id');
     $this->addOption('USE INDEX', array('flaggedpages' => 'PRIMARY'));
     if (is_null($resultPageSet)) {
         $this->addFields(array('page_id', 'page_namespace', 'page_title', 'page_len', 'page_latest', 'fp_page_id', 'fp_quality', 'fp_stable'));
     } else {
         $this->addFields($resultPageSet->getPageTableFields());
         $this->addFields('fp_page_id');
     }
     $limit = $params['limit'];
     $this->addOption('LIMIT', $limit + 1);
     $res = $this->select(__METHOD__);
     $data = array();
     $count = 0;
     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...
             $this->setContinueEnumParameter('start', $row->fp_page_id);
             break;
         }
         if (is_null($resultPageSet)) {
             $title = Title::newFromRow($row);
             $data[] = array('pageid' => intval($row->page_id), 'ns' => intval($title->getNamespace()), 'title' => $title->getPrefixedText(), 'revid' => intval($row->page_latest), 'stable_revid' => intval($row->fp_stable), 'flagged_level' => intval($row->fp_quality), 'flagged_level_text' => FlaggedRevs::getQualityLevelText($row->fp_quality));
         } else {
             $resultPageSet->processDbRow($row);
         }
     }
     if (is_null($resultPageSet)) {
         $result = $this->getResult();
         $result->setIndexedTagName($data, 'p');
         $result->addValue('query', $this->getModuleName(), $data);
     }
 }
    private function run($resultPageSet = null)
    {
        global $wgMemc;
        $params = $this->extractRequestParams();
        // Construct SQL Query
        $this->addTables(array('page', 'flaggedpages'));
        $this->addWhereFld('page_namespace', $params['namespace']);
        if ($params['filterredir'] == 'redirects') {
            $this->addWhereFld('page_is_redirect', 1);
        }
        if ($params['filterredir'] == 'nonredirects') {
            $this->addWhereFld('page_is_redirect', 0);
        }
        $dir = $params['dir'] == 'descending' ? 'older' : 'newer';
        $this->addWhereRange('page_title', $dir, $params['start'], $params['end']);
        $this->addJoinConds(array('flaggedpages' => array('LEFT JOIN', 'fp_page_id=page_id')));
        $this->addWhere('fp_page_id IS NULL OR
			fp_quality < ' . intval($params['filterlevel']));
        $this->addOption('USE INDEX', array('page' => 'name_title', 'flaggedpages' => 'PRIMARY'));
        if (is_null($resultPageSet)) {
            $this->addFields(array('page_id', 'page_namespace', 'page_title', 'page_len', 'page_latest'));
        } else {
            $this->addFields($resultPageSet->getPageTableFields());
        }
        $limit = $params['limit'];
        $this->addOption('LIMIT', $limit + 1);
        $res = $this->select(__METHOD__);
        $data = array();
        $count = 0;
        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...
                $this->setContinueEnumParameter('start', $row->page_title);
                break;
            }
            if (is_null($resultPageSet)) {
                $title = Title::newFromRow($row);
                $data[] = array('pageid' => intval($row->page_id), 'ns' => intval($title->getNamespace()), 'title' => $title->getPrefixedText(), 'revid' => intval($row->page_latest), 'under_review' => FRUserActivity::pageIsUnderReview($row->page_id));
            } else {
                $resultPageSet->processDbRow($row);
            }
        }
        if (is_null($resultPageSet)) {
            $result = $this->getResult();
            $result->setIndexedTagName($data, 'p');
            $result->addValue('query', $this->getModuleName(), $data);
        }
    }
 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");
     }
 }
Esempio n. 14
0
 public function execute()
 {
     $pages = $this->getOption('maxpages');
     $dbr = $this->getDB(DB_REPLICA);
     $totalsec = 0.0;
     $scanned = 0;
     $withcache = 0;
     $withdiff = 0;
     while ($pages-- > 0) {
         $row = $dbr->selectRow('page', '*', ['page_namespace' => $this->getOption('namespace'), 'page_is_redirect' => 0, 'page_random >= ' . wfRandom()], __METHOD__, ['ORDER BY' => 'page_random']);
         if (!$row) {
             continue;
         }
         ++$scanned;
         $title = Title::newFromRow($row);
         $page = WikiPage::factory($title);
         $revision = $page->getRevision();
         $content = $revision->getContent(Revision::RAW);
         $parserOptions = $page->makeParserOptions('canonical');
         $parserOutputOld = ParserCache::singleton()->get($page, $parserOptions);
         if ($parserOutputOld) {
             $t1 = microtime(true);
             $parserOutputNew = $content->getParserOutput($title, $revision->getId(), $parserOptions, false);
             $sec = microtime(true) - $t1;
             $totalsec += $sec;
             $this->output("Parsed '{$title->getPrefixedText()}' in {$sec} seconds.\n");
             $this->output("Found cache entry found for '{$title->getPrefixedText()}'...");
             $oldHtml = trim(preg_replace('#<!-- .+-->#Us', '', $parserOutputOld->getText()));
             $newHtml = trim(preg_replace('#<!-- .+-->#Us', '', $parserOutputNew->getText()));
             $diff = wfDiff($oldHtml, $newHtml);
             if (strlen($diff)) {
                 $this->output("differences found:\n\n{$diff}\n\n");
                 ++$withdiff;
             } else {
                 $this->output("No differences found.\n");
             }
             ++$withcache;
         } else {
             $this->output("No parser cache entry found for '{$title->getPrefixedText()}'.\n");
         }
     }
     $ave = $totalsec ? $totalsec / $scanned : 0;
     $this->output("Checked {$scanned} pages; {$withcache} had prior cache entries.\n");
     $this->output("Pages with differences found: {$withdiff}\n");
     $this->output("Average parse time: {$ave} sec\n");
 }
 protected function list_reviewable_pages($fileHandle)
 {
     global $wgFlaggedRevsNamespaces, $wgUseSquid, $wgUseFileCache;
     $this->output("Building list of all reviewable pages to purge ...\n");
     if (!$wgUseSquid && !$wgUseFileCache) {
         $this->output("Squid/file cache not enabled ... nothing to purge.\n");
         return;
     } elseif (empty($wgFlaggedRevsNamespaces)) {
         $this->output("There are no reviewable namespaces ... nothing to purge.\n");
         return;
     }
     $db = wfGetDB(DB_MASTER);
     $start = $db->selectField('page', 'MIN(page_id)', false, __FUNCTION__);
     $end = $db->selectField('page', 'MAX(page_id)', false, __FUNCTION__);
     if (is_null($start) || is_null($end)) {
         $this->output("... page table seems to be empty.\n");
         return;
     }
     # Do remaining chunk
     $end += $this->mBatchSize - 1;
     $blockStart = $start;
     $blockEnd = $start + $this->mBatchSize - 1;
     $count = 0;
     while ($blockEnd <= $end) {
         $this->output("... doing page_id from {$blockStart} to {$blockEnd}\n");
         $res = $db->select('page', '*', array("page_id BETWEEN {$blockStart} AND {$blockEnd}", 'page_namespace' => $wgFlaggedRevsNamespaces), __FUNCTION__);
         # Go through and append each purgeable page...
         foreach ($res as $row) {
             $title = Title::newFromRow($row);
             $fa = FlaggableWikiPage::getTitleInstance($title);
             if ($fa->isReviewable()) {
                 # Need to purge this page - add to list
                 fwrite($fileHandle, $title->getPrefixedDBKey() . "\n");
                 $count++;
             }
         }
         $db->freeResult($res);
         $blockStart += $this->mBatchSize - 1;
         $blockEnd += $this->mBatchSize - 1;
         wfWaitForSlaves(5);
         // not really needed
     }
     $this->output("List of reviewable pages to purge complete ... {$count} pages\n");
 }
 public function execute()
 {
     $pattern = implode('|', array('IAmABad RedirectChain', 'IAmABad RedirectSelf', 'IDontExistLink', 'IDontExistRdir', 'IDontExistRdirLinked', 'ILinkIRedirectToNonExistentPages', 'ILinkToNonExistentPages', 'IRedirectToNonExistentPages', 'IRedirectToNonExistentPagesLinked', 'Move', 'PreferRecent First exists with contents ', 'PreferRecent Second Second exists with contents ', 'PreferRecent Third exists with contents ', 'ReallyLongLink', 'StartsAsRedirect', 'ToBeRedirect', 'WeightedLink', 'WeightedLinkRdir', 'WeightedLinkRemoveUpdate', 'WLDoubleRdir', 'WLRURdir'));
     $pattern = "/{$pattern}/";
     $dbw = wfGetDB(DB_MASTER);
     $user = \User::newFromName('Admin');
     $it = new \EchoBatchRowIterator($dbw, 'page', 'page_id', 500);
     $it->setFetchColumns(array('*'));
     $it = new \RecursiveIteratorIterator($it);
     foreach ($it as $row) {
         if (preg_match($pattern, $row->page_title) !== 1) {
             continue;
         }
         $title = \Title::newFromRow($row);
         $pageObj = \WikiPage::factory($title);
         echo "Deleting page {$title}\n";
         $pageObj->doDeleteArticleReal('cirrussearch maint task');
     }
 }
 private static function _gatherTerms()
 {
     global $wgExtraNamespaces, $wgGlossaryNamespace;
     $dbr = wfGetDB(DB_SLAVE);
     $aTables = array('page');
     $aFields = array('page_namespace', 'page_title');
     $aWhere = array('page_namespace' => $wgGlossaryNamespace);
     $aJoin = array();
     $aOptions = array();
     $res = $dbr->select($aTables, $aFields, $aWhere, __METHOD__);
     $results = array();
     if ($dbr->numRows($res) > 0) {
         while ($row = $dbr->fetchObject($res)) {
             $title = Title::newFromRow($row);
             $article = new Article($title);
             $results[] = array('title' => $title, 'article' => $article);
         }
     }
     return $results;
 }
 /**
  * Grab all the articles
  */
 private static function cycleThroughAllArticles()
 {
     $res = DatabaseHelper::batchSelect('page', array('page_title'), array('page_namespace' => NS_MAIN, 'page_is_redirect' => 0), __METHOD__, array('LIMIT' => self::CHUNK_LIMIT));
     print 'articles: ' . count($res) . "\n";
     $count = 0;
     $num = 1;
     foreach ($res as $row) {
         //print $num.' - ';
         $title = Title::newFromRow($row);
         if ($title) {
             //print $title->getDBKey();
             if (self::processSubheaders($title)) {
                 $count++;
             }
         }
         //print "\n";
         $num++;
     }
     //print "\nchanged: ".$count."\n";
     print "\ntotal: " . $count . "\n";
     return;
 }
Esempio n. 19
0
 /**
  * Make an array of titles from an array of IDs
  *
  * @param $ids Array of Int Array of IDs
  * @return Array of Titles
  */
 public static function newFromIDs($ids)
 {
     if (!count($ids)) {
         return array();
     }
     $dbr = wfGetDB(DB_SLAVE);
     $res = $dbr->select('page', array('page_namespace', 'page_title', 'page_id', 'page_len', 'page_is_redirect', 'page_latest'), array('page_id' => $ids), __METHOD__);
     $titles = array();
     foreach ($res as $row) {
         $titles[] = Title::newFromRow($row);
     }
     return $titles;
 }
Esempio n. 20
0
 static function bulkLoad($rows)
 {
     // Preload subthreads
     $top_thread_ids = array();
     $all_thread_rows = $rows;
     $pageIds = array();
     $linkBatch = new LinkBatch();
     $userIds = array();
     $loadEditorsFor = array();
     $dbr = wfGetDB(DB_SLAVE);
     if (!is_array(self::$replyCacheById)) {
         self::$replyCacheById = array();
     }
     // Build a list of threads for which to pull replies, and page IDs to pull data for.
     //  Also, pre-initialise the reply cache.
     foreach ($rows as $row) {
         if ($row->thread_ancestor) {
             $top_thread_ids[] = $row->thread_ancestor;
         } else {
             $top_thread_ids[] = $row->thread_id;
         }
         // Grab page data while we're here.
         if ($row->thread_root) {
             $pageIds[] = $row->thread_root;
         }
         if ($row->thread_summary_page) {
             $pageIds[] = $row->thread_summary_page;
         }
         if (!isset(self::$replyCacheById[$row->thread_id])) {
             self::$replyCacheById[$row->thread_id] = array();
         }
     }
     $all_thread_ids = $top_thread_ids;
     // Pull replies to the threads provided, and as above, pull page IDs to pull data for,
     //  pre-initialise the reply cache, and stash the row object for later use.
     if (count($top_thread_ids)) {
         $res = $dbr->select('thread', '*', array('thread_ancestor' => $top_thread_ids, 'thread_type != ' . $dbr->addQuotes(Threads::TYPE_DELETED)), __METHOD__);
         foreach ($res as $row) {
             // Grab page data while we're here.
             if ($row->thread_root) {
                 $pageIds[] = $row->thread_root;
             }
             if ($row->thread_summary_page) {
                 $pageIds[] = $row->thread_summary_page;
             }
             $all_thread_rows[] = $row;
             $all_thread_ids[$row->thread_id] = $row->thread_id;
         }
     }
     // Pull thread reactions
     if (count($all_thread_ids)) {
         $res = $dbr->select('thread_reaction', '*', array('tr_thread' => $all_thread_ids), __METHOD__);
         foreach ($res as $row) {
             $thread_id = $row->tr_thread;
             $user = $row->tr_user_text;
             $info = array('type' => $row->tr_type, 'user-id' => $row->tr_user, 'user-name' => $row->tr_user_text, 'value' => $row->tr_value);
             $type = $info['type'];
             $user = $info['user-name'];
             if (!isset(self::$reactionCacheById[$thread_id])) {
                 self::$reactionCacheById[$thread_id] = array();
             }
             if (!isset(self::$reactionCacheById[$thread_id][$type])) {
                 self::$reactionCacheById[$thread_id][$type] = array();
             }
             self::$reactionCacheById[$thread_id][$type][$user] = $info;
         }
     }
     // Preload page data (restrictions, and preload Article object with everything from
     //  the page table. Also, precache the title and article objects for pulling later.
     $articlesById = array();
     if (count($pageIds)) {
         // Pull restriction info. Needs to come first because otherwise it's done per
         //  page by loadPageData.
         $restrictionRows = array_fill_keys($pageIds, array());
         $res = $dbr->select('page_restrictions', '*', array('pr_page' => $pageIds), __METHOD__);
         foreach ($res as $row) {
             $restrictionRows[$row->pr_page][] = $row;
         }
         $res = $dbr->select('page', '*', array('page_id' => $pageIds), __METHOD__);
         foreach ($res as $row) {
             $t = Title::newFromRow($row);
             if (isset($restrictionRows[$t->getArticleId()])) {
                 $t->loadRestrictionsFromRows($restrictionRows[$t->getArticleId()], $row->page_restrictions);
             }
             $article = new Article($t);
             $article->loadPageData($row);
             self::$titleCacheById[$t->getArticleId()] = $t;
             $articlesById[$article->getId()] = $article;
             if (count(self::$titleCacheById) > 10000) {
                 self::$titleCacheById = array();
             }
         }
     }
     // For every thread we have a row object for, load a Thread object, add the user and
     //  user talk pages to a link batch, cache the relevant user id/name pair, and
     //  populate the reply cache.
     foreach ($all_thread_rows as $row) {
         $thread = Thread::newFromRow($row, null);
         if (isset($articlesById[$thread->rootId])) {
             $thread->root = $articlesById[$thread->rootId];
         }
         // User cache data
         $t = Title::makeTitleSafe(NS_USER, $row->thread_author_name);
         $linkBatch->addObj($t);
         $t = Title::makeTitleSafe(NS_USER_TALK, $row->thread_author_name);
         $linkBatch->addObj($t);
         User::$idCacheByName[$row->thread_author_name] = $row->thread_author_id;
         $userIds[$row->thread_author_id] = true;
         if ($row->thread_editedness > Threads::EDITED_BY_AUTHOR) {
             $loadEditorsFor[$row->thread_root] = $thread;
             $thread->setEditors(array());
         }
     }
     // Pull list of users who have edited
     if (count($loadEditorsFor)) {
         $res = $dbr->select('revision', array('rev_user_text', 'rev_page'), array('rev_page' => array_keys($loadEditorsFor), 'rev_parent_id != ' . $dbr->addQuotes(0)), __METHOD__);
         foreach ($res as $row) {
             $pageid = $row->rev_page;
             $editor = $row->rev_user_text;
             $t = $loadEditorsFor[$pageid];
             $t->addEditor($editor);
         }
     }
     // Pull link batch data.
     $linkBatch->execute();
     $threads = array();
     // Fill and return an array with the threads that were actually requested.
     foreach ($rows as $row) {
         $threads[$row->thread_id] = Threads::$cache_by_id[$row->thread_id];
     }
     return $threads;
 }
Esempio n. 21
0
 /**
  * Get an array of Title objects which are articles which use this file
  * Also adds their IDs to the link cache
  *
  * This is mostly copied from Title::getLinksTo()
  *
  * @deprecated Use HTMLCacheUpdate, this function uses too much memory
  */
 function getLinksTo($options = '')
 {
     wfProfileIn(__METHOD__);
     // Note: use local DB not repo DB, we want to know local links
     if ($options) {
         $db = wfGetDB(DB_MASTER);
     } else {
         $db = wfGetDB(DB_SLAVE);
     }
     $linkCache = LinkCache::singleton();
     list($page, $imagelinks) = $db->tableNamesN('page', 'imagelinks');
     $encName = $db->addQuotes($this->getName());
     $sql = "SELECT page_namespace,page_title,page_id,page_len,page_is_redirect,\n\t\t\tFROM {$page},{$imagelinks} WHERE page_id=il_from AND il_to={$encName} {$options}";
     $res = $db->query($sql, __METHOD__);
     $retVal = array();
     if ($db->numRows($res)) {
         while ($row = $db->fetchObject($res)) {
             if ($titleObj = Title::newFromRow($row)) {
                 $linkCache->addGoodLinkObj($row->page_id, $titleObj, $row->page_len, $row->page_is_redirect);
                 $retVal[] = $titleObj;
             }
         }
     }
     $db->freeResult($res);
     wfProfileOut(__METHOD__);
     return $retVal;
 }
Esempio n. 22
0
 /**
  * Get an array of Title objects which are articles which use this file
  * Also adds their IDs to the link cache
  *
  * This is mostly copied from Title::getLinksTo()
  *
  * @deprecated Use HTMLCacheUpdate, this function uses too much memory
  */
 function getLinksTo($options = array())
 {
     wfProfileIn(__METHOD__);
     // Note: use local DB not repo DB, we want to know local links
     if (count($options) > 0) {
         $db = wfGetDB(DB_MASTER);
     } else {
         $db = wfGetDB(DB_SLAVE);
     }
     $linkCache = LinkCache::singleton();
     $encName = $db->addQuotes($this->getName());
     $res = $db->select(array('page', 'imagelinks'), array('page_namespace', 'page_title', 'page_id', 'page_len', 'page_is_redirect'), array('page_id' => 'il_from', 'il_to' => $encName), __METHOD__, $options);
     $retVal = array();
     if ($db->numRows($res)) {
         while ($row = $db->fetchObject($res)) {
             if ($titleObj = Title::newFromRow($row)) {
                 $linkCache->addGoodLinkObj($row->page_id, $titleObj, $row->page_len, $row->page_is_redirect);
                 $retVal[] = $titleObj;
             }
         }
     }
     $db->freeResult($res);
     wfProfileOut(__METHOD__);
     return $retVal;
 }
Esempio n. 23
0
 /**
  * Get categories which are granted readable for current user
  */
 public static function getReadableCategories()
 {
     global $wgUser;
     $uid = $wgUser->getId();
     // Lookup readable categories
     $pe = IACLStorage::get('SD')->getRules(array('pe_type' => IACL::PE_CATEGORY, 'child_type' => IACL::PE_USER, 'child_id' => $uid, '(actions & ' . (IACL::ACTION_READ | IACL::ACTION_READ << IACL::INDIRECT_OFFSET) . ')'));
     if ($pe) {
         foreach ($pe as &$e) {
             $e = $e['pe_id'];
         }
         unset($e);
         $dbr = wfGetDB(DB_SLAVE);
         $res = $dbr->select('page', '*', array('page_id' => $pe), __METHOD__);
         $titles = array();
         foreach ($res as $row) {
             $titles[] = Title::newFromRow($row);
         }
         // Add child categories
         $pe = IACLStorage::get('Util')->getAllChildrenCategories($titles);
     }
     return $pe;
 }
 static function refreshAll()
 {
     $dbw = wfGetDB(DB_MASTER);
     print "Refreshing right definitions...\n";
     $res = $dbw->select('page', '*', array('page_namespace' => HACL_NS_ACL), __METHOD__);
     $titles = array();
     foreach ($res as $row) {
         $titles[] = Title::newFromRow($row);
     }
     foreach ($titles as $title) {
         $article = new WikiPage($title);
         $article->doEdit($article->getText(), 'Re-parse right definition', EDIT_UPDATE);
     }
 }
Esempio n. 25
0
	/**
	 * Constructor from a database row
	 *
	 * @since 1.20
	 * @param $row object: database row containing at least fields returned
	 *        by selectFields().
	 * @param string|int $from source of $data:
	 *        - "fromdb" or WikiPage::READ_NORMAL: from a slave DB
	 *        - "fromdbmaster" or WikiPage::READ_LATEST: from the master DB
	 *        - "forupdate" or WikiPage::READ_LOCKING: from the master DB using SELECT FOR UPDATE
	 * @return WikiPage
	 */
	public static function newFromRow( $row, $from = 'fromdb' ) {
		$page = self::factory( Title::newFromRow( $row ) );
		$page->loadFromRow( $row, $from );
		return $page;
	}
 /**
  * @param Skin $skin
  * @param object $result Result row
  * @return string
  */
 function formatResult($skin, $result)
 {
     $title = Title::newFromRow($result);
     $ret = Linker::link($title, null, array(), array(), array('known'));
     if ($result->pp_value !== '') {
         // Do not show very long or binary values on the special page
         $valueLength = strlen($result->pp_value);
         $isBinary = strpos($result->pp_value, "") !== false;
         $isTooLong = $valueLength > 1024;
         if ($isBinary || $isTooLong) {
             $message = $this->msg($isBinary ? 'pageswithprop-prophidden-binary' : 'pageswithprop-prophidden-long')->params($this->getLanguage()->formatSize($valueLength));
             $propValue = Html::element('span', array('class' => 'prop-value-hidden'), $message->text());
         } else {
             $propValue = Html::element('span', array('class' => 'prop-value'), $result->pp_value);
         }
         $ret .= $this->msg('colon-separator')->escaped() . $propValue;
     }
     return $ret;
 }
Esempio n. 27
0
 /**
  * Get an array of Title objects linked from this Title
  * Also stores the IDs in the link cache.
  *
  * WARNING: do not use this function on arbitrary user-supplied titles!
  * On heavily-used templates it will max out the memory.
  *
  * @param array $options May be FOR UPDATE
  * @param string $table Table name
  * @param string $prefix Fields prefix
  * @return array Array of Title objects linking here
  */
 public function getLinksFrom($options = array(), $table = 'pagelinks', $prefix = 'pl')
 {
     $id = $this->getArticleID();
     # If the page doesn't exist; there can't be any link from this page
     if (!$id) {
         return array();
     }
     if (count($options) > 0) {
         $db = wfGetDB(DB_MASTER);
     } else {
         $db = wfGetDB(DB_SLAVE);
     }
     $blNamespace = "{$prefix}_namespace";
     $blTitle = "{$prefix}_title";
     $res = $db->select(array($table, 'page'), array_merge(array($blNamespace, $blTitle), WikiPage::selectFields()), array("{$prefix}_from" => $id), __METHOD__, $options, array('page' => array('LEFT JOIN', array("page_namespace={$blNamespace}", "page_title={$blTitle}"))));
     $retVal = array();
     $linkCache = LinkCache::singleton();
     foreach ($res as $row) {
         if ($row->page_id) {
             $titleObj = Title::newFromRow($row);
         } else {
             $titleObj = Title::makeTitle($row->{$blNamespace}, $row->{$blTitle});
             $linkCache->addBadLinkObj($titleObj);
         }
         $retVal[] = $titleObj;
     }
     return $retVal;
 }
Esempio n. 28
0
    /**
     * @param int $namespace Namespace (Default NS_MAIN)
     * @param string $from List all pages from this name (default false)
     * @param string $to List all pages to this name (default false)
     * @param bool $hideredirects Dont show redirects (default false)
     */
    function showChunk($namespace = NS_MAIN, $from = false, $to = false, $hideredirects = false)
    {
        $output = $this->getOutput();
        $fromList = $this->getNamespaceKeyAndText($namespace, $from);
        $toList = $this->getNamespaceKeyAndText($namespace, $to);
        $namespaces = $this->getContext()->getLanguage()->getNamespaces();
        $n = 0;
        $prevTitle = null;
        if (!$fromList || !$toList) {
            $out = $this->msg('allpagesbadtitle')->parseAsBlock();
        } elseif (!array_key_exists($namespace, $namespaces)) {
            // Show errormessage and reset to NS_MAIN
            $out = $this->msg('allpages-bad-ns', $namespace)->parse();
            $namespace = NS_MAIN;
        } else {
            list($namespace, $fromKey, $from) = $fromList;
            list(, $toKey, $to) = $toList;
            $dbr = wfGetDB(DB_SLAVE);
            $filterConds = array('page_namespace' => $namespace);
            if ($hideredirects) {
                $filterConds['page_is_redirect'] = 0;
            }
            $conds = $filterConds;
            $conds[] = 'page_title >= ' . $dbr->addQuotes($fromKey);
            if ($toKey !== "") {
                $conds[] = 'page_title <= ' . $dbr->addQuotes($toKey);
            }
            $res = $dbr->select('page', array('page_namespace', 'page_title', 'page_is_redirect', 'page_id'), $conds, __METHOD__, array('ORDER BY' => 'page_title', 'LIMIT' => $this->maxPerPage + 1, 'USE INDEX' => 'name_title'));
            if ($res->numRows() > 0) {
                $out = Html::openElement('div', array('class' => 'mw-allpages-body'));
                $out .= Html::openElement('ul', array('class' => 'mw-allpages-chunk'));
                while ($n < $this->maxPerPage && ($s = $res->fetchObject())) {
                    $t = Title::newFromRow($s);
                    if ($t) {
                        $out .= '<li' . ($s->page_is_redirect ? ' class="allpagesredirect"' : '') . '>' . Linker::link($t) . "</li>\n";
                    } else {
                        $out .= '<li>[[' . htmlspecialchars($s->page_title) . "]]</li>\n";
                    }
                    $n++;
                }
                $out .= Html::closeElement('ul');
                $out .= Html::closeElement('div');
            } else {
                $out = '';
            }
            if ($fromKey !== '' && !$this->including()) {
                # Get the first title from previous chunk
                $prevConds = $filterConds;
                $prevConds[] = 'page_title < ' . $dbr->addQuotes($fromKey);
                $prevKey = $dbr->selectField('page', 'page_title', $prevConds, __METHOD__, array('ORDER BY' => 'page_title DESC', 'OFFSET' => $this->maxPerPage - 1));
                if ($prevKey === false) {
                    # The previous chunk is not complete, need to link to the very first title
                    # available in the database
                    $prevKey = $dbr->selectField('page', 'page_title', $prevConds, __METHOD__, array('ORDER BY' => 'page_title'));
                }
                if ($prevKey !== false) {
                    $prevTitle = Title::makeTitle($namespace, $prevKey);
                }
            }
        }
        if ($this->including()) {
            $output->addHTML($out);
            return;
        }
        $self = $this->getPageTitle();
        $topLinks = array(Linker::link($self, $this->msg('allpages')->escaped()));
        $bottomLinks = array();
        # Do we put a previous link ?
        if ($prevTitle) {
            $query = array('from' => $prevTitle->getText());
            if ($namespace) {
                $query['namespace'] = $namespace;
            }
            if ($hideredirects) {
                $query['hideredirects'] = $hideredirects;
            }
            $prevLink = Linker::linkKnown($self, $this->msg('prevpage', $prevTitle->getText())->escaped(), array(), $query);
            $topLinks[] = $prevLink;
            $bottomLinks[] = $prevLink;
        }
        if ($n == $this->maxPerPage && ($s = $res->fetchObject())) {
            # $s is the first link of the next chunk
            $t = Title::makeTitle($namespace, $s->page_title);
            $query = array('from' => $t->getText());
            if ($namespace) {
                $query['namespace'] = $namespace;
            }
            if ($hideredirects) {
                $query['hideredirects'] = $hideredirects;
            }
            $nextLink = Linker::linkKnown($self, $this->msg('nextpage', $t->getText())->escaped(), array(), $query);
            $topLinks[] = $nextLink;
            $bottomLinks[] = $nextLink;
        }
        $nsForm = $this->namespaceForm($namespace, $from, $to, $hideredirects);
        $out2 = Xml::openElement('table', array('class' => 'mw-allpages-table-form')) . '<tr>
						<td>' . $nsForm . '</td>
						<td class="mw-allpages-nav">' . $this->getLanguage()->pipeList($topLinks) . '</td></tr></table>';
        $output->addHTML($out2 . $out);
        if (count($bottomLinks)) {
            $output->addHTML(Html::element('hr') . Html::rawElement('div', array('class' => 'mw-allpages-nav'), $this->getLanguage()->pipeList($bottomLinks)));
        }
    }
Esempio n. 29
0
 function doCategoryQuery()
 {
     $dbr = wfGetDB(DB_SLAVE, 'category');
     $this->nextPage = array('page' => null, 'subcat' => null, 'file' => null);
     $this->flip = array('page' => false, 'subcat' => false, 'file' => false);
     foreach (array('page', 'subcat', 'file') as $type) {
         # Get the sortkeys for start/end, if applicable.  Note that if
         # the collation in the database differs from the one
         # set in $wgCategoryCollation, pagination might go totally haywire.
         $extraConds = array('cl_type' => $type);
         if (isset($this->from[$type]) && $this->from[$type] !== null) {
             $extraConds[] = 'cl_sortkey >= ' . $dbr->addQuotes($this->collation->getSortKey($this->from[$type]));
         } elseif (isset($this->until[$type]) && $this->until[$type] !== null) {
             $extraConds[] = 'cl_sortkey < ' . $dbr->addQuotes($this->collation->getSortKey($this->until[$type]));
             $this->flip[$type] = true;
         }
         $res = $dbr->select(array('page', 'categorylinks', 'category'), array('page_id', 'page_title', 'page_namespace', 'page_len', 'page_is_redirect', 'cl_sortkey', 'cat_id', 'cat_title', 'cat_subcats', 'cat_pages', 'cat_files', 'cl_sortkey_prefix', 'cl_collation'), array_merge(array('cl_to' => $this->title->getDBkey()), $extraConds), __METHOD__, array('USE INDEX' => array('categorylinks' => 'cl_sortkey'), 'LIMIT' => $this->limit + 1, 'ORDER BY' => $this->flip[$type] ? 'cl_sortkey DESC' : 'cl_sortkey'), array('categorylinks' => array('INNER JOIN', 'cl_from = page_id'), 'category' => array('LEFT JOIN', array('cat_title = page_title', 'page_namespace' => NS_CATEGORY))));
         $count = 0;
         foreach ($res as $row) {
             $title = Title::newFromRow($row);
             if ($row->cl_collation === '') {
                 // Hack to make sure that while updating from 1.16 schema
                 // and db is inconsistent, that the sky doesn't fall.
                 // See r83544. Could perhaps be removed in a couple decades...
                 $humanSortkey = $row->cl_sortkey;
             } else {
                 $humanSortkey = $title->getCategorySortkey($row->cl_sortkey_prefix);
             }
             if (++$count > $this->limit) {
                 # We've reached the one extra which shows that there
                 # are additional pages to be had. Stop here...
                 $this->nextPage[$type] = $humanSortkey;
                 break;
             }
             if ($title->getNamespace() == NS_CATEGORY) {
                 $cat = Category::newFromRow($row, $title);
                 $this->addSubcategoryObject($cat, $humanSortkey, $row->page_len);
             } elseif ($title->getNamespace() == NS_FILE) {
                 $this->addImage($title, $humanSortkey, $row->page_len, $row->page_is_redirect);
             } else {
                 $this->addPage($title, $humanSortkey, $row->page_len, $row->page_is_redirect);
             }
         }
     }
 }
Esempio n. 30
0
 /**
  * Generates each row in the contributions list.
  *
  * Contributions which are marked "top" are currently on top of the history.
  * For these contributions, a [rollback] link is shown for users with roll-
  * back privileges. The rollback link restores the most recent version that
  * was not written by the target user.
  *
  * @todo This would probably look a lot nicer in a table.
  * @param $row
  * @return string
  */
 function formatRow($row)
 {
     wfProfileIn(__METHOD__);
     $ret = '';
     $classes = array();
     /*
      * There may be more than just revision rows. To make sure that we'll only be processing
      * revisions here, let's _try_ to build a revision out of our row (without displaying
      * notices though) and then trying to grab data from the built object. If we succeed,
      * we're definitely dealing with revision data and we may proceed, if not, we'll leave it
      * to extensions to subscribe to the hook to parse the row.
      */
     wfSuppressWarnings();
     $rev = new Revision($row);
     $validRevision = $rev->getParentId() !== null;
     wfRestoreWarnings();
     if ($validRevision) {
         $classes = array();
         $page = Title::newFromRow($row);
         $link = Linker::link($page, htmlspecialchars($page->getPrefixedText()), array('class' => 'mw-contributions-title'), $page->isRedirect() ? array('redirect' => 'no') : array());
         # Mark current revisions
         $topmarktext = '';
         $user = $this->getUser();
         if ($row->rev_id == $row->page_latest) {
             $topmarktext .= '<span class="mw-uctop">' . $this->messages['uctop'] . '</span>';
             # Add rollback link
             if (!$row->page_is_new && $page->quickUserCan('rollback', $user) && $page->quickUserCan('edit', $user)) {
                 $this->preventClickjacking();
                 $topmarktext .= ' ' . Linker::generateRollback($rev, $this->getContext());
             }
         }
         # Is there a visible previous revision?
         if ($rev->userCan(Revision::DELETED_TEXT, $user) && $rev->getParentId() !== 0) {
             $difftext = Linker::linkKnown($page, $this->messages['diff'], array(), array('diff' => 'prev', 'oldid' => $row->rev_id));
         } else {
             $difftext = $this->messages['diff'];
         }
         $histlink = Linker::linkKnown($page, $this->messages['hist'], array(), array('action' => 'history'));
         if ($row->rev_parent_id === null) {
             // For some reason rev_parent_id isn't populated for this row.
             // Its rumoured this is true on wikipedia for some revisions (bug 34922).
             // Next best thing is to have the total number of bytes.
             $chardiff = ' <span class="mw-changeslist-separator">. .</span> ' . Linker::formatRevisionSize($row->rev_len) . ' <span class="mw-changeslist-separator">. .</span> ';
         } else {
             $parentLen = isset($this->mParentLens[$row->rev_parent_id]) ? $this->mParentLens[$row->rev_parent_id] : 0;
             $chardiff = ' <span class="mw-changeslist-separator">. .</span> ' . ChangesList::showCharacterDifference($parentLen, $row->rev_len, $this->getContext()) . ' <span class="mw-changeslist-separator">. .</span> ';
         }
         $lang = $this->getLanguage();
         $comment = $lang->getDirMark() . Linker::revComment($rev, false, true);
         $date = $lang->userTimeAndDate($row->rev_timestamp, $user);
         if ($rev->userCan(Revision::DELETED_TEXT, $user)) {
             $d = Linker::linkKnown($page, htmlspecialchars($date), array('class' => 'mw-changeslist-date'), array('oldid' => intval($row->rev_id)));
         } else {
             $d = htmlspecialchars($date);
         }
         if ($rev->isDeleted(Revision::DELETED_TEXT)) {
             $d = '<span class="history-deleted">' . $d . '</span>';
         }
         # Show user names for /newbies as there may be different users.
         # Note that we already excluded rows with hidden user names.
         if ($this->contribs == 'newbie') {
             $userlink = ' . . ' . Linker::userLink($rev->getUser(), $rev->getUserText());
             $userlink .= ' ' . $this->msg('parentheses')->rawParams(Linker::userTalkLink($rev->getUser(), $rev->getUserText()))->escaped() . ' ';
         } else {
             $userlink = '';
         }
         if ($rev->getParentId() === 0) {
             $nflag = ChangesList::flag('newpage');
         } else {
             $nflag = '';
         }
         if ($rev->isMinor()) {
             $mflag = ChangesList::flag('minor');
         } else {
             $mflag = '';
         }
         $del = Linker::getRevDeleteLink($user, $rev, $page);
         if ($del !== '') {
             $del .= ' ';
         }
         $diffHistLinks = $this->msg('parentheses')->rawParams($difftext . $this->messages['pipe-separator'] . $histlink)->escaped();
         $ret = "{$del}{$d} {$diffHistLinks}{$chardiff}{$nflag}{$mflag} {$link}{$userlink} {$comment} {$topmarktext}";
         # Denote if username is redacted for this edit
         if ($rev->isDeleted(Revision::DELETED_USER)) {
             $ret .= " <strong>" . $this->msg('rev-deleted-user-contribs')->escaped() . "</strong>";
         }
         # Tags, if any.
         list($tagSummary, $newClasses) = ChangeTags::formatSummaryRow($row->ts_tags, 'contributions');
         $classes = array_merge($classes, $newClasses);
         $ret .= " {$tagSummary}";
     }
     // Let extensions add data
     wfRunHooks('ContributionsLineEnding', array($this, &$ret, $row, &$classes));
     $classes = implode(' ', $classes);
     $ret = "<li class=\"{$classes}\">{$ret}</li>\n";
     wfProfileOut(__METHOD__);
     return $ret;
 }