public function execute() { $params = $this->extractRequestParams(); $rev1 = $this->revisionOrTitleOrId($params['fromrev'], $params['fromtitle'], $params['fromid']); $rev2 = $this->revisionOrTitleOrId($params['torev'], $params['totitle'], $params['toid']); $revision = Revision::newFromId($rev1); if (!$revision) { $this->dieUsage('The diff cannot be retrieved, ' . 'one revision does not exist or you do not have permission to view it.', 'baddiff'); } $contentHandler = $revision->getContentHandler(); $de = $contentHandler->createDifferenceEngine($this->getContext(), $rev1, $rev2, null, true, false); $vals = array(); if (isset($params['fromtitle'])) { $vals['fromtitle'] = $params['fromtitle']; } if (isset($params['fromid'])) { $vals['fromid'] = $params['fromid']; } $vals['fromrevid'] = $rev1; if (isset($params['totitle'])) { $vals['totitle'] = $params['totitle']; } if (isset($params['toid'])) { $vals['toid'] = $params['toid']; } $vals['torevid'] = $rev2; $difftext = $de->getDiffBody(); if ($difftext === false) { $this->dieUsage('The diff cannot be retrieved. Maybe one or both revisions do ' . 'not exist or you do not have permission to view them.', 'baddiff'); } ApiResult::setContentValue($vals, 'body', $difftext); $this->getResult()->addValue(null, $this->getModuleName(), $vals); }
public function execute() { if ($this->getPageSet()->getGoodTitleCount() == 0) { return; } $params = $this->extractRequestParams(); $query = $params['query']; $protocol = ApiQueryExtLinksUsage::getProtocolPrefix($params['protocol']); $this->addFields(array('el_from', 'el_to')); $this->addTables('externallinks'); $this->addWhereFld('el_from', array_keys($this->getPageSet()->getGoodTitles())); $whereQuery = $this->prepareUrlQuerySearchString($query, $protocol); if ($whereQuery !== null) { $this->addWhere($whereQuery); } // Don't order by el_from if it's constant in the WHERE clause if (count($this->getPageSet()->getGoodTitles()) != 1) { $this->addOption('ORDER BY', 'el_from'); } // If we're querying all protocols, use DISTINCT to avoid repeating protocol-relative links twice if ($protocol === null) { $this->addOption('DISTINCT'); } $this->addOption('LIMIT', $params['limit'] + 1); $offset = isset($params['offset']) ? $params['offset'] : 0; if ($offset) { $this->addOption('OFFSET', $params['offset']); } $res = $this->select(__METHOD__); $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('offset', $offset + $params['limit']); break; } $entry = array(); $to = $row->el_to; // expand protocol-relative urls if ($params['expandurl']) { $to = wfExpandUrl($to, PROTO_CANONICAL); } ApiResult::setContentValue($entry, 'url', $to); $fit = $this->addPageSubItem($row->el_from, $entry); if (!$fit) { $this->setContinueEnumParameter('offset', $offset + $count - 1); break; } } }
public function execute() { $params = $this->extractRequestParams(); $title = Title::newFromText($params['title']); if (!$title) { $this->dieUsage('Invalid title', 'invalidtitle'); } $handle = new MessageHandle($title); if (!$handle->isValid()) { $this->dieUsage('Title does not correspond to a translatable message', 'nomessagefortitle'); } $namespace = $title->getNamespace(); $pageInfo = self::getTranslations($handle); $result = $this->getResult(); $count = 0; foreach ($pageInfo as $key => $info) { if (++$count <= $params['offset']) { continue; } $tTitle = Title::makeTitle($namespace, $key); $tHandle = new MessageHandle($tTitle); $data = array('title' => $tTitle->getPrefixedText(), 'language' => $tHandle->getCode(), 'lasttranslator' => $info[1]); $fuzzy = MessageHandle::hasFuzzyString($info[0]) || $tHandle->isFuzzy(); if ($fuzzy) { $data['fuzzy'] = 'fuzzy'; } $translation = str_replace(TRANSLATE_FUZZY, '', $info[0]); if (defined('ApiResult::META_CONTENT')) { ApiResult::setContentValue($data, 'translation', $translation); } else { ApiResult::setContent($data, $translation); } $fit = $result->addValue(array('query', $this->getModuleName()), null, $data); if (!$fit) { $this->setContinueEnumParameter('offset', $count); break; } } if (defined('ApiResult::META_CONTENT')) { $result->addIndexedTagName(array('query', $this->getModuleName()), 'message'); } else { $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'message'); } }
/** * Extract information from the Revision * * @param Revision $revision * @param object $row Should have a field 'ts_tags' if $this->fld_tags is set * @return array */ protected function extractRevisionInfo(Revision $revision, $row) { $title = $revision->getTitle(); $user = $this->getUser(); $vals = array(); $anyHidden = false; if ($this->fld_ids) { $vals['revid'] = intval($revision->getId()); if (!is_null($revision->getParentId())) { $vals['parentid'] = intval($revision->getParentId()); } } if ($this->fld_flags) { $vals['minor'] = $revision->isMinor(); } if ($this->fld_user || $this->fld_userid) { if ($revision->isDeleted(Revision::DELETED_USER)) { $vals['userhidden'] = true; $anyHidden = true; } if ($revision->userCan(Revision::DELETED_USER, $user)) { if ($this->fld_user) { $vals['user'] = $revision->getUserText(Revision::RAW); } $userid = $revision->getUser(Revision::RAW); if (!$userid) { $vals['anon'] = true; } if ($this->fld_userid) { $vals['userid'] = $userid; } } } if ($this->fld_timestamp) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $revision->getTimestamp()); } if ($this->fld_size) { if (!is_null($revision->getSize())) { $vals['size'] = intval($revision->getSize()); } else { $vals['size'] = 0; } } if ($this->fld_sha1) { if ($revision->isDeleted(Revision::DELETED_TEXT)) { $vals['sha1hidden'] = true; $anyHidden = true; } if ($revision->userCan(Revision::DELETED_TEXT, $user)) { if ($revision->getSha1() != '') { $vals['sha1'] = wfBaseConvert($revision->getSha1(), 36, 16, 40); } else { $vals['sha1'] = ''; } } } if ($this->fld_contentmodel) { $vals['contentmodel'] = $revision->getContentModel(); } if ($this->fld_comment || $this->fld_parsedcomment) { if ($revision->isDeleted(Revision::DELETED_COMMENT)) { $vals['commenthidden'] = true; $anyHidden = true; } if ($revision->userCan(Revision::DELETED_COMMENT, $user)) { $comment = $revision->getComment(Revision::RAW); if ($this->fld_comment) { $vals['comment'] = $comment; } if ($this->fld_parsedcomment) { $vals['parsedcomment'] = Linker::formatComment($comment, $title); } } } if ($this->fld_tags) { if ($row->ts_tags) { $tags = explode(',', $row->ts_tags); ApiResult::setIndexedTagName($tags, 'tag'); $vals['tags'] = $tags; } else { $vals['tags'] = array(); } } $content = null; global $wgParser; if ($this->fetchContent) { $content = $revision->getContent(Revision::FOR_THIS_USER, $this->getUser()); // Expand templates after getting section content because // template-added sections don't count and Parser::preprocess() // will have less input if ($content && $this->section !== false) { $content = $content->getSection($this->section, false); if (!$content) { $this->dieUsage("There is no section {$this->section} in r" . $revision->getId(), 'nosuchsection'); } } if ($revision->isDeleted(Revision::DELETED_TEXT)) { $vals['texthidden'] = true; $anyHidden = true; } elseif (!$content) { $vals['textmissing'] = true; } } if ($this->fld_content && $content) { $text = null; if ($this->generateXML) { if ($content->getModel() === CONTENT_MODEL_WIKITEXT) { $t = $content->getNativeData(); # note: don't set $text $wgParser->startExternalParse($title, ParserOptions::newFromContext($this->getContext()), Parser::OT_PREPROCESS); $dom = $wgParser->preprocessToDom($t); if (is_callable(array($dom, 'saveXML'))) { $xml = $dom->saveXML(); } else { $xml = $dom->__toString(); } $vals['parsetree'] = $xml; } else { $vals['badcontentformatforparsetree'] = true; $this->setWarning("Conversion to XML is supported for wikitext only, " . $title->getPrefixedDBkey() . " uses content model " . $content->getModel()); } } if ($this->expandTemplates && !$this->parseContent) { #XXX: implement template expansion for all content types in ContentHandler? if ($content->getModel() === CONTENT_MODEL_WIKITEXT) { $text = $content->getNativeData(); $text = $wgParser->preprocess($text, $title, ParserOptions::newFromContext($this->getContext())); } else { $this->setWarning("Template expansion is supported for wikitext only, " . $title->getPrefixedDBkey() . " uses content model " . $content->getModel()); $vals['badcontentformat'] = true; $text = false; } } if ($this->parseContent) { $po = $content->getParserOutput($title, $revision->getId(), ParserOptions::newFromContext($this->getContext())); $text = $po->getText(); } if ($text === null) { $format = $this->contentFormat ? $this->contentFormat : $content->getDefaultFormat(); $model = $content->getModel(); if (!$content->isSupportedFormat($format)) { $name = $title->getPrefixedDBkey(); $this->setWarning("The requested format {$this->contentFormat} is not " . "supported for content model {$model} used by {$name}"); $vals['badcontentformat'] = true; $text = false; } else { $text = $content->serialize($format); // always include format and model. // Format is needed to deserialize, model is needed to interpret. $vals['contentformat'] = $format; $vals['contentmodel'] = $model; } } if ($text !== false) { ApiResult::setContentValue($vals, 'content', $text); } } if ($content && (!is_null($this->diffto) || !is_null($this->difftotext))) { static $n = 0; // Number of uncached diffs we've had if ($n < $this->getConfig()->get('APIMaxUncachedDiffs')) { $vals['diff'] = array(); $context = new DerivativeContext($this->getContext()); $context->setTitle($title); $handler = $revision->getContentHandler(); if (!is_null($this->difftotext)) { $model = $title->getContentModel(); if ($this->contentFormat && !ContentHandler::getForModelID($model)->isSupportedFormat($this->contentFormat)) { $name = $title->getPrefixedDBkey(); $this->setWarning("The requested format {$this->contentFormat} is not " . "supported for content model {$model} used by {$name}"); $vals['diff']['badcontentformat'] = true; $engine = null; } else { $difftocontent = ContentHandler::makeContent($this->difftotext, $title, $model, $this->contentFormat); $engine = $handler->createDifferenceEngine($context); $engine->setContent($content, $difftocontent); } } else { $engine = $handler->createDifferenceEngine($context, $revision->getID(), $this->diffto); $vals['diff']['from'] = $engine->getOldid(); $vals['diff']['to'] = $engine->getNewid(); } if ($engine) { $difftext = $engine->getDiffBody(); ApiResult::setContentValue($vals['diff'], 'body', $difftext); if (!$engine->wasCacheHit()) { $n++; } } } else { $vals['diff']['notcached'] = true; } } if ($anyHidden && $revision->isDeleted(Revision::DELETED_RESTRICTED)) { $vals['suppressed'] = true; } return $vals; }
public function execute() { if ($this->getPageSet()->getGoodTitleCount() == 0) { return; } $params = $this->extractRequestParams(); $prop = array_flip((array) $params['prop']); if (isset($params['title']) && !isset($params['lang'])) { $this->dieUsageMsg(array('missingparam', 'lang')); } // Handle deprecated param $this->requireMaxOneParameter($params, 'url', 'prop'); if ($params['url']) { $prop = array('url' => 1); } $this->addFields(array('ll_from', 'll_lang', 'll_title')); $this->addTables('langlinks'); $this->addWhereFld('ll_from', array_keys($this->getPageSet()->getGoodTitles())); if (!is_null($params['continue'])) { $cont = explode('|', $params['continue']); $this->dieContinueUsageIf(count($cont) != 2); $op = $params['dir'] == 'descending' ? '<' : '>'; $llfrom = intval($cont[0]); $lllang = $this->getDB()->addQuotes($cont[1]); $this->addWhere("ll_from {$op} {$llfrom} OR " . "(ll_from = {$llfrom} AND " . "ll_lang {$op}= {$lllang})"); } // FIXME: (follow-up) To allow extensions to add to the language links, we need // to load them all, add the extra links, then apply paging. // Should not be terrible, it's not going to be more than a few hundred links. // Note that, since (ll_from, ll_lang) is a unique key, we don't need // to sort by ll_title to ensure deterministic ordering. $sort = $params['dir'] == 'descending' ? ' DESC' : ''; if (isset($params['lang'])) { $this->addWhereFld('ll_lang', $params['lang']); if (isset($params['title'])) { $this->addWhereFld('ll_title', $params['title']); } $this->addOption('ORDER BY', 'll_from' . $sort); } else { // Don't order by ll_from if it's constant in the WHERE clause if (count($this->getPageSet()->getGoodTitles()) == 1) { $this->addOption('ORDER BY', 'll_lang' . $sort); } else { $this->addOption('ORDER BY', array('ll_from' . $sort, 'll_lang' . $sort)); } } $this->addOption('LIMIT', $params['limit'] + 1); $res = $this->select(__METHOD__); $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->ll_from}|{$row->ll_lang}"); break; } $entry = array('lang' => $row->ll_lang); if (isset($prop['url'])) { $title = Title::newFromText("{$row->ll_lang}:{$row->ll_title}"); if ($title) { $entry['url'] = wfExpandUrl($title->getFullURL(), PROTO_CURRENT); } } if (isset($prop['langname'])) { $entry['langname'] = Language::fetchLanguageName($row->ll_lang, $params['inlanguagecode']); } if (isset($prop['autonym'])) { $entry['autonym'] = Language::fetchLanguageName($row->ll_lang); } ApiResult::setContentValue($entry, 'title', $row->ll_title); $fit = $this->addPageSubItem($row->ll_from, $entry); if (!$fit) { $this->setContinueEnumParameter('continue', "{$row->ll_from}|{$row->ll_lang}"); break; } } }
protected function getCurrentUserInfo() { $user = $this->getUser(); $result = $this->getResult(); $vals = array(); $vals['id'] = intval($user->getId()); $vals['name'] = $user->getName(); if ($user->isAnon()) { $vals['anon'] = true; } if (isset($this->prop['blockinfo'])) { if ($user->isBlocked()) { $block = $user->getBlock(); $vals['blockid'] = $block->getId(); $vals['blockedby'] = $block->getByName(); $vals['blockedbyid'] = $block->getBy(); $vals['blockreason'] = $user->blockedFor(); $vals['blockedtimestamp'] = wfTimestamp(TS_ISO_8601, $block->mTimestamp); $vals['blockexpiry'] = $block->getExpiry() === 'infinity' ? 'infinite' : wfTimestamp(TS_ISO_8601, $block->getExpiry()); } } if (isset($this->prop['hasmsg'])) { $vals['messages'] = $user->getNewtalk(); } if (isset($this->prop['groups'])) { $vals['groups'] = $user->getEffectiveGroups(); ApiResult::setArrayType($vals['groups'], 'array'); // even if empty ApiResult::setIndexedTagName($vals['groups'], 'g'); // even if empty } if (isset($this->prop['implicitgroups'])) { $vals['implicitgroups'] = $user->getAutomaticGroups(); ApiResult::setArrayType($vals['implicitgroups'], 'array'); // even if empty ApiResult::setIndexedTagName($vals['implicitgroups'], 'g'); // even if empty } if (isset($this->prop['rights'])) { // User::getRights() may return duplicate values, strip them $vals['rights'] = array_values(array_unique($user->getRights())); ApiResult::setArrayType($vals['rights'], 'array'); // even if empty ApiResult::setIndexedTagName($vals['rights'], 'r'); // even if empty } if (isset($this->prop['changeablegroups'])) { $vals['changeablegroups'] = $user->changeableGroups(); ApiResult::setIndexedTagName($vals['changeablegroups']['add'], 'g'); ApiResult::setIndexedTagName($vals['changeablegroups']['remove'], 'g'); ApiResult::setIndexedTagName($vals['changeablegroups']['add-self'], 'g'); ApiResult::setIndexedTagName($vals['changeablegroups']['remove-self'], 'g'); } if (isset($this->prop['options'])) { $vals['options'] = $user->getOptions(); $vals['options'][ApiResult::META_BC_BOOLS] = array_keys($vals['options']); } if (isset($this->prop['preferencestoken'])) { $p = $this->getModulePrefix(); $this->setWarning("{$p}prop=preferencestoken has been deprecated. Please use action=query&meta=tokens instead."); } if (isset($this->prop['preferencestoken']) && !$this->lacksSameOriginSecurity() && $user->isAllowed('editmyoptions')) { $vals['preferencestoken'] = $user->getEditToken('', $this->getMain()->getRequest()); } if (isset($this->prop['editcount'])) { // use intval to prevent null if a non-logged-in user calls // api.php?format=jsonfm&action=query&meta=userinfo&uiprop=editcount $vals['editcount'] = intval($user->getEditCount()); } if (isset($this->prop['ratelimits'])) { $vals['ratelimits'] = $this->getRateLimits(); } if (isset($this->prop['realname']) && !in_array('realname', $this->getConfig()->get('HiddenPrefs'))) { $vals['realname'] = $user->getRealName(); } if ($user->isAllowed('viewmyprivateinfo')) { if (isset($this->prop['email'])) { $vals['email'] = $user->getEmail(); $auth = $user->getEmailAuthenticationTimestamp(); if (!is_null($auth)) { $vals['emailauthenticated'] = wfTimestamp(TS_ISO_8601, $auth); } } } if (isset($this->prop['registrationdate'])) { $regDate = $user->getRegistration(); if ($regDate !== false) { $vals['registrationdate'] = wfTimestamp(TS_ISO_8601, $regDate); } } if (isset($this->prop['acceptlang'])) { $langs = $this->getRequest()->getAcceptLang(); $acceptLang = array(); foreach ($langs as $lang => $val) { $r = array('q' => $val); ApiResult::setContentValue($r, 'code', $lang); $acceptLang[] = $r; } ApiResult::setIndexedTagName($acceptLang, 'lang'); $vals['acceptlang'] = $acceptLang; } if (isset($this->prop['unreadcount'])) { $dbr = $this->getQuery()->getNamedDB('watchlist', DB_SLAVE, 'watchlist'); $count = $dbr->selectRowCount('watchlist', '1', array('wl_user' => $user->getId(), 'wl_notificationtimestamp IS NOT NULL'), __METHOD__, array('LIMIT' => self::WL_UNREAD_LIMIT)); if ($count >= self::WL_UNREAD_LIMIT) { $vals['unreadcount'] = self::WL_UNREAD_LIMIT . '+'; } else { $vals['unreadcount'] = $count; } } return $vals; }
public function execute() { // Cache may vary on $wgUser because ParserOptions gets data from it $this->getMain()->setCacheMode('anon-public-user-private'); // Get parameters $params = $this->extractRequestParams(); $this->requireMaxOneParameter($params, 'prop', 'generatexml'); if ($params['prop'] === null) { $this->logFeatureUsage('action=expandtemplates&!prop'); $this->setWarning('Because no values have been specified for the prop parameter, a ' . 'legacy format has been used for the output. This format is deprecated, and in ' . 'the future, a default value will be set for the prop parameter, causing the new' . 'format to always be used.'); $prop = array(); } else { $prop = array_flip($params['prop']); } // Get title and revision ID for parser $revid = $params['revid']; if ($revid !== null) { $rev = Revision::newFromId($revid); if (!$rev) { $this->dieUsage("There is no revision ID {$revid}", 'missingrev'); } $title_obj = $rev->getTitle(); } else { $title_obj = Title::newFromText($params['title']); if (!$title_obj || $title_obj->isExternal()) { $this->dieUsageMsg(array('invalidtitle', $params['title'])); } } $result = $this->getResult(); // Parse text global $wgParser; $options = ParserOptions::newFromContext($this->getContext()); if ($params['includecomments']) { $options->setRemoveComments(false); } $retval = array(); if (isset($prop['parsetree']) || $params['generatexml']) { if (!isset($prop['parsetree'])) { $this->logFeatureUsage('action=expandtemplates&generatexml'); } $wgParser->startExternalParse($title_obj, $options, Parser::OT_PREPROCESS); $dom = $wgParser->preprocessToDom($params['text']); if (is_callable(array($dom, 'saveXML'))) { $xml = $dom->saveXML(); } else { $xml = $dom->__toString(); } if (isset($prop['parsetree'])) { unset($prop['parsetree']); $retval['parsetree'] = $xml; } else { // the old way $result->addValue(null, 'parsetree', $xml); $result->addValue(null, ApiResult::META_BC_SUBELEMENTS, array('parsetree')); } } // if they didn't want any output except (probably) the parse tree, // then don't bother actually fully expanding it if ($prop || $params['prop'] === null) { $wgParser->startExternalParse($title_obj, $options, Parser::OT_PREPROCESS); $frame = $wgParser->getPreprocessor()->newFrame(); $wikitext = $wgParser->preprocess($params['text'], $title_obj, $options, $revid, $frame); if ($params['prop'] === null) { // the old way ApiResult::setContentValue($retval, 'wikitext', $wikitext); } else { if (isset($prop['categories'])) { $categories = $wgParser->getOutput()->getCategories(); if ($categories) { $categories_result = array(); foreach ($categories as $category => $sortkey) { $entry = array(); $entry['sortkey'] = $sortkey; ApiResult::setContentValue($entry, 'category', $category); $categories_result[] = $entry; } ApiResult::setIndexedTagName($categories_result, 'category'); $retval['categories'] = $categories_result; } } if (isset($prop['properties'])) { $properties = $wgParser->getOutput()->getProperties(); if ($properties) { ApiResult::setArrayType($properties, 'BCkvp', 'name'); ApiResult::setIndexedTagName($properties, 'property'); $retval['properties'] = $properties; } } if (isset($prop['volatile'])) { $retval['volatile'] = $frame->isVolatile(); } if (isset($prop['ttl']) && $frame->getTTL() !== null) { $retval['ttl'] = $frame->getTTL(); } if (isset($prop['wikitext'])) { $retval['wikitext'] = $wikitext; } } } ApiResult::setSubelementsList($retval, array('wikitext', 'parsetree')); $result->addValue(null, $this->getModuleName(), $retval); }
private function formatHeadItems($headItems) { $result = []; foreach ($headItems as $tag => $content) { $entry = []; $entry['tag'] = $tag; ApiResult::setContentValue($entry, 'content', $content); $result[] = $entry; } return $result; }
protected function getCurrentUserInfo() { $user = $this->getUser(); $vals = []; $vals['id'] = intval($user->getId()); $vals['name'] = $user->getName(); if ($user->isAnon()) { $vals['anon'] = true; } if (isset($this->prop['blockinfo']) && $user->isBlocked()) { $vals = array_merge($vals, self::getBlockInfo($user->getBlock())); } if (isset($this->prop['hasmsg'])) { $vals['messages'] = $user->getNewtalk(); } if (isset($this->prop['groups'])) { $vals['groups'] = $user->getEffectiveGroups(); ApiResult::setArrayType($vals['groups'], 'array'); // even if empty ApiResult::setIndexedTagName($vals['groups'], 'g'); // even if empty } if (isset($this->prop['implicitgroups'])) { $vals['implicitgroups'] = $user->getAutomaticGroups(); ApiResult::setArrayType($vals['implicitgroups'], 'array'); // even if empty ApiResult::setIndexedTagName($vals['implicitgroups'], 'g'); // even if empty } if (isset($this->prop['rights'])) { // User::getRights() may return duplicate values, strip them $vals['rights'] = array_values(array_unique($user->getRights())); ApiResult::setArrayType($vals['rights'], 'array'); // even if empty ApiResult::setIndexedTagName($vals['rights'], 'r'); // even if empty } if (isset($this->prop['changeablegroups'])) { $vals['changeablegroups'] = $user->changeableGroups(); ApiResult::setIndexedTagName($vals['changeablegroups']['add'], 'g'); ApiResult::setIndexedTagName($vals['changeablegroups']['remove'], 'g'); ApiResult::setIndexedTagName($vals['changeablegroups']['add-self'], 'g'); ApiResult::setIndexedTagName($vals['changeablegroups']['remove-self'], 'g'); } if (isset($this->prop['options'])) { $vals['options'] = $user->getOptions(); $vals['options'][ApiResult::META_BC_BOOLS] = array_keys($vals['options']); } if (isset($this->prop['preferencestoken'])) { $p = $this->getModulePrefix(); $this->setWarning("{$p}prop=preferencestoken has been deprecated. Please use action=query&meta=tokens instead."); } if (isset($this->prop['preferencestoken']) && !$this->lacksSameOriginSecurity() && $user->isAllowed('editmyoptions')) { $vals['preferencestoken'] = $user->getEditToken('', $this->getMain()->getRequest()); } if (isset($this->prop['editcount'])) { // use intval to prevent null if a non-logged-in user calls // api.php?format=jsonfm&action=query&meta=userinfo&uiprop=editcount $vals['editcount'] = intval($user->getEditCount()); } if (isset($this->prop['ratelimits'])) { $vals['ratelimits'] = $this->getRateLimits(); } if (isset($this->prop['realname']) && !in_array('realname', $this->getConfig()->get('HiddenPrefs'))) { $vals['realname'] = $user->getRealName(); } if ($user->isAllowed('viewmyprivateinfo')) { if (isset($this->prop['email'])) { $vals['email'] = $user->getEmail(); $auth = $user->getEmailAuthenticationTimestamp(); if (!is_null($auth)) { $vals['emailauthenticated'] = wfTimestamp(TS_ISO_8601, $auth); } } } if (isset($this->prop['registrationdate'])) { $regDate = $user->getRegistration(); if ($regDate !== false) { $vals['registrationdate'] = wfTimestamp(TS_ISO_8601, $regDate); } } if (isset($this->prop['acceptlang'])) { $langs = $this->getRequest()->getAcceptLang(); $acceptLang = []; foreach ($langs as $lang => $val) { $r = ['q' => $val]; ApiResult::setContentValue($r, 'code', $lang); $acceptLang[] = $r; } ApiResult::setIndexedTagName($acceptLang, 'lang'); $vals['acceptlang'] = $acceptLang; } if (isset($this->prop['unreadcount'])) { $store = MediaWikiServices::getInstance()->getWatchedItemStore(); $unreadNotifications = $store->countUnreadNotifications($user, self::WL_UNREAD_LIMIT); if ($unreadNotifications === true) { $vals['unreadcount'] = self::WL_UNREAD_LIMIT . '+'; } else { $vals['unreadcount'] = $unreadNotifications; } } if (isset($this->prop['centralids'])) { $vals += self::getCentralUserInfo($this->getConfig(), $this->getUser(), $this->params['attachedwiki']); } return $vals; }
/** * Formats the internal list of exposed APIs into an array suitable * to pass to the API's XML formatter. * * @return array */ protected function formatRsdApiList() { $apis = $this->getRsdApiList(); $outputData = array(); foreach ($apis as $name => $info) { $data = array('name' => $name, 'preferred' => wfBoolToStr($name == 'MediaWiki'), 'apiLink' => $info['apiLink'], 'blogID' => isset($info['blogID']) ? $info['blogID'] : ''); $settings = array(); if (isset($info['docs'])) { $settings['docs'] = $info['docs']; ApiResult::setSubelementsList($settings, 'docs'); } if (isset($info['settings'])) { foreach ($info['settings'] as $setting => $val) { if (is_bool($val)) { $xmlVal = wfBoolToStr($val); } else { $xmlVal = $val; } $setting = array('name' => $setting); ApiResult::setContentValue($setting, 'value', $xmlVal); $settings[] = $setting; } } if (count($settings)) { ApiResult::setIndexedTagName($settings, 'setting'); $data['settings'] = $settings; } $outputData[] = $data; } return $outputData; }
/** * @covers ApiResult */ public function testStaticDataMethods() { $arr = array(); ApiResult::setValue($arr, 'setValue', '1'); ApiResult::setValue($arr, null, 'unnamed 1'); ApiResult::setValue($arr, null, 'unnamed 2'); ApiResult::setValue($arr, 'deleteValue', '2'); ApiResult::unsetValue($arr, 'deleteValue'); ApiResult::setContentValue($arr, 'setContentValue', '3'); $this->assertSame(array('setValue' => '1', 'unnamed 1', 'unnamed 2', ApiResult::META_CONTENT => 'setContentValue', 'setContentValue' => '3'), $arr); try { ApiResult::setValue($arr, 'setValue', '99'); $this->fail('Expected exception not thrown'); } catch (RuntimeException $ex) { $this->assertSame('Attempting to add element setValue=99, existing value is 1', $ex->getMessage(), 'Expected exception'); } try { ApiResult::setContentValue($arr, 'setContentValue2', '99'); $this->fail('Expected exception not thrown'); } catch (RuntimeException $ex) { $this->assertSame('Attempting to set content element as setContentValue2 when setContentValue ' . 'is already set as the content element', $ex->getMessage(), 'Expected exception'); } ApiResult::setValue($arr, 'setValue', '99', ApiResult::OVERRIDE); $this->assertSame('99', $arr['setValue']); ApiResult::setContentValue($arr, 'setContentValue2', '99', ApiResult::OVERRIDE); $this->assertSame('setContentValue2', $arr[ApiResult::META_CONTENT]); $arr = array('foo' => 1, 'bar' => 1); ApiResult::setValue($arr, 'top', '2', ApiResult::ADD_ON_TOP); ApiResult::setValue($arr, null, '2', ApiResult::ADD_ON_TOP); ApiResult::setValue($arr, 'bottom', '2'); ApiResult::setValue($arr, 'foo', '2', ApiResult::OVERRIDE); ApiResult::setValue($arr, 'bar', '2', ApiResult::OVERRIDE | ApiResult::ADD_ON_TOP); $this->assertSame(array(0, 'top', 'foo', 'bar', 'bottom'), array_keys($arr)); $arr = array(); ApiResult::setValue($arr, 'sub', array('foo' => 1)); ApiResult::setValue($arr, 'sub', array('bar' => 1)); $this->assertSame(array('sub' => array('foo' => 1, 'bar' => 1)), $arr); try { ApiResult::setValue($arr, 'sub', array('foo' => 2, 'baz' => 2)); $this->fail('Expected exception not thrown'); } catch (RuntimeException $ex) { $this->assertSame('Conflicting keys (foo) when attempting to merge element sub', $ex->getMessage(), 'Expected exception'); } $arr = array(); $title = Title::newFromText("MediaWiki:Foobar"); $obj = new stdClass(); $obj->foo = 1; $obj->bar = 2; ApiResult::setValue($arr, 'title', $title); ApiResult::setValue($arr, 'obj', $obj); $this->assertSame(array('title' => (string) $title, 'obj' => array('foo' => 1, 'bar' => 2, ApiResult::META_TYPE => 'assoc')), $arr); $fh = tmpfile(); try { ApiResult::setValue($arr, 'file', $fh); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add resource(stream) to ApiResult', $ex->getMessage(), 'Expected exception'); } try { ApiResult::setValue($arr, null, $fh); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add resource(stream) to ApiResult', $ex->getMessage(), 'Expected exception'); } try { $obj->file = $fh; ApiResult::setValue($arr, 'sub', $obj); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add resource(stream) to ApiResult', $ex->getMessage(), 'Expected exception'); } try { $obj->file = $fh; ApiResult::setValue($arr, null, $obj); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add resource(stream) to ApiResult', $ex->getMessage(), 'Expected exception'); } fclose($fh); try { ApiResult::setValue($arr, 'inf', INF); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add non-finite floats to ApiResult', $ex->getMessage(), 'Expected exception'); } try { ApiResult::setValue($arr, null, INF); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add non-finite floats to ApiResult', $ex->getMessage(), 'Expected exception'); } try { ApiResult::setValue($arr, 'nan', NAN); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add non-finite floats to ApiResult', $ex->getMessage(), 'Expected exception'); } try { ApiResult::setValue($arr, null, NAN); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add non-finite floats to ApiResult', $ex->getMessage(), 'Expected exception'); } ApiResult::setValue($arr, null, NAN, ApiResult::NO_VALIDATE); try { ApiResult::setValue($arr, null, NAN, ApiResult::NO_SIZE_CHECK); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add non-finite floats to ApiResult', $ex->getMessage(), 'Expected exception'); } $arr = array(); $result2 = new ApiResult(8388608); $result2->addValue(null, 'foo', 'bar'); ApiResult::setValue($arr, 'baz', $result2); $this->assertSame(array('baz' => array(ApiResult::META_TYPE => 'assoc', 'foo' => 'bar')), $arr); $arr = array(); ApiResult::setValue($arr, 'foo', "foo�bar"); ApiResult::setValue($arr, 'bar', "á"); ApiResult::setValue($arr, 'baz', 74); ApiResult::setValue($arr, null, "foo�bar"); ApiResult::setValue($arr, null, "á"); $this->assertSame(array('foo' => "foo�bar", 'bar' => "á", 'baz' => 74, 0 => "foo�bar", 1 => "á"), $arr); }
public function execute() { if ($this->getPageSet()->getGoodTitleCount() == 0) { return; } $params = $this->extractRequestParams(); $prop = array_flip((array) $params['prop']); if (isset($params['title']) && !isset($params['prefix'])) { $this->dieUsageMsg(['missingparam', 'prefix']); } // Handle deprecated param $this->requireMaxOneParameter($params, 'url', 'prop'); if ($params['url']) { $prop = ['url' => 1]; } $this->addFields(['iwl_from', 'iwl_prefix', 'iwl_title']); $this->addTables('iwlinks'); $this->addWhereFld('iwl_from', array_keys($this->getPageSet()->getGoodTitles())); if (!is_null($params['continue'])) { $cont = explode('|', $params['continue']); $this->dieContinueUsageIf(count($cont) != 3); $op = $params['dir'] == 'descending' ? '<' : '>'; $db = $this->getDB(); $iwlfrom = intval($cont[0]); $iwlprefix = $db->addQuotes($cont[1]); $iwltitle = $db->addQuotes($cont[2]); $this->addWhere("iwl_from {$op} {$iwlfrom} OR " . "(iwl_from = {$iwlfrom} AND " . "(iwl_prefix {$op} {$iwlprefix} OR " . "(iwl_prefix = {$iwlprefix} AND " . "iwl_title {$op}= {$iwltitle})))"); } $sort = $params['dir'] == 'descending' ? ' DESC' : ''; if (isset($params['prefix'])) { $this->addWhereFld('iwl_prefix', $params['prefix']); if (isset($params['title'])) { $this->addWhereFld('iwl_title', $params['title']); $this->addOption('ORDER BY', 'iwl_from' . $sort); } else { $this->addOption('ORDER BY', ['iwl_from' . $sort, 'iwl_title' . $sort]); } } else { // Don't order by iwl_from if it's constant in the WHERE clause if (count($this->getPageSet()->getGoodTitles()) == 1) { $this->addOption('ORDER BY', 'iwl_prefix' . $sort); } else { $this->addOption('ORDER BY', ['iwl_from' . $sort, 'iwl_prefix' . $sort, 'iwl_title' . $sort]); } } $this->addOption('LIMIT', $params['limit'] + 1); $res = $this->select(__METHOD__); $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->iwl_from}|{$row->iwl_prefix}|{$row->iwl_title}"); break; } $entry = ['prefix' => $row->iwl_prefix]; if (isset($prop['url'])) { $title = Title::newFromText("{$row->iwl_prefix}:{$row->iwl_title}"); if ($title) { $entry['url'] = wfExpandUrl($title->getFullURL(), PROTO_CURRENT); } } ApiResult::setContentValue($entry, 'title', $row->iwl_title); $fit = $this->addPageSubItem($row->iwl_from, $entry); if (!$fit) { $this->setContinueEnumParameter('continue', "{$row->iwl_from}|{$row->iwl_prefix}|{$row->iwl_title}"); break; } } }
/** * Replace the result data with the information about an exception. * Returns the error code * @param Exception $e * @return string */ protected function substituteResultWithError($e) { $result = $this->getResult(); $config = $this->getConfig(); $errMessage = $this->errorMessageFromException($e); if ($e instanceof UsageException) { // User entered incorrect parameters - generate error response $link = wfExpandUrl(wfScript('api')); ApiResult::setContentValue($errMessage, 'docref', "See {$link} for API usage"); } else { // Something is seriously wrong if ($config->get('ShowExceptionDetails')) { ApiResult::setContentValue($errMessage, 'trace', MWExceptionHandler::getRedactedTraceAsString($e)); } } // Remember all the warnings to re-add them later $warnings = $result->getResultData(['warnings']); $result->reset(); // Re-add the id $requestid = $this->getParameter('requestid'); if (!is_null($requestid)) { $result->addValue(null, 'requestid', $requestid, ApiResult::NO_SIZE_CHECK); } if ($config->get('ShowHostnames')) { // servedby is especially useful when debugging errors $result->addValue(null, 'servedby', wfHostname(), ApiResult::NO_SIZE_CHECK); } if ($warnings !== null) { $result->addValue(null, 'warnings', $warnings, ApiResult::NO_SIZE_CHECK); } $result->addValue(null, 'error', $errMessage, ApiResult::NO_SIZE_CHECK); return $errMessage['code']; }
public function execute() { $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'); } $this->setWarning('list=deletedrevs has been deprecated. Please use prop=deletedrevisions or ' . 'list=alldeletedrevisions instead.'); $this->logFeatureUsage('action=query&list=deletedrevs'); $db = $this->getDB(); $params = $this->extractRequestParams(false); $prop = array_flip($params['prop']); $fld_parentid = isset($prop['parentid']); $fld_revid = isset($prop['revid']); $fld_user = isset($prop['user']); $fld_userid = isset($prop['userid']); $fld_comment = isset($prop['comment']); $fld_parsedcomment = isset($prop['parsedcomment']); $fld_minor = isset($prop['minor']); $fld_len = isset($prop['len']); $fld_sha1 = isset($prop['sha1']); $fld_content = isset($prop['content']); $fld_token = isset($prop['token']); $fld_tags = isset($prop['tags']); if (isset($prop['token'])) { $p = $this->getModulePrefix(); $this->setWarning("{$p}prop=token has been deprecated. Please use action=query&meta=tokens instead."); } // If we're in a mode that breaks the same-origin policy, no tokens can // be obtained if ($this->lacksSameOriginSecurity()) { $fld_token = false; } // If user can't undelete, no tokens if (!$user->isAllowed('undelete')) { $fld_token = false; } $result = $this->getResult(); $pageSet = $this->getPageSet(); $titles = $pageSet->getTitles(); // This module operates in three modes: // 'revs': List deleted revs for certain titles (1) // 'user': List deleted revs by a certain user (2) // 'all': List all deleted revs in NS (3) $mode = 'all'; if (count($titles) > 0) { $mode = 'revs'; } elseif (!is_null($params['user'])) { $mode = 'user'; } if ($mode == 'revs' || $mode == 'user') { // Ignore namespace and unique due to inability to know whether they were purposely set foreach (array('from', 'to', 'prefix') as $p) { if (!is_null($params[$p])) { $this->dieUsage("The '{$p}' parameter cannot be used in modes 1 or 2", 'badparams'); } } } else { foreach (array('start', 'end') as $p) { if (!is_null($params[$p])) { $this->dieUsage("The {$p} parameter cannot be used in mode 3", 'badparams'); } } } if (!is_null($params['user']) && !is_null($params['excludeuser'])) { $this->dieUsage('user and excludeuser cannot be used together', 'badparams'); } $this->addTables('archive'); $this->addFields(array('ar_title', 'ar_namespace', 'ar_timestamp', 'ar_deleted', 'ar_id')); $this->addFieldsIf('ar_parent_id', $fld_parentid); $this->addFieldsIf('ar_rev_id', $fld_revid); $this->addFieldsIf('ar_user_text', $fld_user); $this->addFieldsIf('ar_user', $fld_userid); $this->addFieldsIf('ar_comment', $fld_comment || $fld_parsedcomment); $this->addFieldsIf('ar_minor_edit', $fld_minor); $this->addFieldsIf('ar_len', $fld_len); $this->addFieldsIf('ar_sha1', $fld_sha1); if ($fld_tags) { $this->addTables('tag_summary'); $this->addJoinConds(array('tag_summary' => array('LEFT JOIN', array('ar_rev_id=ts_rev_id')))); $this->addFields('ts_tags'); } if (!is_null($params['tag'])) { $this->addTables('change_tag'); $this->addJoinConds(array('change_tag' => array('INNER JOIN', array('ar_rev_id=ct_rev_id')))); $this->addWhereFld('ct_tag', $params['tag']); } if ($fld_content) { // 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, plus ar_text_id // to be able to tell the difference. $this->addTables('text'); $this->addJoinConds(array('text' => array('LEFT JOIN', array('ar_text_id=old_id')))); $this->addFields(array('ar_text', 'ar_flags', 'ar_text_id', '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'); } } // Check limits $userMax = $fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1; $botMax = $fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2; $limit = $params['limit']; if ($limit == 'max') { $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax; $this->getResult()->addParsedLimit($this->getModuleName(), $limit); } $this->validateLimit('limit', $limit, 1, $userMax, $botMax); if ($fld_token) { // Undelete tokens are identical for all pages, so we cache one here $token = $user->getEditToken('', $this->getMain()->getRequest()); } $dir = $params['dir']; // We need a custom WHERE clause that matches all titles. if ($mode == 'revs') { $lb = new LinkBatch($titles); $where = $lb->constructSet('ar', $db); $this->addWhere($where); } elseif ($mode == 'all') { $this->addWhereFld('ar_namespace', $params['namespace']); $from = $params['from'] === null ? null : $this->titlePartToKey($params['from'], $params['namespace']); $to = $params['to'] === null ? null : $this->titlePartToKey($params['to'], $params['namespace']); $this->addWhereRange('ar_title', $dir, $from, $to); if (isset($params['prefix'])) { $this->addWhere('ar_title' . $db->buildLike($this->titlePartToKey($params['prefix'], $params['namespace']), $db->anyString())); } } 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 ($mode == 'all' || $mode == 'revs') { $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', $limit + 1); $this->addOption('USE INDEX', array('archive' => $mode == 'user' ? 'usertext_timestamp' : 'name_title_timestamp')); if ($mode == 'all') { if ($params['unique']) { // @todo Does this work on non-MySQL? $this->addOption('GROUP BY', 'ar_title'); } else { $sort = $dir == 'newer' ? '' : ' DESC'; $this->addOption('ORDER BY', array('ar_title' . $sort, 'ar_timestamp' . $sort, 'ar_id' . $sort)); } } else { if ($mode == 'revs') { // Sort by ns and title in the same order as timestamp for efficiency $this->addWhereRange('ar_namespace', $dir, null, null); $this->addWhereRange('ar_title', $dir, null, null); } $this->addTimestampWhereRange('ar_timestamp', $dir, $params['start'], $params['end']); // Include in ORDER BY for uniqueness $this->addWhereRange('ar_id', $dir, null, null); } $res = $this->select(__METHOD__); $pageMap = array(); // Maps ns&title to (fake) pageid $count = 0; $newPageID = 0; foreach ($res as $row) { if (++$count > $limit) { // We've had enough if ($mode == 'all' || $mode == 'revs') { $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; } $rev = array(); $anyHidden = false; $rev['timestamp'] = wfTimestamp(TS_ISO_8601, $row->ar_timestamp); if ($fld_revid) { $rev['revid'] = intval($row->ar_rev_id); } if ($fld_parentid && !is_null($row->ar_parent_id)) { $rev['parentid'] = intval($row->ar_parent_id); } if ($fld_user || $fld_userid) { if ($row->ar_deleted & Revision::DELETED_USER) { $rev['userhidden'] = true; $anyHidden = true; } if (Revision::userCanBitfield($row->ar_deleted, Revision::DELETED_USER, $user)) { if ($fld_user) { $rev['user'] = $row->ar_user_text; } if ($fld_userid) { $rev['userid'] = $row->ar_user; } } } if ($fld_comment || $fld_parsedcomment) { if ($row->ar_deleted & Revision::DELETED_COMMENT) { $rev['commenthidden'] = true; $anyHidden = true; } if (Revision::userCanBitfield($row->ar_deleted, Revision::DELETED_COMMENT, $user)) { if ($fld_comment) { $rev['comment'] = $row->ar_comment; } if ($fld_parsedcomment) { $title = Title::makeTitle($row->ar_namespace, $row->ar_title); $rev['parsedcomment'] = Linker::formatComment($row->ar_comment, $title); } } } if ($fld_minor) { $rev['minor'] = $row->ar_minor_edit == 1; } if ($fld_len) { $rev['len'] = $row->ar_len; } if ($fld_sha1) { if ($row->ar_deleted & Revision::DELETED_TEXT) { $rev['sha1hidden'] = true; $anyHidden = true; } if (Revision::userCanBitfield($row->ar_deleted, Revision::DELETED_TEXT, $user)) { if ($row->ar_sha1 != '') { $rev['sha1'] = wfBaseConvert($row->ar_sha1, 36, 16, 40); } else { $rev['sha1'] = ''; } } } if ($fld_content) { if ($row->ar_deleted & Revision::DELETED_TEXT) { $rev['texthidden'] = true; $anyHidden = true; } if (Revision::userCanBitfield($row->ar_deleted, Revision::DELETED_TEXT, $user)) { if (isset($row->ar_text) && !$row->ar_text_id) { // Pre-1.5 ar_text row (if condition from Revision::newFromArchiveRow) ApiResult::setContentValue($rev, 'text', Revision::getRevisionText($row, 'ar_')); } else { ApiResult::setContentValue($rev, 'text', Revision::getRevisionText($row)); } } } if ($fld_tags) { if ($row->ts_tags) { $tags = explode(',', $row->ts_tags); ApiResult::setIndexedTagName($tags, 'tag'); $rev['tags'] = $tags; } else { $rev['tags'] = array(); } } if ($anyHidden && $row->ar_deleted & Revision::DELETED_RESTRICTED) { $rev['suppressed'] = true; } if (!isset($pageMap[$row->ar_namespace][$row->ar_title])) { $pageID = $newPageID++; $pageMap[$row->ar_namespace][$row->ar_title] = $pageID; $a['revisions'] = array($rev); ApiResult::setIndexedTagName($a['revisions'], 'rev'); $title = Title::makeTitle($row->ar_namespace, $row->ar_title); ApiQueryBase::addTitleInfo($a, $title); if ($fld_token) { $a['token'] = $token; } $fit = $result->addValue(array('query', $this->getModuleName()), $pageID, $a); } else { $pageID = $pageMap[$row->ar_namespace][$row->ar_title]; $fit = $result->addValue(array('query', $this->getModuleName(), $pageID, 'revisions'), null, $rev); } if (!$fit) { if ($mode == 'all' || $mode == 'revs') { $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; } } $result->addIndexedTagName(array('query', $this->getModuleName()), 'page'); }
private function formatCss($css) { $result = array(); foreach ($css as $file => $link) { $entry = array(); $entry['file'] = $file; ApiResult::setContentValue($entry, 'link', $link); $result[] = $entry; } return $result; }
public function appendSkins($property) { $data = array(); $allowed = Skin::getAllowedSkins(); $default = Skin::normalizeKey('default'); foreach (Skin::getSkinNames() as $name => $displayName) { $msg = $this->msg("skinname-{$name}"); $code = $this->getParameter('inlanguagecode'); if ($code && Language::isValidCode($code)) { $msg->inLanguage($code); } else { $msg->inContentLanguage(); } if ($msg->exists()) { $displayName = $msg->text(); } $skin = array('code' => $name); ApiResult::setContentValue($skin, 'name', $displayName); if (!isset($allowed[$name])) { $skin['unusable'] = true; } if ($name === $default) { $skin['default'] = true; } $data[] = $skin; } ApiResult::setIndexedTagName($data, 'skin'); return $this->getResult()->addValue('query', $property, $data); }
/** * @param ApiPageSet $resultPageSet */ private function run($resultPageSet = null) { $db = $this->getDB(); $params = $this->extractRequestParams(); $this->addTables('category'); $this->addFields('cat_title'); if (!is_null($params['continue'])) { $cont = explode('|', $params['continue']); $this->dieContinueUsageIf(count($cont) != 1); $op = $params['dir'] == 'descending' ? '<' : '>'; $cont_from = $db->addQuotes($cont[0]); $this->addWhere("cat_title {$op}= {$cont_from}"); } $dir = $params['dir'] == 'descending' ? 'older' : 'newer'; $from = $params['from'] === null ? null : $this->titlePartToKey($params['from'], NS_CATEGORY); $to = $params['to'] === null ? null : $this->titlePartToKey($params['to'], NS_CATEGORY); $this->addWhereRange('cat_title', $dir, $from, $to); $min = $params['min']; $max = $params['max']; if ($dir == 'newer') { $this->addWhereRange('cat_pages', 'newer', $min, $max); } else { $this->addWhereRange('cat_pages', 'older', $max, $min); } if (isset($params['prefix'])) { $this->addWhere('cat_title' . $db->buildLike($this->titlePartToKey($params['prefix'], NS_CATEGORY), $db->anyString())); } $this->addOption('LIMIT', $params['limit'] + 1); $sort = $params['dir'] == 'descending' ? ' DESC' : ''; $this->addOption('ORDER BY', 'cat_title' . $sort); $prop = array_flip($params['prop']); $this->addFieldsIf(array('cat_pages', 'cat_subcats', 'cat_files'), isset($prop['size'])); if (isset($prop['hidden'])) { $this->addTables(array('page', 'page_props')); $this->addJoinConds(array('page' => array('LEFT JOIN', array('page_namespace' => NS_CATEGORY, 'page_title=cat_title')), 'page_props' => array('LEFT JOIN', array('pp_page=page_id', 'pp_propname' => 'hiddencat')))); $this->addFields(array('cat_hidden' => 'pp_propname')); } $res = $this->select(__METHOD__); $pages = array(); $result = $this->getResult(); $count = 0; foreach ($res as $row) { if (++$count > $params['limit']) { // We've reached the one extra which shows that there are // additional cats to be had. Stop here... $this->setContinueEnumParameter('continue', $row->cat_title); break; } // Normalize titles $titleObj = Title::makeTitle(NS_CATEGORY, $row->cat_title); if (!is_null($resultPageSet)) { $pages[] = $titleObj; } else { $item = array(); ApiResult::setContentValue($item, 'category', $titleObj->getText()); if (isset($prop['size'])) { $item['size'] = intval($row->cat_pages); $item['pages'] = $row->cat_pages - $row->cat_subcats - $row->cat_files; $item['files'] = intval($row->cat_files); $item['subcats'] = intval($row->cat_subcats); } if (isset($prop['hidden'])) { $item['hidden'] = (bool) $row->cat_hidden; } $fit = $result->addValue(array('query', $this->getModuleName()), null, $item); if (!$fit) { $this->setContinueEnumParameter('continue', $row->cat_title); break; } } } if (is_null($resultPageSet)) { $result->addIndexedTagName(array('query', $this->getModuleName()), 'c'); } else { $resultPageSet->populateFromTitles($pages); } }
public function execute() { $params = $this->extractRequestParams(); if (is_null($params['lang'])) { $langObj = $this->getLanguage(); } elseif (!Language::isValidCode($params['lang'])) { $this->dieUsage('Invalid language code for parameter lang', 'invalidlang'); } else { $langObj = Language::factory($params['lang']); } if ($params['enableparser']) { if (!is_null($params['title'])) { $title = Title::newFromText($params['title']); if (!$title || $title->isExternal()) { $this->dieUsageMsg(['invalidtitle', $params['title']]); } } else { $title = Title::newFromText('API'); } } $prop = array_flip((array) $params['prop']); // Determine which messages should we print if (in_array('*', $params['messages'])) { $message_names = Language::getMessageKeysFor($langObj->getCode()); if ($params['includelocal']) { $message_names = array_unique(array_merge($message_names, MessageCache::singleton()->getAllMessageKeys($this->getConfig()->get('LanguageCode')))); } sort($message_names); $messages_target = $message_names; } else { $messages_target = $params['messages']; } // Filter messages that have the specified prefix // Because we sorted the message array earlier, they will appear in a clump: if (isset($params['prefix'])) { $skip = false; $messages_filtered = []; foreach ($messages_target as $message) { // === 0: must be at beginning of string (position 0) if (strpos($message, $params['prefix']) === 0) { if (!$skip) { $skip = true; } $messages_filtered[] = $message; } elseif ($skip) { break; } } $messages_target = $messages_filtered; } // Filter messages that contain specified string if (isset($params['filter'])) { $messages_filtered = []; foreach ($messages_target as $message) { // !== is used because filter can be at the beginning of the string if (strpos($message, $params['filter']) !== false) { $messages_filtered[] = $message; } } $messages_target = $messages_filtered; } // Whether we have any sort of message customisation filtering $customiseFilterEnabled = $params['customised'] !== 'all'; if ($customiseFilterEnabled) { global $wgContLang; $customisedMessages = AllMessagesTablePager::getCustomisedStatuses(array_map([$langObj, 'ucfirst'], $messages_target), $langObj->getCode(), !$langObj->equals($wgContLang)); $customised = $params['customised'] === 'modified'; } // Get all requested messages and print the result $skip = !is_null($params['from']); $useto = !is_null($params['to']); $result = $this->getResult(); foreach ($messages_target as $message) { // Skip all messages up to $params['from'] if ($skip && $message === $params['from']) { $skip = false; } if ($useto && $message > $params['to']) { break; } if (!$skip) { $a = ['name' => $message, 'normalizedname' => MessageCache::normalizeKey($message)]; $args = []; if (isset($params['args']) && count($params['args']) != 0) { $args = $params['args']; } if ($customiseFilterEnabled) { $messageIsCustomised = isset($customisedMessages['pages'][$langObj->ucfirst($message)]); if ($customised === $messageIsCustomised) { if ($customised) { $a['customised'] = true; } } else { continue; } } $msg = wfMessage($message, $args)->inLanguage($langObj); if (!$msg->exists()) { $a['missing'] = true; } else { // Check if the parser is enabled: if ($params['enableparser']) { $msgString = $msg->title($title)->text(); } else { $msgString = $msg->plain(); } if (!$params['nocontent']) { ApiResult::setContentValue($a, 'content', $msgString); } if (isset($prop['default'])) { $default = wfMessage($message)->inLanguage($langObj)->useDatabase(false); if (!$default->exists()) { $a['defaultmissing'] = true; } elseif ($default->plain() != $msgString) { $a['default'] = $default->plain(); } } } $fit = $result->addValue(['query', $this->getModuleName()], null, $a); if (!$fit) { $this->setContinueEnumParameter('from', $message); break; } } } $result->addIndexedTagName(['query', $this->getModuleName()], 'message'); }
/** * Replace the result data with the information about an exception. * Returns the error code * @param Exception $e * @return string */ protected function substituteResultWithError($e) { $result = $this->getResult(); $config = $this->getConfig(); if ($e instanceof UsageException) { // User entered incorrect parameters - generate error response $errMessage = $e->getMessageArray(); $link = wfExpandUrl(wfScript('api')); ApiResult::setContentValue($errMessage, 'docref', "See {$link} for API usage"); } else { // Something is seriously wrong if ($e instanceof DBQueryError && !$config->get('ShowSQLErrors')) { $info = 'Database query error'; } else { $info = "Exception Caught: {$e->getMessage()}"; } $errMessage = array('code' => 'internal_api_error_' . get_class($e), 'info' => '[' . MWExceptionHandler::getLogId($e) . '] ' . $info); if ($config->get('ShowExceptionDetails')) { ApiResult::setContentValue($errMessage, 'trace', MWExceptionHandler::getRedactedTraceAsString($e)); } } // Remember all the warnings to re-add them later $warnings = $result->getResultData(array('warnings')); $result->reset(); // Re-add the id $requestid = $this->getParameter('requestid'); if (!is_null($requestid)) { $result->addValue(null, 'requestid', $requestid, ApiResult::NO_SIZE_CHECK); } if ($config->get('ShowHostnames')) { // servedby is especially useful when debugging errors $result->addValue(null, 'servedby', wfHostName(), ApiResult::NO_SIZE_CHECK); } if ($warnings !== null) { $result->addValue(null, 'warnings', $warnings, ApiResult::NO_SIZE_CHECK); } $result->addValue(null, 'error', $errMessage, ApiResult::NO_SIZE_CHECK); return $errMessage['code']; }
/** * @covers ApiResult */ public function testInstanceDataMethods() { $result = new ApiResult(8388608); $result->addValue(null, 'setValue', '1'); $result->addValue(null, null, 'unnamed 1'); $result->addValue(null, null, 'unnamed 2'); $result->addValue(null, 'deleteValue', '2'); $result->removeValue(null, 'deleteValue'); $result->addValue(['a', 'b'], 'deleteValue', '3'); $result->removeValue(['a', 'b', 'deleteValue'], null, '3'); $result->addContentValue(null, 'setContentValue', '3'); $this->assertSame(['setValue' => '1', 'unnamed 1', 'unnamed 2', 'a' => ['b' => []], 'setContentValue' => '3', ApiResult::META_TYPE => 'assoc', ApiResult::META_CONTENT => 'setContentValue'], $result->getResultData()); $this->assertSame(20, $result->getSize()); try { $result->addValue(null, 'setValue', '99'); $this->fail('Expected exception not thrown'); } catch (RuntimeException $ex) { $this->assertSame('Attempting to add element setValue=99, existing value is 1', $ex->getMessage(), 'Expected exception'); } try { $result->addContentValue(null, 'setContentValue2', '99'); $this->fail('Expected exception not thrown'); } catch (RuntimeException $ex) { $this->assertSame('Attempting to set content element as setContentValue2 when setContentValue ' . 'is already set as the content element', $ex->getMessage(), 'Expected exception'); } $result->addValue(null, 'setValue', '99', ApiResult::OVERRIDE); $this->assertSame('99', $result->getResultData(['setValue'])); $result->addContentValue(null, 'setContentValue2', '99', ApiResult::OVERRIDE); $this->assertSame('setContentValue2', $result->getResultData([ApiResult::META_CONTENT])); $result->reset(); $this->assertSame([ApiResult::META_TYPE => 'assoc'], $result->getResultData()); $this->assertSame(0, $result->getSize()); $result->addValue(null, 'foo', 1); $result->addValue(null, 'bar', 1); $result->addValue(null, 'top', '2', ApiResult::ADD_ON_TOP); $result->addValue(null, null, '2', ApiResult::ADD_ON_TOP); $result->addValue(null, 'bottom', '2'); $result->addValue(null, 'foo', '2', ApiResult::OVERRIDE); $result->addValue(null, 'bar', '2', ApiResult::OVERRIDE | ApiResult::ADD_ON_TOP); $this->assertSame([0, 'top', 'foo', 'bar', 'bottom', ApiResult::META_TYPE], array_keys($result->getResultData())); $result->reset(); $result->addValue(null, 'foo', ['bar' => 1]); $result->addValue(['foo', 'top'], 'x', 2, ApiResult::ADD_ON_TOP); $result->addValue(['foo', 'bottom'], 'x', 2); $this->assertSame(['top', 'bar', 'bottom'], array_keys($result->getResultData(['foo']))); $result->reset(); $result->addValue(null, 'sub', ['foo' => 1]); $result->addValue(null, 'sub', ['bar' => 1]); $this->assertSame(['sub' => ['foo' => 1, 'bar' => 1], ApiResult::META_TYPE => 'assoc'], $result->getResultData()); try { $result->addValue(null, 'sub', ['foo' => 2, 'baz' => 2]); $this->fail('Expected exception not thrown'); } catch (RuntimeException $ex) { $this->assertSame('Conflicting keys (foo) when attempting to merge element sub', $ex->getMessage(), 'Expected exception'); } $result->reset(); $title = Title::newFromText("MediaWiki:Foobar"); $obj = new stdClass(); $obj->foo = 1; $obj->bar = 2; $result->addValue(null, 'title', $title); $result->addValue(null, 'obj', $obj); $this->assertSame(['title' => (string) $title, 'obj' => ['foo' => 1, 'bar' => 2, ApiResult::META_TYPE => 'assoc'], ApiResult::META_TYPE => 'assoc'], $result->getResultData()); $fh = tmpfile(); try { $result->addValue(null, 'file', $fh); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add resource(stream) to ApiResult', $ex->getMessage(), 'Expected exception'); } try { $result->addValue(null, null, $fh); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add resource(stream) to ApiResult', $ex->getMessage(), 'Expected exception'); } try { $obj->file = $fh; $result->addValue(null, 'sub', $obj); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add resource(stream) to ApiResult', $ex->getMessage(), 'Expected exception'); } try { $obj->file = $fh; $result->addValue(null, null, $obj); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add resource(stream) to ApiResult', $ex->getMessage(), 'Expected exception'); } fclose($fh); try { $result->addValue(null, 'inf', INF); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add non-finite floats to ApiResult', $ex->getMessage(), 'Expected exception'); } try { $result->addValue(null, null, INF); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add non-finite floats to ApiResult', $ex->getMessage(), 'Expected exception'); } try { $result->addValue(null, 'nan', NAN); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add non-finite floats to ApiResult', $ex->getMessage(), 'Expected exception'); } try { $result->addValue(null, null, NAN); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add non-finite floats to ApiResult', $ex->getMessage(), 'Expected exception'); } $result->addValue(null, null, NAN, ApiResult::NO_VALIDATE); try { $result->addValue(null, null, NAN, ApiResult::NO_SIZE_CHECK); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Cannot add non-finite floats to ApiResult', $ex->getMessage(), 'Expected exception'); } $result->reset(); $result->addParsedLimit('foo', 12); $this->assertSame(['limits' => ['foo' => 12], ApiResult::META_TYPE => 'assoc'], $result->getResultData()); $result->addParsedLimit('foo', 13); $this->assertSame(['limits' => ['foo' => 13], ApiResult::META_TYPE => 'assoc'], $result->getResultData()); $this->assertSame(null, $result->getResultData(['foo', 'bar', 'baz'])); $this->assertSame(13, $result->getResultData(['limits', 'foo'])); try { $result->getResultData(['limits', 'foo', 'bar']); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertSame('Path limits.foo is not an array', $ex->getMessage(), 'Expected exception'); } // Add two values and some metadata, but ensure metadata is not counted $result = new ApiResult(100); $obj = ['attr' => '12345']; ApiResult::setContentValue($obj, 'content', '1234567890'); $this->assertTrue($result->addValue(null, 'foo', $obj)); $this->assertSame(15, $result->getSize()); $result = new ApiResult(10); $formatter = new ApiErrorFormatter($result, Language::factory('en'), 'none', false); $result->setErrorFormatter($formatter); $this->assertFalse($result->addValue(null, 'foo', '12345678901')); $this->assertTrue($result->addValue(null, 'foo', '12345678901', ApiResult::NO_SIZE_CHECK)); $this->assertSame(0, $result->getSize()); $result->reset(); $this->assertTrue($result->addValue(null, 'foo', '1234567890')); $this->assertFalse($result->addValue(null, 'foo', '1')); $result->removeValue(null, 'foo'); $this->assertTrue($result->addValue(null, 'foo', '1')); $result = new ApiResult(10); $obj = new ApiResultTestSerializableObject('ok'); $obj->foobar = 'foobaz'; $this->assertTrue($result->addValue(null, 'foo', $obj)); $this->assertSame(2, $result->getSize()); $result = new ApiResult(8388608); $result2 = new ApiResult(8388608); $result2->addValue(null, 'foo', 'bar'); $result->addValue(null, 'baz', $result2); $this->assertSame(['baz' => ['foo' => 'bar', ApiResult::META_TYPE => 'assoc'], ApiResult::META_TYPE => 'assoc'], $result->getResultData()); $result = new ApiResult(8388608); $result->addValue(null, 'foo', "foo�bar"); $result->addValue(null, 'bar', "á"); $result->addValue(null, 'baz', 74); $result->addValue(null, null, "foo�bar"); $result->addValue(null, null, "á"); $this->assertSame(['foo' => "foo�bar", 'bar' => "á", 'baz' => 74, 0 => "foo�bar", 1 => "á", ApiResult::META_TYPE => 'assoc'], $result->getResultData()); $result = new ApiResult(8388608); $obj = new stdClass(); $obj->{'1'} = 'one'; $arr = []; $result->addValue($arr, 'foo', $obj); $this->assertSame(['foo' => [1 => 'one', ApiResult::META_TYPE => 'assoc'], ApiResult::META_TYPE => 'assoc'], $result->getResultData()); }