/** * Purges the cache of a page */ public function execute() { global $wgUser; $params = $this->extractRequestParams(); if (!$wgUser->isAllowed('purge')) { $this->dieUsageMsg(array('cantpurge')); } if (!isset($params['titles'])) { $this->dieUsageMsg(array('missingparam', 'titles')); } $result = array(); foreach ($params['titles'] as $t) { $r = array(); $title = Title::newFromText($t); if (!$title instanceof Title) { $r['title'] = $t; $r['invalid'] = ''; $result[] = $r; continue; } ApiQueryBase::addTitleInfo($r, $title); if (!$title->exists()) { $r['missing'] = ''; $result[] = $r; continue; } $article = new Article($title); $article->doPurge(); // Directly purge and skip the UI part of purge(). $r['purged'] = ''; $result[] = $r; } $this->getResult()->setIndexedTagName($result, 'page'); $this->getResult()->addValue(null, $this->getModuleName(), $result); }
/** * Patrols the article or provides the reason the patrol failed. */ public function execute() { global $wgUser, $wgUseRCPatrol, $wgUseNPPatrol; $this->getMain()->requestWriteMode(); $params = $this->extractRequestParams(); if (!isset($params['token'])) { $this->dieUsageMsg(array('missingparam', 'token')); } if (!isset($params['rcid'])) { $this->dieUsageMsg(array('missingparam', 'rcid')); } if (!$wgUser->matchEditToken($params['token'])) { $this->dieUsageMsg(array('sessionfailure')); } $rc = RecentChange::newFromID($params['rcid']); if (!$rc instanceof RecentChange) { $this->dieUsageMsg(array('nosuchrcid', $params['rcid'])); } $retval = RecentChange::markPatrolled($params['rcid']); if ($retval) { $this->dieUsageMsg(current($retval)); } $result = array('rcid' => $rc->getAttribute('rc_id')); ApiQueryBase::addTitleInfo($result, $rc->getTitle()); $this->getResult()->addValue(null, $this->getModuleName(), $result); }
/** * Patrols the article or provides the reason the patrol failed. */ public function execute() { $params = $this->extractRequestParams(); $this->requireOnlyOneParameter($params, 'rcid', 'revid'); if (isset($params['rcid'])) { $rc = RecentChange::newFromID($params['rcid']); if (!$rc) { $this->dieUsageMsg(array('nosuchrcid', $params['rcid'])); } } else { $rev = Revision::newFromId($params['revid']); if (!$rev) { $this->dieUsageMsg(array('nosuchrevid', $params['revid'])); } $rc = $rev->getRecentChange(); if (!$rc) { $this->dieUsage('The revision ' . $params['revid'] . " can't be patrolled as it's too old", 'notpatrollable'); } } $retval = $rc->doMarkPatrolled($this->getUser()); if ($retval) { $this->dieUsageMsg(reset($retval)); } $result = array('rcid' => intval($rc->getAttribute('rc_id'))); ApiQueryBase::addTitleInfo($result, $rc->getTitle()); $this->getResult()->addValue(null, $this->getModuleName(), $result); }
/** * Purges the cache of a page */ public function execute() { $params = $this->extractRequestParams(); $forceLinkUpdate = $params['forcelinkupdate']; $pageSet = $this->getPageSet(); $pageSet->execute(); $result = array(); self::addValues($result, $pageSet->getInvalidTitles(), 'invalid', 'title'); self::addValues($result, $pageSet->getSpecialTitles(), 'special', 'title'); self::addValues($result, $pageSet->getMissingPageIDs(), 'missing', 'pageid'); self::addValues($result, $pageSet->getMissingRevisionIDs(), 'missing', 'revid'); self::addValues($result, $pageSet->getMissingTitles(), 'missing'); self::addValues($result, $pageSet->getInterwikiTitlesAsResult()); foreach ($pageSet->getGoodTitles() as $title) { $r = array(); ApiQueryBase::addTitleInfo($r, $title); $page = WikiPage::factory($title); $page->doPurge(); // Directly purge and skip the UI part of purge(). $r['purged'] = ''; if ($forceLinkUpdate) { if (!$this->getUser()->pingLimiter()) { global $wgEnableParserCache; $popts = $page->makeParserOptions('canonical'); # Parse content; note that HTML generation is only needed if we want to cache the result. $content = $page->getContent(Revision::RAW); $p_result = $content->getParserOutput($title, $page->getLatest(), $popts, $wgEnableParserCache); # Update the links tables $updates = $content->getSecondaryDataUpdates($title, null, true, $p_result); DataUpdate::runUpdates($updates); $r['linkupdate'] = ''; if ($wgEnableParserCache) { $pcache = ParserCache::singleton(); $pcache->save($p_result, $page, $popts); } } else { $error = $this->parseMsg(array('actionthrottledtext')); $this->setWarning($error['info']); $forceLinkUpdate = false; } } $result[] = $r; } $apiResult = $this->getResult(); $apiResult->setIndexedTagName($result, 'page'); $apiResult->addValue(null, $this->getModuleName(), $result); $values = $pageSet->getNormalizedTitlesAsResult($apiResult); if ($values) { $apiResult->addValue(null, 'normalized', $values); } $values = $pageSet->getConvertedTitlesAsResult($apiResult); if ($values) { $apiResult->addValue(null, 'converted', $values); } $values = $pageSet->getRedirectTitlesAsResult($apiResult); if ($values) { $apiResult->addValue(null, 'redirects', $values); } }
/** * Purges the cache of a page */ public function execute() { global $wgUser; $params = $this->extractRequestParams(); if (!$wgUser->isAllowed('purge') && !$this->getMain()->isInternalMode() && !$this->getMain()->getRequest()->wasPosted()) { $this->dieUsageMsg(array('mustbeposted', $this->getModuleName())); } $forceLinkUpdate = $params['forcelinkupdate']; $result = array(); foreach ($params['titles'] as $t) { $r = array(); $title = Title::newFromText($t); if (!$title instanceof Title) { $r['title'] = $t; $r['invalid'] = ''; $result[] = $r; continue; } ApiQueryBase::addTitleInfo($r, $title); if (!$title->exists()) { $r['missing'] = ''; $result[] = $r; continue; } $context = $this->createContext(); $context->setTitle($title); $article = Article::newFromTitle($title, $context); $article->doPurge(); // Directly purge and skip the UI part of purge(). $r['purged'] = ''; if ($forceLinkUpdate) { if (!$wgUser->pingLimiter()) { global $wgParser, $wgEnableParserCache; $popts = new ParserOptions(); $p_result = $wgParser->parse($article->getContent(), $title, $popts); # Update the links tables $u = new LinksUpdate($title, $p_result); $u->doUpdate(); $r['linkupdate'] = ''; if ($wgEnableParserCache) { $pcache = ParserCache::singleton(); $pcache->save($p_result, $article, $popts); } } else { $this->setWarning($this->parseMsg(array('actionthrottledtext'))); $forceLinkUpdate = false; } } $result[] = $r; } $apiResult = $this->getResult(); $apiResult->setIndexedTagName($result, 'page'); $apiResult->addValue(null, $this->getModuleName(), $result); }
/** * Patrols the article or provides the reason the patrol failed. */ public function execute() { $params = $this->extractRequestParams(); $rc = RecentChange::newFromID($params['rcid']); if (!$rc instanceof RecentChange) { $this->dieUsageMsg(array('nosuchrcid', $params['rcid'])); } $retval = $rc->doMarkPatrolled($this->getUser()); if ($retval) { $this->dieUsageMsg(reset($retval)); } $result = array('rcid' => intval($rc->getAttribute('rc_id'))); ApiQueryBase::addTitleInfo($result, $rc->getTitle()); $this->getResult()->addValue(null, $this->getModuleName(), $result); }
/** * Add all items from $values into the result * @param array $result Output * @param array $values Values to add * @param string $flag The name of the boolean flag to mark this element * @param string $name If given, name of the value */ private static function addValues(array &$result, $values, $flag = null, $name = null) { foreach ($values as $val) { if ($val instanceof Title) { $v = array(); ApiQueryBase::addTitleInfo($v, $val); } elseif ($name !== null) { $v = array($name => $val); } else { $v = $val; } if ($flag !== null) { $v[$flag] = ''; } $result[] = $v; } }
function reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo = '' ) { // Add a result entry $r = array(); ApiQueryBase::addTitleInfo($r, $title); $r['revisions'] = intval($successCount); $this->mResultArr[] = $r; # call the parent to do the logging # avoid bug in 1.15.4 Special:Import (new file page text without the file uploaded) # PHP Fatal error: Call to a member function insertOn() on a non-object in E:\www\psychologos\includes\specials\SpecialImport.php on line 334 // do not create informational null revisions // because they are placed on top of real user made revisions, // making the binary search algorithm used to compare local and remote revs to fail // TODO: change the binary search algorithm to two/three level hashes if ( WikiSyncSetup::$report_null_revisions && $title->getArticleId() !== 0 ) { parent::reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo ); } }
private function run($resultPageSet = null) { if ($this->getPageSet()->getGoodTitleCount() == 0) { return; } // nothing to do $this->addFields(array('il_from', 'il_to')); $this->addTables('imagelinks'); $this->addWhereFld('il_from', array_keys($this->getPageSet()->getGoodTitles())); $this->addOption('ORDER BY', "il_from, il_to"); $db = $this->getDB(); $res = $this->select(__METHOD__); if (is_null($resultPageSet)) { $data = array(); $lastId = 0; // database has no ID 0 while ($row = $db->fetchObject($res)) { if ($lastId != $row->il_from) { if ($lastId != 0) { $this->addPageSubItems($lastId, $data); $data = array(); } $lastId = $row->il_from; } $vals = array(); ApiQueryBase::addTitleInfo($vals, Title::makeTitle(NS_IMAGE, $row->il_to)); $data[] = $vals; } if ($lastId != 0) { $this->addPageSubItems($lastId, $data); } } else { $titles = array(); while ($row = $db->fetchObject($res)) { $titles[] = Title::makeTitle(NS_IMAGE, $row->il_to); } $resultPageSet->populateFromTitles($titles); } $db->freeResult($res); }
/** * Patrols the article or provides the reason the patrol failed. */ public function execute() { $params = $this->extractRequestParams(); $this->requireOnlyOneParameter($params, 'rcid', 'revid'); if (isset($params['rcid'])) { $rc = RecentChange::newFromId($params['rcid']); if (!$rc) { $this->dieUsageMsg(['nosuchrcid', $params['rcid']]); } } else { $rev = Revision::newFromId($params['revid']); if (!$rev) { $this->dieUsageMsg(['nosuchrevid', $params['revid']]); } $rc = $rev->getRecentChange(); if (!$rc) { $this->dieUsage('The revision ' . $params['revid'] . " can't be patrolled as it's too old", 'notpatrollable'); } } $user = $this->getUser(); $tags = $params['tags']; // Check if user can add tags if (!is_null($tags)) { $ableToTag = ChangeTags::canAddTagsAccompanyingChange($tags, $user); if (!$ableToTag->isOK()) { $this->dieStatus($ableToTag); } } $retval = $rc->doMarkPatrolled($user, false, $tags); if ($retval) { $this->dieUsageMsg(reset($retval)); } $result = ['rcid' => intval($rc->getAttribute('rc_id'))]; ApiQueryBase::addTitleInfo($result, $rc->getTitle()); $this->getResult()->addValue(null, $this->getModuleName(), $result); }
/** * @param Title $title * @param Title $origTitle * @param int $revisionCount * @param int $successCount * @param array $pageInfo * @return void */ function reportPage($title, $origTitle, $revisionCount, $successCount, $pageInfo) { // Add a result entry $r = array(); if ($title === null) { # Invalid or non-importable title $r['title'] = $pageInfo['title']; $r['invalid'] = true; } else { ApiQueryBase::addTitleInfo($r, $title); $r['revisions'] = intval($successCount); } $this->mResultArr[] = $r; // Piggyback on the parent to do the logging parent::reportPage($title, $origTitle, $revisionCount, $successCount, $pageInfo); }
/** * @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'); } }
/** * @param $resultPageSet ApiPageSet * @return void */ private function run($resultPageSet = null) { $this->selectNamedDB('watchlist', DB_SLAVE, 'watchlist'); $params = $this->extractRequestParams(); $user = $this->getWatchlistUser($params); $prop = array_flip((array) $params['prop']); $show = array_flip((array) $params['show']); if (isset($show['changed']) && isset($show['!changed'])) { $this->dieUsageMsg('show'); } $this->addTables('watchlist'); $this->addFields(array('wl_namespace', 'wl_title')); $this->addFieldsIf('wl_notificationtimestamp', isset($prop['changed'])); $this->addWhereFld('wl_user', $user->getId()); $this->addWhereFld('wl_namespace', $params['namespace']); $this->addWhereIf('wl_notificationtimestamp IS NOT NULL', isset($show['changed'])); $this->addWhereIf('wl_notificationtimestamp IS NULL', isset($show['!changed'])); if (isset($params['continue'])) { $cont = explode('|', $params['continue']); $this->dieContinueUsageIf(count($cont) != 2); $ns = intval($cont[0]); $this->dieContinueUsageIf(strval($ns) !== $cont[0]); $title = $this->getDB()->addQuotes($cont[1]); $op = $params['dir'] == 'ascending' ? '>' : '<'; $this->addWhere("wl_namespace {$op} {$ns} OR " . "(wl_namespace = {$ns} AND " . "wl_title {$op}= {$title})"); } $sort = $params['dir'] == 'descending' ? ' DESC' : ''; // Don't ORDER BY wl_namespace if it's constant in the WHERE clause if (count($params['namespace']) == 1) { $this->addOption('ORDER BY', 'wl_title' . $sort); } else { $this->addOption('ORDER BY', array('wl_namespace' . $sort, 'wl_title' . $sort)); } $this->addOption('LIMIT', $params['limit'] + 1); $res = $this->select(__METHOD__); $titles = array(); $count = 0; foreach ($res as $row) { if (++$count > $params['limit']) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... $this->setContinueEnumParameter('continue', $row->wl_namespace . '|' . $row->wl_title); break; } $t = Title::makeTitle($row->wl_namespace, $row->wl_title); if (is_null($resultPageSet)) { $vals = array(); ApiQueryBase::addTitleInfo($vals, $t); if (isset($prop['changed']) && !is_null($row->wl_notificationtimestamp)) { $vals['changed'] = wfTimestamp(TS_ISO_8601, $row->wl_notificationtimestamp); } $fit = $this->getResult()->addValue($this->getModuleName(), null, $vals); if (!$fit) { $this->setContinueEnumParameter('continue', $row->wl_namespace . '|' . $row->wl_title); break; } } else { $titles[] = $t; } } if (is_null($resultPageSet)) { $this->getResult()->setIndexedTagName_internal($this->getModuleName(), 'wr'); } else { $resultPageSet->populateFromTitles($titles); } }
/** * Appends an element for each page in the current pageSet with the * most general information (id, title), plus any title normalizations * and missing or invalid title/pageids/revids. */ private function outputGeneralPageInfo() { $pageSet = $this->getPageSet(); $result = $this->getResult(); // We don't check for a full result set here because we can't be adding // more than 380K. The maximum revision size is in the megabyte range, // and the maximum result size must be even higher than that. $values = $pageSet->getNormalizedTitlesAsResult($result); if ($values) { $result->addValue('query', 'normalized', $values); } $values = $pageSet->getConvertedTitlesAsResult($result); if ($values) { $result->addValue('query', 'converted', $values); } $values = $pageSet->getInterwikiTitlesAsResult($result, $this->mParams['iwurl']); if ($values) { $result->addValue('query', 'interwiki', $values); } $values = $pageSet->getRedirectTitlesAsResult($result); if ($values) { $result->addValue('query', 'redirects', $values); } $values = $pageSet->getMissingRevisionIDsAsResult($result); if ($values) { $result->addValue('query', 'badrevids', $values); } // Page elements $pages = array(); // Report any missing titles foreach ($pageSet->getMissingTitles() as $fakeId => $title) { $vals = array(); ApiQueryBase::addTitleInfo($vals, $title); $vals['missing'] = ''; $pages[$fakeId] = $vals; } // Report any invalid titles foreach ($pageSet->getInvalidTitles() as $fakeId => $title) { $pages[$fakeId] = array('title' => $title, 'invalid' => ''); } // Report any missing page ids foreach ($pageSet->getMissingPageIDs() as $pageid) { $pages[$pageid] = array('pageid' => $pageid, 'missing' => ''); } // Report special pages /** @var $title Title */ foreach ($pageSet->getSpecialTitles() as $fakeId => $title) { $vals = array(); ApiQueryBase::addTitleInfo($vals, $title); $vals['special'] = ''; if ($title->isSpecialPage() && !SpecialPageFactory::exists($title->getDBkey())) { $vals['missing'] = ''; } elseif ($title->getNamespace() == NS_MEDIA && !wfFindFile($title)) { $vals['missing'] = ''; } $pages[$fakeId] = $vals; } // Output general page information for found titles foreach ($pageSet->getGoodTitles() as $pageid => $title) { $vals = array(); $vals['pageid'] = $pageid; ApiQueryBase::addTitleInfo($vals, $title); $pages[$pageid] = $vals; } if (count($pages)) { if ($this->mParams['indexpageids']) { $pageIDs = array_keys($pages); // json treats all map keys as strings - converting to match $pageIDs = array_map('strval', $pageIDs); $result->setIndexedTagName($pageIDs, 'id'); $result->addValue('query', 'pageids', $pageIDs); } $result->setIndexedTagName($pages, 'page'); $result->addValue('query', 'pages', $pages); } if ($this->mParams['export']) { $this->doExport($pageSet, $result); } }
/** * @param $resultPageSet ApiPageSet * @return void */ private function run($resultPageSet = null) { global $wgContLang; $params = $this->extractRequestParams(); // Extract parameters $limit = $params['limit']; $query = $params['search']; $what = $params['what']; $searchInfo = array_flip($params['info']); $prop = array_flip($params['prop']); // Create search engine instance and set options $search = isset($params['backend']) && $params['backend'] != self::BACKEND_NULL_PARAM ? SearchEngine::create($params['backend']) : SearchEngine::create(); $search->setLimitOffset($limit + 1, $params['offset']); $search->setNamespaces($params['namespace']); $search->showRedirects = $params['redirects']; $query = $search->transformSearchTerm($query); $query = $search->replacePrefixes($query); // Perform the actual search if ($what == 'text') { $matches = $search->searchText($query); } elseif ($what == 'title') { $matches = $search->searchTitle($query); } elseif ($what == 'nearmatch') { $matches = SearchEngine::getNearMatchResultSet($query); } else { // We default to title searches; this is a terrible legacy // of the way we initially set up the MySQL fulltext-based // search engine with separate title and text fields. // In the future, the default should be for a combined index. $what = 'title'; $matches = $search->searchTitle($query); // Not all search engines support a separate title search, // for instance the Lucene-based engine we use on Wikipedia. // In this case, fall back to full-text search (which will // include titles in it!) if (is_null($matches)) { $what = 'text'; $matches = $search->searchText($query); } } if (is_null($matches)) { $this->dieUsage("{$what} search is disabled", "search-{$what}-disabled"); } elseif ($matches instanceof Status && !$matches->isGood()) { $this->dieUsage($matches->getWikiText(), 'search-error'); } $apiResult = $this->getResult(); // Add search meta data to result if (isset($searchInfo['totalhits'])) { $totalhits = $matches->getTotalHits(); if ($totalhits !== null) { $apiResult->addValue(array('query', 'searchinfo'), 'totalhits', $totalhits); } } if (isset($searchInfo['suggestion']) && $matches->hasSuggestion()) { $apiResult->addValue(array('query', 'searchinfo'), 'suggestion', $matches->getSuggestionQuery()); } // Add the search results to the result $terms = $wgContLang->convertForSearchResult($matches->termMatches()); $titles = array(); $count = 0; $result = $matches->next(); while ($result) { if (++$count > $limit) { // We've reached the one extra which shows that there are // additional items to be had. Stop here... $this->setContinueEnumParameter('offset', $params['offset'] + $params['limit']); break; } // Silently skip broken and missing titles if ($result->isBrokenTitle() || $result->isMissingRevision()) { $result = $matches->next(); continue; } $title = $result->getTitle(); if (is_null($resultPageSet)) { $vals = array(); ApiQueryBase::addTitleInfo($vals, $title); if (isset($prop['snippet'])) { $vals['snippet'] = $result->getTextSnippet($terms); } if (isset($prop['size'])) { $vals['size'] = $result->getByteSize(); } if (isset($prop['wordcount'])) { $vals['wordcount'] = $result->getWordCount(); } if (isset($prop['timestamp'])) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $result->getTimestamp()); } if (!is_null($result->getScore()) && isset($prop['score'])) { $vals['score'] = $result->getScore(); } if (isset($prop['titlesnippet'])) { $vals['titlesnippet'] = $result->getTitleSnippet($terms); } if (!is_null($result->getRedirectTitle())) { if (isset($prop['redirecttitle'])) { $vals['redirecttitle'] = $result->getRedirectTitle(); } if (isset($prop['redirectsnippet'])) { $vals['redirectsnippet'] = $result->getRedirectSnippet($terms); } } if (!is_null($result->getSectionTitle())) { if (isset($prop['sectiontitle'])) { $vals['sectiontitle'] = $result->getSectionTitle()->getFragment(); } if (isset($prop['sectionsnippet'])) { $vals['sectionsnippet'] = $result->getSectionSnippet(); } } if (isset($prop['hasrelated']) && $result->hasRelated()) { $vals['hasrelated'] = ''; } // Add item to results and see whether it fits $fit = $apiResult->addValue(array('query', $this->getModuleName()), null, $vals); if (!$fit) { $this->setContinueEnumParameter('offset', $params['offset'] + $count - 1); break; } } else { $titles[] = $title; } $result = $matches->next(); } if (is_null($resultPageSet)) { $apiResult->setIndexedTagName_internal(array('query', $this->getModuleName()), 'p'); } else { $resultPageSet->populateFromTitles($titles); } }
/** * Extracts from a single sql row the data needed to describe one recent change. * * @param stdClass $row The row from which to extract the data. * @return array An array mapping strings (descriptors) to their respective string values. * @access public */ public function extractRowInfo($row) { /* Determine the title of the page that has been changed. */ $title = Title::makeTitle($row->rc_namespace, $row->rc_title); $user = $this->getUser(); /* Our output data. */ $vals = array(); $type = intval($row->rc_type); $vals['type'] = RecentChange::parseFromRCType($type); $anyHidden = false; /* Create a new entry in the result for the title. */ if ($this->fld_title || $this->fld_ids) { if ($type === RC_LOG && $row->rc_deleted & LogPage::DELETED_ACTION) { $vals['actionhidden'] = true; $anyHidden = true; } if ($type !== RC_LOG || LogEventsList::userCanBitfield($row->rc_deleted, LogPage::DELETED_ACTION, $user)) { if ($this->fld_title) { ApiQueryBase::addTitleInfo($vals, $title); } if ($this->fld_ids) { $vals['pageid'] = intval($row->rc_cur_id); $vals['revid'] = intval($row->rc_this_oldid); $vals['old_revid'] = intval($row->rc_last_oldid); } } } if ($this->fld_ids) { $vals['rcid'] = intval($row->rc_id); } /* Add user data and 'anon' flag, if user is anonymous. */ if ($this->fld_user || $this->fld_userid) { if ($row->rc_deleted & Revision::DELETED_USER) { $vals['userhidden'] = true; $anyHidden = true; } if (Revision::userCanBitfield($row->rc_deleted, Revision::DELETED_USER, $user)) { if ($this->fld_user) { $vals['user'] = $row->rc_user_text; } if ($this->fld_userid) { $vals['userid'] = $row->rc_user; } if (!$row->rc_user) { $vals['anon'] = true; } } } /* Add flags, such as new, minor, bot. */ if ($this->fld_flags) { $vals['bot'] = (bool) $row->rc_bot; $vals['new'] = $row->rc_type == RC_NEW; $vals['minor'] = (bool) $row->rc_minor; } /* Add sizes of each revision. (Only available on 1.10+) */ if ($this->fld_sizes) { $vals['oldlen'] = intval($row->rc_old_len); $vals['newlen'] = intval($row->rc_new_len); } /* Add the timestamp. */ if ($this->fld_timestamp) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->rc_timestamp); } /* Add edit summary / log summary. */ if ($this->fld_comment || $this->fld_parsedcomment) { if ($row->rc_deleted & Revision::DELETED_COMMENT) { $vals['commenthidden'] = true; $anyHidden = true; } if (Revision::userCanBitfield($row->rc_deleted, Revision::DELETED_COMMENT, $user)) { if ($this->fld_comment && isset($row->rc_comment)) { $vals['comment'] = $row->rc_comment; } if ($this->fld_parsedcomment && isset($row->rc_comment)) { $vals['parsedcomment'] = Linker::formatComment($row->rc_comment, $title); } } } if ($this->fld_redirect) { $vals['redirect'] = (bool) $row->page_is_redirect; } /* Add the patrolled flag */ if ($this->fld_patrolled) { $vals['patrolled'] = $row->rc_patrolled == 1; $vals['unpatrolled'] = ChangesList::isUnpatrolled($row, $user); } if ($this->fld_loginfo && $row->rc_type == RC_LOG) { if ($row->rc_deleted & LogPage::DELETED_ACTION) { $vals['actionhidden'] = true; $anyHidden = true; } if (LogEventsList::userCanBitfield($row->rc_deleted, LogPage::DELETED_ACTION, $user)) { $vals['logid'] = intval($row->rc_logid); $vals['logtype'] = $row->rc_log_type; $vals['logaction'] = $row->rc_log_action; $vals['logparams'] = LogFormatter::newFromRow($row)->formatParametersForApi(); } } if ($this->fld_tags) { if ($row->ts_tags) { $tags = explode(',', $row->ts_tags); ApiResult::setIndexedTagName($tags, 'tag'); $vals['tags'] = $tags; } else { $vals['tags'] = array(); } } if ($this->fld_sha1 && $row->rev_sha1 !== null) { if ($row->rev_deleted & Revision::DELETED_TEXT) { $vals['sha1hidden'] = true; $anyHidden = true; } if (Revision::userCanBitfield($row->rev_deleted, Revision::DELETED_TEXT, $user)) { if ($row->rev_sha1 !== '') { $vals['sha1'] = wfBaseConvert($row->rev_sha1, 36, 16, 40); } else { $vals['sha1'] = ''; } } } if (!is_null($this->token)) { $tokenFunctions = $this->getTokenFunctions(); foreach ($this->token as $t) { $val = call_user_func($tokenFunctions[$t], $row->rc_cur_id, $title, RecentChange::newFromRow($row)); if ($val === false) { $this->setWarning("Action '{$t}' is not allowed for the current user"); } else { $vals[$t . 'token'] = $val; } } } if ($anyHidden && $row->rc_deleted & Revision::DELETED_RESTRICTED) { $vals['suppressed'] = true; } return $vals; }
/** * @param $resultPageSet ApiPageSet * @return void */ private function run($resultPageSet = null) { $params = $this->extractRequestParams(); $this->addTables('protected_titles'); $this->addFields(array('pt_namespace', 'pt_title', 'pt_timestamp')); $prop = array_flip($params['prop']); $this->addFieldsIf('pt_user', isset($prop['user']) || isset($prop['userid'])); $this->addFieldsIf('pt_reason', isset($prop['comment']) || isset($prop['parsedcomment'])); $this->addFieldsIf('pt_expiry', isset($prop['expiry'])); $this->addFieldsIf('pt_create_perm', isset($prop['level'])); $this->addTimestampWhereRange('pt_timestamp', $params['dir'], $params['start'], $params['end']); $this->addWhereFld('pt_namespace', $params['namespace']); $this->addWhereFld('pt_create_perm', $params['level']); if (isset($prop['user'])) { $this->addTables('user'); $this->addFields('user_name'); $this->addJoinConds(array('user' => array('LEFT JOIN', 'user_id=pt_user'))); } $this->addOption('LIMIT', $params['limit'] + 1); $res = $this->select(__METHOD__); $count = 0; $result = $this->getResult(); $titles = array(); foreach ($res as $row) { if (++$count > $params['limit']) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->pt_timestamp)); break; } $title = Title::makeTitle($row->pt_namespace, $row->pt_title); if (is_null($resultPageSet)) { $vals = array(); ApiQueryBase::addTitleInfo($vals, $title); if (isset($prop['timestamp'])) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->pt_timestamp); } if (isset($prop['user']) && !is_null($row->user_name)) { $vals['user'] = $row->user_name; } if (isset($prop['user'])) { $vals['userid'] = $row->pt_user; } if (isset($prop['comment'])) { $vals['comment'] = $row->pt_reason; } if (isset($prop['parsedcomment'])) { global $wgUser; $vals['parsedcomment'] = $wgUser->getSkin()->formatComment($row->pt_reason, $title); } if (isset($prop['expiry'])) { global $wgContLang; $vals['expiry'] = $wgContLang->formatExpiry($row->pt_expiry, TS_ISO_8601); } if (isset($prop['level'])) { $vals['level'] = $row->pt_create_perm; } $fit = $result->addValue(array('query', $this->getModuleName()), null, $vals); if (!$fit) { $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->pt_timestamp)); break; } } else { $titles[] = $title; } } if (is_null($resultPageSet)) { $result->setIndexedTagName_internal(array('query', $this->getModuleName()), $this->getModulePrefix()); } else { $resultPageSet->populateFromTitles($titles); } }
public function execute() { $this->useTransactionalTimeLimit(); $params = $this->extractRequestParams(); $rotation = $params['rotation']; $continuationManager = new ApiContinuationManager($this, [], []); $this->setContinuationManager($continuationManager); $pageSet = $this->getPageSet(); $pageSet->execute(); $result = []; $result = $pageSet->getInvalidTitlesAndRevisions(['invalidTitles', 'special', 'missingIds', 'missingRevIds', 'interwikiTitles']); foreach ($pageSet->getTitles() as $title) { $r = []; $r['id'] = $title->getArticleID(); ApiQueryBase::addTitleInfo($r, $title); if (!$title->exists()) { $r['missing'] = true; if ($title->isKnown()) { $r['known'] = true; } } $file = wfFindFile($title, ['latest' => true]); if (!$file) { $r['result'] = 'Failure'; $r['errormessage'] = 'File does not exist'; $result[] = $r; continue; } $handler = $file->getHandler(); if (!$handler || !$handler->canRotate()) { $r['result'] = 'Failure'; $r['errormessage'] = 'File type cannot be rotated'; $result[] = $r; continue; } // Check whether we're allowed to rotate this file $permError = $this->checkPermissions($this->getUser(), $file->getTitle()); if ($permError !== null) { $r['result'] = 'Failure'; $r['errormessage'] = $permError; $result[] = $r; continue; } $srcPath = $file->getLocalRefPath(); if ($srcPath === false) { $r['result'] = 'Failure'; $r['errormessage'] = 'Cannot get local file path'; $result[] = $r; continue; } $ext = strtolower(pathinfo("{$srcPath}", PATHINFO_EXTENSION)); $tmpFile = TempFSFile::factory('rotate_', $ext, wfTempDir()); $dstPath = $tmpFile->getPath(); $err = $handler->rotate($file, ['srcPath' => $srcPath, 'dstPath' => $dstPath, 'rotation' => $rotation]); if (!$err) { $comment = wfMessage('rotate-comment')->numParams($rotation)->inContentLanguage()->text(); $status = $file->upload($dstPath, $comment, $comment, 0, false, false, $this->getUser()); if ($status->isGood()) { $r['result'] = 'Success'; } else { $r['result'] = 'Failure'; $r['errormessage'] = $this->getErrorFormatter()->arrayFromStatus($status); } } else { $r['result'] = 'Failure'; $r['errormessage'] = $err->toText(); } $result[] = $r; } $apiResult = $this->getResult(); ApiResult::setIndexedTagName($result, 'page'); $apiResult->addValue(null, $this->getModuleName(), $result); $this->setContinuationManager(null); $continuationManager->setContinuationIntoResult($apiResult); }
/** * @param ApiPageSet $resultPageSet * @return void */ protected function run(ApiPageSet $resultPageSet = null) { $user = $this->getUser(); // Before doing anything at all, let's check permissions if (!$user->isAllowed('deletedhistory')) { $this->dieUsage('You don\'t have permission to view deleted revision information', 'permissiondenied'); } $db = $this->getDB(); $params = $this->extractRequestParams(false); $result = $this->getResult(); // If the user wants no namespaces, they get no pages. if ($params['namespace'] === []) { if ($resultPageSet === null) { $result->addValue('query', $this->getModuleName(), []); } return; } // This module operates in two modes: // 'user': List deleted revs by a certain user // 'all': List all deleted revs in NS $mode = 'all'; if (!is_null($params['user'])) { $mode = 'user'; } if ($mode == 'user') { foreach (['from', 'to', 'prefix', 'excludeuser'] as $param) { if (!is_null($params[$param])) { $p = $this->getModulePrefix(); $this->dieUsage("The '{$p}{$param}' parameter cannot be used with '{$p}user'", 'badparams'); } } } else { foreach (['start', 'end'] as $param) { if (!is_null($params[$param])) { $p = $this->getModulePrefix(); $this->dieUsage("The '{$p}{$param}' parameter may only be used with '{$p}user'", 'badparams'); } } } // If we're generating titles only, we can use DISTINCT for a better // query. But we can't do that in 'user' mode (wrong index), and we can // only do it when sorting ASC (because MySQL apparently can't use an // index backwards for grouping even though it can for ORDER BY, WTF?) $dir = $params['dir']; $optimizeGenerateTitles = false; if ($mode === 'all' && $params['generatetitles'] && $resultPageSet !== null) { if ($dir === 'newer') { $optimizeGenerateTitles = true; } else { $p = $this->getModulePrefix(); $this->setWarning("For better performance when generating titles, set {$p}dir=newer"); } } $this->addTables('archive'); if ($resultPageSet === null) { $this->parseParameters($params); $this->addFields(Revision::selectArchiveFields()); $this->addFields(['ar_title', 'ar_namespace']); } else { $this->limit = $this->getParameter('limit') ?: 10; $this->addFields(['ar_title', 'ar_namespace']); if ($optimizeGenerateTitles) { $this->addOption('DISTINCT'); } else { $this->addFields(['ar_timestamp', 'ar_rev_id', 'ar_id']); } } if ($this->fld_tags) { $this->addTables('tag_summary'); $this->addJoinConds(['tag_summary' => ['LEFT JOIN', ['ar_rev_id=ts_rev_id']]]); $this->addFields('ts_tags'); } if (!is_null($params['tag'])) { $this->addTables('change_tag'); $this->addJoinConds(['change_tag' => ['INNER JOIN', ['ar_rev_id=ct_rev_id']]]); $this->addWhereFld('ct_tag', $params['tag']); } if ($this->fetchContent) { // Modern MediaWiki has the content for deleted revs in the 'text' // table using fields old_text and old_flags. But revisions deleted // pre-1.5 store the content in the 'archive' table directly using // fields ar_text and ar_flags, and no corresponding 'text' row. So // we have to LEFT JOIN and fetch all four fields. $this->addTables('text'); $this->addJoinConds(['text' => ['LEFT JOIN', ['ar_text_id=old_id']]]); $this->addFields(['ar_text', 'ar_flags', 'old_text', 'old_flags']); // This also means stricter restrictions if (!$user->isAllowedAny('undelete', 'deletedtext')) { $this->dieUsage('You don\'t have permission to view deleted revision content', 'permissiondenied'); } } $miser_ns = null; if ($mode == 'all') { if ($params['namespace'] !== null) { $namespaces = $params['namespace']; } else { $namespaces = MWNamespace::getValidNamespaces(); } $this->addWhereFld('ar_namespace', $namespaces); // For from/to/prefix, we have to consider the potential // transformations of the title in all specified namespaces. // Generally there will be only one transformation, but wikis with // some namespaces case-sensitive could have two. if ($params['from'] !== null || $params['to'] !== null) { $isDirNewer = $dir === 'newer'; $after = $isDirNewer ? '>=' : '<='; $before = $isDirNewer ? '<=' : '>='; $where = []; foreach ($namespaces as $ns) { $w = []; if ($params['from'] !== null) { $w[] = 'ar_title' . $after . $db->addQuotes($this->titlePartToKey($params['from'], $ns)); } if ($params['to'] !== null) { $w[] = 'ar_title' . $before . $db->addQuotes($this->titlePartToKey($params['to'], $ns)); } $w = $db->makeList($w, LIST_AND); $where[$w][] = $ns; } if (count($where) == 1) { $where = key($where); $this->addWhere($where); } else { $where2 = []; foreach ($where as $w => $ns) { $where2[] = $db->makeList([$w, 'ar_namespace' => $ns], LIST_AND); } $this->addWhere($db->makeList($where2, LIST_OR)); } } if (isset($params['prefix'])) { $where = []; foreach ($namespaces as $ns) { $w = 'ar_title' . $db->buildLike($this->titlePartToKey($params['prefix'], $ns), $db->anyString()); $where[$w][] = $ns; } if (count($where) == 1) { $where = key($where); $this->addWhere($where); } else { $where2 = []; foreach ($where as $w => $ns) { $where2[] = $db->makeList([$w, 'ar_namespace' => $ns], LIST_AND); } $this->addWhere($db->makeList($where2, LIST_OR)); } } } else { if ($this->getConfig()->get('MiserMode')) { $miser_ns = $params['namespace']; } else { $this->addWhereFld('ar_namespace', $params['namespace']); } $this->addTimestampWhereRange('ar_timestamp', $dir, $params['start'], $params['end']); } if (!is_null($params['user'])) { $this->addWhereFld('ar_user_text', $params['user']); } elseif (!is_null($params['excludeuser'])) { $this->addWhere('ar_user_text != ' . $db->addQuotes($params['excludeuser'])); } if (!is_null($params['user']) || !is_null($params['excludeuser'])) { // Paranoia: avoid brute force searches (bug 17342) // (shouldn't be able to get here without 'deletedhistory', but // check it again just in case) if (!$user->isAllowed('deletedhistory')) { $bitmask = Revision::DELETED_USER; } elseif (!$user->isAllowedAny('suppressrevision', 'viewsuppressed')) { $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED; } else { $bitmask = 0; } if ($bitmask) { $this->addWhere($db->bitAnd('ar_deleted', $bitmask) . " != {$bitmask}"); } } if (!is_null($params['continue'])) { $cont = explode('|', $params['continue']); $op = $dir == 'newer' ? '>' : '<'; if ($optimizeGenerateTitles) { $this->dieContinueUsageIf(count($cont) != 2); $ns = intval($cont[0]); $this->dieContinueUsageIf(strval($ns) !== $cont[0]); $title = $db->addQuotes($cont[1]); $this->addWhere("ar_namespace {$op} {$ns} OR " . "(ar_namespace = {$ns} AND ar_title {$op}= {$title})"); } elseif ($mode == 'all') { $this->dieContinueUsageIf(count($cont) != 4); $ns = intval($cont[0]); $this->dieContinueUsageIf(strval($ns) !== $cont[0]); $title = $db->addQuotes($cont[1]); $ts = $db->addQuotes($db->timestamp($cont[2])); $ar_id = (int) $cont[3]; $this->dieContinueUsageIf(strval($ar_id) !== $cont[3]); $this->addWhere("ar_namespace {$op} {$ns} OR " . "(ar_namespace = {$ns} AND " . "(ar_title {$op} {$title} OR " . "(ar_title = {$title} AND " . "(ar_timestamp {$op} {$ts} OR " . "(ar_timestamp = {$ts} AND " . "ar_id {$op}= {$ar_id})))))"); } else { $this->dieContinueUsageIf(count($cont) != 2); $ts = $db->addQuotes($db->timestamp($cont[0])); $ar_id = (int) $cont[1]; $this->dieContinueUsageIf(strval($ar_id) !== $cont[1]); $this->addWhere("ar_timestamp {$op} {$ts} OR " . "(ar_timestamp = {$ts} AND " . "ar_id {$op}= {$ar_id})"); } } $this->addOption('LIMIT', $this->limit + 1); $sort = $dir == 'newer' ? '' : ' DESC'; $orderby = []; if ($optimizeGenerateTitles) { // Targeting index name_title_timestamp if ($params['namespace'] === null || count(array_unique($params['namespace'])) > 1) { $orderby[] = "ar_namespace {$sort}"; } $orderby[] = "ar_title {$sort}"; } elseif ($mode == 'all') { // Targeting index name_title_timestamp if ($params['namespace'] === null || count(array_unique($params['namespace'])) > 1) { $orderby[] = "ar_namespace {$sort}"; } $orderby[] = "ar_title {$sort}"; $orderby[] = "ar_timestamp {$sort}"; $orderby[] = "ar_id {$sort}"; } else { // Targeting index usertext_timestamp // 'user' is always constant. $orderby[] = "ar_timestamp {$sort}"; $orderby[] = "ar_id {$sort}"; } $this->addOption('ORDER BY', $orderby); $res = $this->select(__METHOD__); $pageMap = []; // Maps ns&title to array index $count = 0; $nextIndex = 0; $generated = []; foreach ($res as $row) { if (++$count > $this->limit) { // We've had enough if ($optimizeGenerateTitles) { $this->setContinueEnumParameter('continue', "{$row->ar_namespace}|{$row->ar_title}"); } elseif ($mode == 'all') { $this->setContinueEnumParameter('continue', "{$row->ar_namespace}|{$row->ar_title}|{$row->ar_timestamp}|{$row->ar_id}"); } else { $this->setContinueEnumParameter('continue', "{$row->ar_timestamp}|{$row->ar_id}"); } break; } // Miser mode namespace check if ($miser_ns !== null && !in_array($row->ar_namespace, $miser_ns)) { continue; } if ($resultPageSet !== null) { if ($params['generatetitles']) { $key = "{$row->ar_namespace}:{$row->ar_title}"; if (!isset($generated[$key])) { $generated[$key] = Title::makeTitle($row->ar_namespace, $row->ar_title); } } else { $generated[] = $row->ar_rev_id; } } else { $revision = Revision::newFromArchiveRow($row); $rev = $this->extractRevisionInfo($revision, $row); if (!isset($pageMap[$row->ar_namespace][$row->ar_title])) { $index = $nextIndex++; $pageMap[$row->ar_namespace][$row->ar_title] = $index; $title = $revision->getTitle(); $a = ['pageid' => $title->getArticleID(), 'revisions' => [$rev]]; ApiResult::setIndexedTagName($a['revisions'], 'rev'); ApiQueryBase::addTitleInfo($a, $title); $fit = $result->addValue(['query', $this->getModuleName()], $index, $a); } else { $index = $pageMap[$row->ar_namespace][$row->ar_title]; $fit = $result->addValue(['query', $this->getModuleName(), $index, 'revisions'], null, $rev); } if (!$fit) { if ($mode == 'all') { $this->setContinueEnumParameter('continue', "{$row->ar_namespace}|{$row->ar_title}|{$row->ar_timestamp}|{$row->ar_id}"); } else { $this->setContinueEnumParameter('continue', "{$row->ar_timestamp}|{$row->ar_id}"); } break; } } } if ($resultPageSet !== null) { if ($params['generatetitles']) { $resultPageSet->populateFromTitles($generated); } else { $resultPageSet->populateFromRevisionIDs($generated); } } else { $result->addIndexedTagName(['query', $this->getModuleName()], 'page'); } }
private function run($resultPageSet = null) { if ($this->getPageSet()->getGoodTitleCount() == 0) { return; } // nothing to do $params = $this->extractRequestParams(); $prop = $params['prop']; $this->addFields(array('cl_from', 'cl_to')); $fld_sortkey = false; if (!is_null($prop)) { foreach ($prop as $p) { switch ($p) { case 'sortkey': $this->addFields('cl_sortkey'); $fld_sortkey = true; break; default: ApiBase::dieDebug(__METHOD__, "Unknown prop={$p}"); } } } $this->addTables('categorylinks'); $this->addWhereFld('cl_from', array_keys($this->getPageSet()->getGoodTitles())); $this->addOption('ORDER BY', "cl_from, cl_to"); $db = $this->getDB(); $res = $this->select(__METHOD__); if (is_null($resultPageSet)) { $data = array(); $lastId = 0; // database has no ID 0 while ($row = $db->fetchObject($res)) { if ($lastId != $row->cl_from) { if ($lastId != 0) { $this->addPageSubItems($lastId, $data); $data = array(); } $lastId = $row->cl_from; } $title = Title::makeTitle(NS_CATEGORY, $row->cl_to); $vals = array(); ApiQueryBase::addTitleInfo($vals, $title); if ($fld_sortkey) { $vals['sortkey'] = $row->cl_sortkey; } $data[] = $vals; } if ($lastId != 0) { $this->addPageSubItems($lastId, $data); } } else { $titles = array(); while ($row = $db->fetchObject($res)) { $titles[] = Title::makeTitle(NS_CATEGORY, $row->cl_to); } $resultPageSet->populateFromTitles($titles); } $db->freeResult($res); }
/** * @param ApiPageSet $resultPageSet */ private function run(ApiPageSet $resultPageSet = null) { $settings = self::$settings[$this->getModuleName()]; $db = $this->getDB(); $params = $this->extractRequestParams(); $prop = array_flip($params['prop']); $emptyString = $db->addQuotes(''); $pageSet = $this->getPageSet(); $titles = $pageSet->getGoodTitles() + $pageSet->getMissingTitles(); $map = $pageSet->getAllTitlesByNamespace(); // Determine our fields to query on $p = $settings['prefix']; $hasNS = !isset($settings['to_namespace']); if ($hasNS) { $bl_namespace = "{$p}_namespace"; $bl_title = "{$p}_title"; } else { $bl_namespace = $settings['to_namespace']; $bl_title = "{$p}_to"; $titles = array_filter($titles, function ($t) use($bl_namespace) { return $t->getNamespace() === $bl_namespace; }); $map = array_intersect_key($map, array($bl_namespace => true)); } $bl_from = "{$p}_from"; if (!$titles) { return; // nothing to do } // Figure out what we're sorting by, and add associated WHERE clauses. // MySQL's query planner screws up if we include a field in ORDER BY // when it's constant in WHERE, so we have to test that for each field. $sortby = array(); if ($hasNS && count($map) > 1) { $sortby[$bl_namespace] = 'ns'; } $theTitle = null; foreach ($map as $nsTitles) { reset($nsTitles); $key = key($nsTitles); if ($theTitle === null) { $theTitle = $key; } if (count($nsTitles) > 1 || $key !== $theTitle) { $sortby[$bl_title] = 'title'; break; } } $miser_ns = null; if ($params['namespace'] !== null) { if (empty($settings['from_namespace']) && $this->getConfig()->get('MiserMode')) { $miser_ns = $params['namespace']; } else { $this->addWhereFld("{$p}_from_namespace", $params['namespace']); if (!empty($settings['from_namespace']) && count($params['namespace']) > 1) { $sortby["{$p}_from_namespace"] = 'int'; } } } $sortby[$bl_from] = 'int'; // Now use the $sortby to figure out the continuation if (!is_null($params['continue'])) { $cont = explode('|', $params['continue']); $this->dieContinueUsageIf(count($cont) != count($sortby)); $where = ''; $i = count($sortby) - 1; $cont_ns = 0; $cont_title = ''; foreach (array_reverse($sortby, true) as $field => $type) { $v = $cont[$i]; switch ($type) { case 'ns': $cont_ns = (int) $v; /* fall through */ /* fall through */ case 'int': $v = (int) $v; $this->dieContinueUsageIf($v != $cont[$i]); break; case 'title': $cont_title = $v; /* fall through */ /* fall through */ default: $v = $db->addQuotes($v); break; } if ($where === '') { $where = "{$field} >= {$v}"; } else { $where = "{$field} > {$v} OR ({$field} = {$v} AND ({$where}))"; } $i--; } $this->addWhere($where); } // Populate the rest of the query $this->addTables(array($settings['linktable'], 'page')); $this->addWhere("{$bl_from} = page_id"); if ($this->getModuleName() === 'redirects') { $this->addWhere("rd_interwiki = {$emptyString} OR rd_interwiki IS NULL"); } $this->addFields(array_keys($sortby)); $this->addFields(array('bl_namespace' => $bl_namespace, 'bl_title' => $bl_title)); if (is_null($resultPageSet)) { $fld_pageid = isset($prop['pageid']); $fld_title = isset($prop['title']); $fld_redirect = isset($prop['redirect']); $this->addFieldsIf('page_id', $fld_pageid); $this->addFieldsIf(array('page_title', 'page_namespace'), $fld_title); $this->addFieldsIf('page_is_redirect', $fld_redirect); // prop=redirects $fld_fragment = isset($prop['fragment']); $this->addFieldsIf('rd_fragment', $fld_fragment); } else { $this->addFields($resultPageSet->getPageTableFields()); } $this->addFieldsIf('page_namespace', $miser_ns !== null); if ($hasNS) { $lb = new LinkBatch($titles); $this->addWhere($lb->constructSet($p, $db)); } else { $where = array(); foreach ($titles as $t) { if ($t->getNamespace() == $bl_namespace) { $where[] = "{$bl_title} = " . $db->addQuotes($t->getDBkey()); } } $this->addWhere($db->makeList($where, LIST_OR)); } if ($params['show'] !== null) { // prop=redirects only $show = array_flip($params['show']); if (isset($show['fragment']) && isset($show['!fragment']) || isset($show['redirect']) && isset($show['!redirect'])) { $this->dieUsageMsg('show'); } $this->addWhereIf("rd_fragment != {$emptyString}", isset($show['fragment'])); $this->addWhereIf("rd_fragment = {$emptyString} OR rd_fragment IS NULL", isset($show['!fragment'])); $this->addWhereIf(array('page_is_redirect' => 1), isset($show['redirect'])); $this->addWhereIf(array('page_is_redirect' => 0), isset($show['!redirect'])); } // Override any ORDER BY from above with what we calculated earlier. $this->addOption('ORDER BY', array_keys($sortby)); $this->addOption('LIMIT', $params['limit'] + 1); $res = $this->select(__METHOD__); if (is_null($resultPageSet)) { $count = 0; foreach ($res as $row) { if (++$count > $params['limit']) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... $this->setContinue($row, $sortby); break; } if ($miser_ns !== null && !in_array($row->page_namespace, $miser_ns)) { // Miser mode namespace check continue; } // Get the ID of the current page $id = $map[$row->bl_namespace][$row->bl_title]; $vals = array(); if ($fld_pageid) { $vals['pageid'] = $row->page_id; } if ($fld_title) { ApiQueryBase::addTitleInfo($vals, Title::makeTitle($row->page_namespace, $row->page_title)); } if ($fld_fragment && $row->rd_fragment !== null && $row->rd_fragment !== '') { $vals['fragment'] = $row->rd_fragment; } if ($fld_redirect && $row->page_is_redirect) { $vals['redirect'] = ''; } $fit = $this->addPageSubItem($id, $vals); if (!$fit) { $this->setContinue($row, $sortby); break; } } } else { $titles = array(); $count = 0; foreach ($res as $row) { if (++$count > $params['limit']) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... $this->setContinue($row, $sortby); break; } $titles[] = Title::makeTitle($row->page_namespace, $row->page_title); } $resultPageSet->populateFromTitles($titles); } }
private function extractRowInfo($row) { $vals = array(); if ($this->fld_ids) { $vals['logid'] = intval($row->log_id); $vals['pageid'] = intval($row->page_id); } if ($this->fld_title || $this->fld_parsedcomment) { $title = Title::makeTitle($row->log_namespace, $row->log_title); } if ($this->fld_title) { if (LogEventsList::isDeleted($row, LogPage::DELETED_ACTION)) { $vals['actionhidden'] = ''; } else { ApiQueryBase::addTitleInfo($vals, $title); } } if ($this->fld_type || $this->fld_action) { $vals['type'] = $row->log_type; $vals['action'] = $row->log_action; } if ($this->fld_details && $row->log_params !== '') { if (LogEventsList::isDeleted($row, LogPage::DELETED_ACTION)) { $vals['actionhidden'] = ''; } else { self::addLogParams($this->getResult(), $vals, $row->log_params, $row->log_type, $row->log_action, $row->log_timestamp); } } if ($this->fld_user || $this->fld_userid) { if (LogEventsList::isDeleted($row, LogPage::DELETED_USER)) { $vals['userhidden'] = ''; } else { if ($this->fld_user) { $vals['user'] = $row->user_name; } if ($this->fld_userid) { $vals['userid'] = $row->user_id; } if (!$row->log_user) { $vals['anon'] = ''; } } } if ($this->fld_timestamp) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->log_timestamp); } if (($this->fld_comment || $this->fld_parsedcomment) && isset($row->log_comment)) { if (LogEventsList::isDeleted($row, LogPage::DELETED_COMMENT)) { $vals['commenthidden'] = ''; } else { if ($this->fld_comment) { $vals['comment'] = $row->log_comment; } if ($this->fld_parsedcomment) { global $wgUser; $vals['parsedcomment'] = $wgUser->getSkin()->formatComment($row->log_comment, $title); } } } if ($this->fld_tags) { if ($row->ts_tags) { $tags = explode(',', $row->ts_tags); $this->getResult()->setIndexedTagName($tags, 'tag'); $vals['tags'] = $tags; } else { $vals['tags'] = array(); } } return $vals; }
private function extractRowInfo($row) { $vals = array(); $type = intval($row->rc_type); /* Determine what kind of change this was. */ switch ($type) { case RC_EDIT: $vals['type'] = 'edit'; break; case RC_NEW: $vals['type'] = 'new'; break; case RC_MOVE: $vals['type'] = 'move'; break; case RC_LOG: $vals['type'] = 'log'; break; case RC_EXTERNAL: $vals['type'] = 'external'; break; case RC_MOVE_OVER_REDIRECT: $vals['type'] = 'move over redirect'; break; default: $vals['type'] = $type; } if ($this->fld_ids) { $vals['pageid'] = intval($row->rc_cur_id); $vals['revid'] = intval($row->rc_this_oldid); $vals['old_revid'] = intval($row->rc_last_oldid); } $title = Title::makeTitle($row->rc_namespace, $row->rc_title); if ($this->fld_title) { ApiQueryBase::addTitleInfo($vals, $title); } if ($this->fld_user || $this->fld_userid) { if ($this->fld_userid) { $vals['userid'] = $row->rc_user; // for backwards compatibility $vals['user'] = $row->rc_user; } if ($this->fld_user) { $vals['user'] = $row->rc_user_text; } if (!$row->rc_user) { $vals['anon'] = ''; } } if ($this->fld_flags) { if ($row->rc_type == RC_NEW) { $vals['new'] = ''; } if ($row->rc_minor) { $vals['minor'] = ''; } if ($row->rc_bot) { $vals['bot'] = ''; } } if ($this->fld_patrol && isset($row->rc_patrolled)) { $vals['patrolled'] = ''; } if ($this->fld_timestamp) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->rc_timestamp); } if ($this->fld_sizes) { $vals['oldlen'] = intval($row->rc_old_len); $vals['newlen'] = intval($row->rc_new_len); } if ($this->fld_notificationtimestamp) { $vals['notificationtimestamp'] = $row->wl_notificationtimestamp == null ? '' : wfTimestamp(TS_ISO_8601, $row->wl_notificationtimestamp); } if ($this->fld_comment && isset($row->rc_comment)) { $vals['comment'] = $row->rc_comment; } if ($this->fld_parsedcomment && isset($row->rc_comment)) { $vals['parsedcomment'] = Linker::formatComment($row->rc_comment, $title); } if ($this->fld_loginfo && $row->rc_type == RC_LOG) { $vals['logid'] = intval($row->rc_logid); $vals['logtype'] = $row->rc_log_type; $vals['logaction'] = $row->rc_log_action; $logEntry = DatabaseLogEntry::newFromRow((array) $row); ApiQueryLogEvents::addLogParams($this->getResult(), $vals, $logEntry->getParameters(), $logEntry->getType(), $logEntry->getSubtype(), $logEntry->getTimestamp()); } return $vals; }
/** * Extracts from a single sql row the data needed to describe one recent change. * * @param $row The row from which to extract the data. * @return An array mapping strings (descriptors) to their respective string values. * @access public */ public function extractRowInfo($row) { /* If page was moved somewhere, get the title of the move target. */ $movedToTitle = false; if (isset($row->rc_moved_to_title) && $row->rc_moved_to_title !== '') { $movedToTitle = Title::makeTitle($row->rc_moved_to_ns, $row->rc_moved_to_title); } /* Determine the title of the page that has been changed. */ $title = Title::makeTitle($row->rc_namespace, $row->rc_title); /* Our output data. */ $vals = array(); $type = intval($row->rc_type); /* Determine what kind of change this was. */ switch ($type) { case RC_EDIT: $vals['type'] = 'edit'; break; case RC_NEW: $vals['type'] = 'new'; break; case RC_MOVE: $vals['type'] = 'move'; break; case RC_LOG: $vals['type'] = 'log'; break; case RC_MOVE_OVER_REDIRECT: $vals['type'] = 'move over redirect'; break; default: $vals['type'] = $type; } /* Create a new entry in the result for the title. */ if ($this->fld_title) { ApiQueryBase::addTitleInfo($vals, $title); if ($movedToTitle) { ApiQueryBase::addTitleInfo($vals, $movedToTitle, 'new_'); } } /* Add ids, such as rcid, pageid, revid, and oldid to the change's info. */ if ($this->fld_ids) { $vals['rcid'] = intval($row->rc_id); $vals['pageid'] = intval($row->rc_cur_id); $vals['revid'] = intval($row->rc_this_oldid); $vals['old_revid'] = intval($row->rc_last_oldid); } /* Add user data and 'anon' flag, if use is anonymous. */ if ($this->fld_user || $this->fld_userid) { if ($this->fld_user) { $vals['user'] = $row->rc_user_text; } if ($this->fld_userid) { $vals['userid'] = $row->rc_user; } if (!$row->rc_user) { $vals['anon'] = ''; } } /* Add flags, such as new, minor, bot. */ if ($this->fld_flags) { if ($row->rc_bot) { $vals['bot'] = ''; } if ($row->rc_new) { $vals['new'] = ''; } if ($row->rc_minor) { $vals['minor'] = ''; } } /* Add sizes of each revision. (Only available on 1.10+) */ if ($this->fld_sizes) { $vals['oldlen'] = intval($row->rc_old_len); $vals['newlen'] = intval($row->rc_new_len); } /* Add the timestamp. */ if ($this->fld_timestamp) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->rc_timestamp); } /* Add edit summary / log summary. */ if ($this->fld_comment && isset($row->rc_comment)) { $vals['comment'] = $row->rc_comment; } if ($this->fld_parsedcomment && isset($row->rc_comment)) { $vals['parsedcomment'] = Linker::formatComment($row->rc_comment, $title); } if ($this->fld_redirect) { if ($row->page_is_redirect) { $vals['redirect'] = ''; } } /* Add the patrolled flag */ if ($this->fld_patrolled && $row->rc_patrolled == 1) { $vals['patrolled'] = ''; } if ($this->fld_loginfo && $row->rc_type == RC_LOG) { $vals['logid'] = intval($row->rc_logid); $vals['logtype'] = $row->rc_log_type; $vals['logaction'] = $row->rc_log_action; ApiQueryLogEvents::addLogParams($this->getResult(), $vals, $row->rc_params, $row->rc_log_action, $row->rc_log_type, $row->rc_timestamp); } if ($this->fld_tags) { if ($row->ts_tags) { $tags = explode(',', $row->ts_tags); $this->getResult()->setIndexedTagName($tags, 'tag'); $vals['tags'] = $tags; } else { $vals['tags'] = array(); } } if (!is_null($this->token)) { $tokenFunctions = $this->getTokenFunctions(); foreach ($this->token as $t) { $val = call_user_func($tokenFunctions[$t], $row->rc_cur_id, $title, RecentChange::newFromRow($row)); if ($val === false) { $this->setWarning("Action '{$t}' is not allowed for the current user"); } else { $vals[$t . 'token'] = $val; } } } if ($this->fld_wikiamode) { $vals['rc_params'] = $row->rc_params; } return $vals; }
private function run($resultPageSet = null) { if ($this->getPageSet()->getGoodTitleCount() == 0) { return; } // nothing to do $params = $this->extractRequestParams(); $prop = array_flip((array) $params['prop']); $show = array_flip((array) $params['show']); $this->addFields(array('cl_from', 'cl_to')); $this->addFieldsIf('cl_sortkey', isset($prop['sortkey'])); $this->addFieldsIf('cl_timestamp', isset($prop['timestamp'])); $this->addTables('categorylinks'); $this->addWhereFld('cl_from', array_keys($this->getPageSet()->getGoodTitles())); if (!is_null($params['categories'])) { $cats = array(); foreach ($params['categories'] as $cat) { $title = Title::newFromText($cat); if (!$title || $title->getNamespace() != NS_CATEGORY) { $this->setWarning("``{$cat}'' is not a category"); } else { $cats[] = $title->getDBkey(); } } $this->addWhereFld('cl_to', $cats); } 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"); } $clfrom = intval($cont[0]); $clto = $this->getDB()->strencode($this->titleToKey($cont[1])); $this->addWhere("cl_from > {$clfrom} OR " . "(cl_from = {$clfrom} AND " . "cl_to >= '{$clto}')"); } if (isset($show['hidden']) && isset($show['!hidden'])) { $this->dieUsageMsg(array('show')); } if (isset($show['hidden']) || isset($show['!hidden']) || isset($prop['hidden'])) { $this->addOption('STRAIGHT_JOIN'); $this->addTables(array('page', 'page_props')); $this->addFieldsIf('pp_propname', isset($prop['hidden'])); $this->addJoinConds(array('page' => array('LEFT JOIN', array('page_namespace' => NS_CATEGORY, 'page_title = cl_to')), 'page_props' => array('LEFT JOIN', array('pp_page=page_id', 'pp_propname' => 'hiddencat')))); if (isset($show['hidden'])) { $this->addWhere(array('pp_propname IS NOT NULL')); } else { if (isset($show['!hidden'])) { $this->addWhere(array('pp_propname IS NULL')); } } } $this->addOption('USE INDEX', array('categorylinks' => 'cl_from')); // Don't order by cl_from if it's constant in the WHERE clause if (count($this->getPageSet()->getGoodTitles()) == 1) { $this->addOption('ORDER BY', 'cl_to'); } else { $this->addOption('ORDER BY', "cl_from, cl_to"); } $db = $this->getDB(); $res = $this->select(__METHOD__); if (is_null($resultPageSet)) { $count = 0; while ($row = $db->fetchObject($res)) { if (++$count > $params['limit']) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... $this->setContinueEnumParameter('continue', $row->cl_from . '|' . $this->keyToTitle($row->cl_to)); break; } $title = Title::makeTitle(NS_CATEGORY, $row->cl_to); $vals = array(); ApiQueryBase::addTitleInfo($vals, $title); if (isset($prop['sortkey'])) { $vals['sortkey'] = $row->cl_sortkey; } if (isset($prop['timestamp'])) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->cl_timestamp); } if (isset($prop['hidden']) && !is_null($row->pp_propname)) { $vals['hidden'] = ''; } $fit = $this->addPageSubItem($row->cl_from, $vals); if (!$fit) { $this->setContinueEnumParameter('continue', $row->cl_from . '|' . $this->keyToTitle($row->cl_to)); break; } } } else { $titles = array(); while ($row = $db->fetchObject($res)) { if (++$count > $params['limit']) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... $this->setContinueEnumParameter('continue', $row->cl_from . '|' . $this->keyToTitle($row->cl_to)); break; } $titles[] = Title::makeTitle(NS_CATEGORY, $row->cl_to); } $resultPageSet->populateFromTitles($titles); } $db->freeResult($res); }
/** * Format a single parameter value for API output * * @since 1.25 * @param string $name * @param string $type * @param string $value * @return array */ protected function formatParameterValueForApi($name, $type, $value) { $type = strtolower(trim($type)); switch ($type) { case 'bool': $value = (bool) $value; break; case 'number': if (ctype_digit($value) || is_int($value)) { $value = (int) $value; } else { $value = (double) $value; } break; case 'array': case 'assoc': case 'kvp': if (is_array($value)) { ApiResult::setArrayType($value, $type); } break; case 'timestamp': $value = wfTimestamp(TS_ISO_8601, $value); break; case 'msg': case 'msg-content': $msg = $this->msg($value); if ($type === 'msg-content') { $msg->inContentLanguage(); } $value = array(); $value["{$name}_key"] = $msg->getKey(); if ($msg->getParams()) { $value["{$name}_params"] = $msg->getParams(); } $value["{$name}_text"] = $msg->text(); return $value; case 'title': case 'title-link': $title = Title::newFromText($value); if ($title) { $value = array(); ApiQueryBase::addTitleInfo($value, $title, "{$name}_"); } return $value; case 'user': case 'user-link': $user = User::newFromName($value); if ($user) { $value = $user->getName(); } break; default: // do nothing break; } return array($name => $value); }
/** * @param ApiPageSet $resultPageSet */ private function run($resultPageSet = null) { if ($this->getPageSet()->getGoodTitleCount() == 0) { return; // nothing to do } $params = $this->extractRequestParams(); $prop = array_flip((array) $params['prop']); $show = array_flip((array) $params['show']); $this->addFields(array('cl_from', 'cl_to')); $this->addFieldsIf(array('cl_sortkey', 'cl_sortkey_prefix'), isset($prop['sortkey'])); $this->addFieldsIf('cl_timestamp', isset($prop['timestamp'])); $this->addTables('categorylinks'); $this->addWhereFld('cl_from', array_keys($this->getPageSet()->getGoodTitles())); if (!is_null($params['categories'])) { $cats = array(); foreach ($params['categories'] as $cat) { $title = Title::newFromText($cat); if (!$title || $title->getNamespace() != NS_CATEGORY) { $this->setWarning("\"{$cat}\" is not a category"); } else { $cats[] = $title->getDBkey(); } } $this->addWhereFld('cl_to', $cats); } if (!is_null($params['continue'])) { $cont = explode('|', $params['continue']); $this->dieContinueUsageIf(count($cont) != 2); $op = $params['dir'] == 'descending' ? '<' : '>'; $clfrom = intval($cont[0]); $clto = $this->getDB()->addQuotes($cont[1]); $this->addWhere("cl_from {$op} {$clfrom} OR " . "(cl_from = {$clfrom} AND " . "cl_to {$op}= {$clto})"); } if (isset($show['hidden']) && isset($show['!hidden'])) { $this->dieUsageMsg('show'); } if (isset($show['hidden']) || isset($show['!hidden']) || isset($prop['hidden'])) { $this->addOption('STRAIGHT_JOIN'); $this->addTables(array('page', 'page_props')); $this->addFieldsIf('pp_propname', isset($prop['hidden'])); $this->addJoinConds(array('page' => array('LEFT JOIN', array('page_namespace' => NS_CATEGORY, 'page_title = cl_to')), 'page_props' => array('LEFT JOIN', array('pp_page=page_id', 'pp_propname' => 'hiddencat')))); if (isset($show['hidden'])) { $this->addWhere(array('pp_propname IS NOT NULL')); } elseif (isset($show['!hidden'])) { $this->addWhere(array('pp_propname IS NULL')); } } $sort = $params['dir'] == 'descending' ? ' DESC' : ''; // Don't order by cl_from if it's constant in the WHERE clause if (count($this->getPageSet()->getGoodTitles()) == 1) { $this->addOption('ORDER BY', 'cl_to' . $sort); } else { $this->addOption('ORDER BY', array('cl_from' . $sort, 'cl_to' . $sort)); } $res = $this->select(__METHOD__); $count = 0; if (is_null($resultPageSet)) { foreach ($res as $row) { if (++$count > $params['limit']) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... $this->setContinueEnumParameter('continue', $row->cl_from . '|' . $row->cl_to); break; } $title = Title::makeTitle(NS_CATEGORY, $row->cl_to); $vals = array(); ApiQueryBase::addTitleInfo($vals, $title); if (isset($prop['sortkey'])) { $vals['sortkey'] = bin2hex($row->cl_sortkey); $vals['sortkeyprefix'] = $row->cl_sortkey_prefix; } if (isset($prop['timestamp'])) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->cl_timestamp); } if (isset($prop['hidden'])) { $vals['hidden'] = !is_null($row->pp_propname); } $fit = $this->addPageSubItem($row->cl_from, $vals); if (!$fit) { $this->setContinueEnumParameter('continue', $row->cl_from . '|' . $row->cl_to); break; } } } else { $titles = array(); foreach ($res as $row) { if (++$count > $params['limit']) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... $this->setContinueEnumParameter('continue', $row->cl_from . '|' . $row->cl_to); break; } $titles[] = Title::makeTitle(NS_CATEGORY, $row->cl_to); } $resultPageSet->populateFromTitles($titles); } }
/** * @param ApiPageSet $resultPageSet * @return void */ private function run($resultPageSet = null) { global $wgContLang; $params = $this->extractRequestParams(); // Extract parameters $limit = $params['limit']; $query = $params['search']; $what = $params['what']; $interwiki = $params['interwiki']; $searchInfo = array_flip($params['info']); $prop = array_flip($params['prop']); // Deprecated parameters if (isset($prop['hasrelated'])) { $this->logFeatureUsage('action=search&srprop=hasrelated'); $this->setWarning('srprop=hasrelated has been deprecated'); } if (isset($prop['score'])) { $this->logFeatureUsage('action=search&srprop=score'); $this->setWarning('srprop=score has been deprecated'); } // Create search engine instance and set options $search = isset($params['backend']) && $params['backend'] != self::BACKEND_NULL_PARAM ? SearchEngine::create($params['backend']) : SearchEngine::create(); $search->setLimitOffset($limit + 1, $params['offset']); $search->setNamespaces($params['namespace']); $search->setFeatureData('rewrite', (bool) $params['enablerewrites']); $query = $search->transformSearchTerm($query); $query = $search->replacePrefixes($query); // Perform the actual search if ($what == 'text') { $matches = $search->searchText($query); } elseif ($what == 'title') { $matches = $search->searchTitle($query); } elseif ($what == 'nearmatch') { // near matches must receive the user input as provided, otherwise // the near matches within namespaces are lost. $matches = SearchEngine::getNearMatchResultSet($params['search']); } else { // We default to title searches; this is a terrible legacy // of the way we initially set up the MySQL fulltext-based // search engine with separate title and text fields. // In the future, the default should be for a combined index. $what = 'title'; $matches = $search->searchTitle($query); // Not all search engines support a separate title search, // for instance the Lucene-based engine we use on Wikipedia. // In this case, fall back to full-text search (which will // include titles in it!) if (is_null($matches)) { $what = 'text'; $matches = $search->searchText($query); } } if (is_null($matches)) { $this->dieUsage("{$what} search is disabled", "search-{$what}-disabled"); } elseif ($matches instanceof Status && !$matches->isGood()) { $this->dieUsage($matches->getWikiText(), 'search-error'); } if ($resultPageSet === null) { $apiResult = $this->getResult(); // Add search meta data to result if (isset($searchInfo['totalhits'])) { $totalhits = $matches->getTotalHits(); if ($totalhits !== null) { $apiResult->addValue(array('query', 'searchinfo'), 'totalhits', $totalhits); } } if (isset($searchInfo['suggestion']) && $matches->hasSuggestion()) { $apiResult->addValue(array('query', 'searchinfo'), 'suggestion', $matches->getSuggestionQuery()); $apiResult->addValue(array('query', 'searchinfo'), 'suggestionsnippet', $matches->getSuggestionSnippet()); } if (isset($searchInfo['rewrittenquery']) && $matches->hasRewrittenQuery()) { $apiResult->addValue(array('query', 'searchinfo'), 'rewrittenquery', $matches->getQueryAfterRewrite()); $apiResult->addValue(array('query', 'searchinfo'), 'rewrittenquerysnippet', $matches->getQueryAfterRewriteSnippet()); } } // Add the search results to the result $terms = $wgContLang->convertForSearchResult($matches->termMatches()); $titles = array(); $count = 0; $result = $matches->next(); while ($result) { if (++$count > $limit) { // We've reached the one extra which shows that there are // additional items to be had. Stop here... $this->setContinueEnumParameter('offset', $params['offset'] + $params['limit']); break; } // Silently skip broken and missing titles if ($result->isBrokenTitle() || $result->isMissingRevision()) { $result = $matches->next(); continue; } $title = $result->getTitle(); if ($resultPageSet === null) { $vals = array(); ApiQueryBase::addTitleInfo($vals, $title); if (isset($prop['snippet'])) { $vals['snippet'] = $result->getTextSnippet($terms); } if (isset($prop['size'])) { $vals['size'] = $result->getByteSize(); } if (isset($prop['wordcount'])) { $vals['wordcount'] = $result->getWordCount(); } if (isset($prop['timestamp'])) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $result->getTimestamp()); } if (isset($prop['titlesnippet'])) { $vals['titlesnippet'] = $result->getTitleSnippet(); } if (isset($prop['categorysnippet'])) { $vals['categorysnippet'] = $result->getCategorySnippet(); } if (!is_null($result->getRedirectTitle())) { if (isset($prop['redirecttitle'])) { $vals['redirecttitle'] = $result->getRedirectTitle()->getPrefixedText(); } if (isset($prop['redirectsnippet'])) { $vals['redirectsnippet'] = $result->getRedirectSnippet(); } } if (!is_null($result->getSectionTitle())) { if (isset($prop['sectiontitle'])) { $vals['sectiontitle'] = $result->getSectionTitle()->getFragment(); } if (isset($prop['sectionsnippet'])) { $vals['sectionsnippet'] = $result->getSectionSnippet(); } } if (isset($prop['isfilematch'])) { $vals['isfilematch'] = $result->isFileMatch(); } // Add item to results and see whether it fits $fit = $apiResult->addValue(array('query', $this->getModuleName()), null, $vals); if (!$fit) { $this->setContinueEnumParameter('offset', $params['offset'] + $count - 1); break; } } else { $titles[] = $title; } $result = $matches->next(); } $hasInterwikiResults = false; $totalhits = null; if ($interwiki && $resultPageSet === null && $matches->hasInterwikiResults()) { foreach ($matches->getInterwikiResults() as $matches) { $matches = $matches->getInterwikiResults(); $hasInterwikiResults = true; // Include number of results if requested if ($resultPageSet === null && isset($searchInfo['totalhits'])) { $totalhits += $matches->getTotalHits(); } $result = $matches->next(); while ($result) { $title = $result->getTitle(); if ($resultPageSet === null) { $vals = array('namespace' => $result->getInterwikiNamespaceText(), 'title' => $title->getText(), 'url' => $title->getFullUrl()); // Add item to results and see whether it fits $fit = $apiResult->addValue(array('query', 'interwiki' . $this->getModuleName(), $result->getInterwikiPrefix()), null, $vals); if (!$fit) { // We hit the limit. We can't really provide any meaningful // pagination info so just bail out break; } } else { $titles[] = $title; } $result = $matches->next(); } } if ($totalhits !== null) { $apiResult->addValue(array('query', 'interwikisearchinfo'), 'totalhits', $totalhits); } } if ($resultPageSet === null) { $apiResult->addIndexedTagName(array('query', $this->getModuleName()), 'p'); if ($hasInterwikiResults) { $apiResult->addIndexedTagName(array('query', 'interwiki' . $this->getModuleName()), 'p'); } } else { $resultPageSet->populateFromTitles($titles); $offset = $params['offset'] + 1; foreach ($titles as $index => $title) { $resultPageSet->setGeneratorData($title, array('index' => $index + $offset)); } } }
/** * @param ApiPageSet $resultPageSet * @return void */ private function run($resultPageSet = null) { $params = $this->extractRequestParams(); $this->addTables('protected_titles'); $this->addFields(array('pt_namespace', 'pt_title', 'pt_timestamp')); $prop = array_flip($params['prop']); $this->addFieldsIf('pt_user', isset($prop['user']) || isset($prop['userid'])); $this->addFieldsIf('pt_reason', isset($prop['comment']) || isset($prop['parsedcomment'])); $this->addFieldsIf('pt_expiry', isset($prop['expiry'])); $this->addFieldsIf('pt_create_perm', isset($prop['level'])); $this->addTimestampWhereRange('pt_timestamp', $params['dir'], $params['start'], $params['end']); $this->addWhereFld('pt_namespace', $params['namespace']); $this->addWhereFld('pt_create_perm', $params['level']); // Include in ORDER BY for uniqueness $this->addWhereRange('pt_namespace', $params['dir'], null, null); $this->addWhereRange('pt_title', $params['dir'], null, null); if (!is_null($params['continue'])) { $cont = explode('|', $params['continue']); $this->dieContinueUsageIf(count($cont) != 3); $op = $params['dir'] === 'newer' ? '>' : '<'; $db = $this->getDB(); $continueTimestamp = $db->addQuotes($db->timestamp($cont[0])); $continueNs = (int) $cont[1]; $this->dieContinueUsageIf($continueNs != $cont[1]); $continueTitle = $db->addQuotes($cont[2]); $this->addWhere("pt_timestamp {$op} {$continueTimestamp} OR " . "(pt_timestamp = {$continueTimestamp} AND " . "(pt_namespace {$op} {$continueNs} OR " . "(pt_namespace = {$continueNs} AND " . "pt_title {$op}= {$continueTitle})))"); } if (isset($prop['user'])) { $this->addTables('user'); $this->addFields('user_name'); $this->addJoinConds(array('user' => array('LEFT JOIN', 'user_id=pt_user'))); } $this->addOption('LIMIT', $params['limit'] + 1); $res = $this->select(__METHOD__); $count = 0; $result = $this->getResult(); $titles = array(); foreach ($res as $row) { if (++$count > $params['limit']) { // We've reached the one extra which shows that there are // additional pages to be had. Stop here... $this->setContinueEnumParameter('continue', "{$row->pt_timestamp}|{$row->pt_namespace}|{$row->pt_title}"); break; } $title = Title::makeTitle($row->pt_namespace, $row->pt_title); if (is_null($resultPageSet)) { $vals = array(); ApiQueryBase::addTitleInfo($vals, $title); if (isset($prop['timestamp'])) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->pt_timestamp); } if (isset($prop['user']) && !is_null($row->user_name)) { $vals['user'] = $row->user_name; } if (isset($prop['userid']) || isset($prop['user'])) { $vals['userid'] = (int) $row->pt_user; } if (isset($prop['comment'])) { $vals['comment'] = $row->pt_reason; } if (isset($prop['parsedcomment'])) { $vals['parsedcomment'] = Linker::formatComment($row->pt_reason, $title); } if (isset($prop['expiry'])) { global $wgContLang; $vals['expiry'] = $wgContLang->formatExpiry($row->pt_expiry, TS_ISO_8601); } if (isset($prop['level'])) { $vals['level'] = $row->pt_create_perm; } $fit = $result->addValue(array('query', $this->getModuleName()), null, $vals); if (!$fit) { $this->setContinueEnumParameter('continue', "{$row->pt_timestamp}|{$row->pt_namespace}|{$row->pt_title}"); break; } } else { $titles[] = $title; } } if (is_null($resultPageSet)) { $result->addIndexedTagName(array('query', $this->getModuleName()), $this->getModulePrefix()); } else { $resultPageSet->populateFromTitles($titles); } }
private function extractRedirRowInfo($row) { $a['pageid'] = intval($row->page_id); ApiQueryBase::addTitleInfo($a, Title::makeTitle($row->page_namespace, $row->page_title)); if ($row->page_is_redirect) { $a['redirect'] = ''; } $ns = $this->hasNS ? $row->{$this->bl_ns} : NS_FILE; $parentID = $this->pageMap[$ns][$row->{$this->bl_title}]; // Put all the results in an array first $this->resultArr[$parentID]['redirlinks'][] = $a; $this->getResult()->setIndexedTagName($this->resultArr[$parentID]['redirlinks'], $this->bl_code); }