Esempio n. 1
0
 public function execute()
 {
     $prop = null;
     extract($this->extractRequestParams());
     foreach ($prop as $p) {
         switch ($p) {
             case 'general':
                 global $wgSitename, $wgVersion, $wgCapitalLinks;
                 $data = array();
                 $mainPage = Title::newFromText(wfMsgForContent('mainpage'));
                 $data['mainpage'] = $mainPage->getText();
                 $data['base'] = $mainPage->getFullUrl();
                 $data['sitename'] = $wgSitename;
                 $data['generator'] = "MediaWiki {$wgVersion}";
                 $data['case'] = $wgCapitalLinks ? 'first-letter' : 'case-sensitive';
                 // 'case-insensitive' option is reserved for future
                 $this->getResult()->addValue('query', $p, $data);
                 break;
             case 'namespaces':
                 global $wgContLang;
                 $data = array();
                 foreach ($wgContLang->getFormattedNamespaces() as $ns => $title) {
                     $data[$ns] = array('id' => $ns);
                     ApiResult::setContent($data[$ns], $title);
                 }
                 ApiResult::setIndexedTagName($data, 'ns');
                 $this->getResult()->addValue('query', $p, $data);
                 break;
             default:
                 ApiBase::dieDebug(__METHOD__, "Unknown prop={$p}");
         }
     }
 }
Esempio n. 2
0
 public function execute()
 {
     if (!$this->hasAnyRoutes()) {
         $this->dieUsage('No password reset routes are available.', 'moduledisabled');
     }
     $params = $this->extractRequestParams() + ['user' => null, 'email' => null];
     $this->requireOnlyOneParameter($params, 'user', 'email');
     $passwordReset = new PasswordReset($this->getConfig(), AuthManager::singleton());
     $status = $passwordReset->isAllowed($this->getUser(), $params['capture']);
     if (!$status->isOK()) {
         $this->dieStatus(Status::wrap($status));
     }
     $status = $passwordReset->execute($this->getUser(), $params['user'], $params['email'], $params['capture']);
     if (!$status->isOK()) {
         $status->value = null;
         $this->dieStatus(Status::wrap($status));
     }
     $result = $this->getResult();
     $result->addValue(['resetpassword'], 'status', 'success');
     if ($params['capture']) {
         $passwords = $status->getValue() ?: [];
         ApiResult::setArrayType($passwords, 'kvp', 'user');
         ApiResult::setIndexedTagName($passwords, 'p');
         $result->addValue(['resetpassword'], 'passwords', $passwords);
     }
 }
Esempio n. 3
0
 /**
  * Purges the cache of a page
  */
 public function execute()
 {
     $params = $this->extractRequestParams();
     $continuationManager = new ApiContinuationManager($this, array(), array());
     $this->setContinuationManager($continuationManager);
     $forceLinkUpdate = $params['forcelinkupdate'];
     $forceRecursiveLinkUpdate = $params['forcerecursivelinkupdate'];
     $pageSet = $this->getPageSet();
     $pageSet->execute();
     $result = $pageSet->getInvalidTitlesAndRevisions();
     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'] = true;
         if ($forceLinkUpdate || $forceRecursiveLinkUpdate) {
             if (!$this->getUser()->pingLimiter('linkpurge')) {
                 $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);
                 $enableParserCache = $this->getConfig()->get('EnableParserCache');
                 $p_result = $content->getParserOutput($title, $page->getLatest(), $popts, $enableParserCache);
                 # Update the links tables
                 $updates = $content->getSecondaryDataUpdates($title, null, $forceRecursiveLinkUpdate, $p_result);
                 DataUpdate::runUpdates($updates);
                 $r['linkupdate'] = true;
                 if ($enableParserCache) {
                     $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);
     }
     $this->setContinuationManager(null);
     $continuationManager->setContinuationIntoResult($apiResult);
 }
Esempio n. 4
0
 public function execute()
 {
     $params = $this->extractRequestParams();
     $user = $this->getUrUser($params);
     $form = $this->getUserRightsPage();
     $form->setContext($this->getContext());
     $r['user'] = $user->getName();
     $r['userid'] = $user->getId();
     list($r['added'], $r['removed']) = $form->doSaveUserGroups($user, (array) $params['add'], (array) $params['remove'], $params['reason']);
     $result = $this->getResult();
     ApiResult::setIndexedTagName($r['added'], 'group');
     ApiResult::setIndexedTagName($r['removed'], 'group');
     $result->addValue(null, $this->getModuleName(), $r);
 }
Esempio n. 5
0
 public function execute()
 {
     $user = $this->getUser();
     if (!$user->isLoggedIn()) {
         $this->dieUsage('You must be logged-in to have a watchlist', 'notloggedin');
     }
     if (!$user->isAllowed('editmywatchlist')) {
         $this->dieUsage('You don\'t have permission to edit your watchlist', 'permissiondenied');
     }
     $params = $this->extractRequestParams();
     $continuationManager = new ApiContinuationManager($this, array(), array());
     $this->setContinuationManager($continuationManager);
     $pageSet = $this->getPageSet();
     // by default we use pageset to extract the page to work on.
     // title is still supported for backward compatibility
     if (!isset($params['title'])) {
         $pageSet->execute();
         $res = $pageSet->getInvalidTitlesAndRevisions(array('invalidTitles', 'special', 'missingIds', 'missingRevIds', 'interwikiTitles'));
         foreach ($pageSet->getMissingTitles() as $title) {
             $r = $this->watchTitle($title, $user, $params);
             $r['missing'] = 1;
             $res[] = $r;
         }
         foreach ($pageSet->getGoodTitles() as $title) {
             $r = $this->watchTitle($title, $user, $params);
             $res[] = $r;
         }
         ApiResult::setIndexedTagName($res, 'w');
     } else {
         // dont allow use of old title parameter with new pageset parameters.
         $extraParams = array_keys(array_filter($pageSet->extractRequestParams(), function ($x) {
             return $x !== null && $x !== false;
         }));
         if ($extraParams) {
             $p = $this->getModulePrefix();
             $this->dieUsage("The parameter {$p}title can not be used with " . implode(", ", $extraParams), 'invalidparammix');
         }
         $this->logFeatureUsage('action=watch&title');
         $title = Title::newFromText($params['title']);
         if (!$title || !$title->isWatchable()) {
             $this->dieUsageMsg(array('invalidtitle', $params['title']));
         }
         $res = $this->watchTitle($title, $user, $params, true);
     }
     $this->getResult()->addValue(null, $this->getModuleName(), $res);
     $this->setContinuationManager(null);
     $continuationManager->setContinuationIntoResult($this->getResult());
 }
Esempio n. 6
0
 public function execute()
 {
     $params = $this->extractRequestParams();
     $props = array_flip($params['prop']);
     $repos = array();
     $repoGroup = $this->getInitialisedRepoGroup();
     $repoGroup->forEachForeignRepo(function ($repo) use(&$repos, $props) {
         $repos[] = array_intersect_key($repo->getInfo(), $props);
     });
     $repos[] = array_intersect_key($repoGroup->getLocalRepo()->getInfo(), $props);
     $result = $this->getResult();
     ApiResult::setIndexedTagName($repos, 'repo');
     ApiResult::setArrayTypeRecursive($repos, 'assoc');
     ApiResult::setArrayType($repos, 'array');
     $result->addValue(array('query'), 'repos', $repos);
 }
Esempio n. 7
0
 public function execute()
 {
     $this->useTransactionalTimeLimit();
     $user = $this->getUser();
     $params = $this->extractRequestParams();
     $this->requireMaxOneParameter($params, 'namespace', 'rootpage');
     $isUpload = false;
     if (isset($params['interwikisource'])) {
         if (!$user->isAllowed('import')) {
             $this->dieUsageMsg('cantimport');
         }
         if (!isset($params['interwikipage'])) {
             $this->dieUsageMsg(array('missingparam', 'interwikipage'));
         }
         $source = ImportStreamSource::newFromInterwiki($params['interwikisource'], $params['interwikipage'], $params['fullhistory'], $params['templates']);
     } else {
         $isUpload = true;
         if (!$user->isAllowed('importupload')) {
             $this->dieUsageMsg('cantimport-upload');
         }
         $source = ImportStreamSource::newFromUpload('xml');
     }
     if (!$source->isOK()) {
         $this->dieStatus($source);
     }
     $importer = new WikiImporter($source->value, $this->getConfig());
     if (isset($params['namespace'])) {
         $importer->setTargetNamespace($params['namespace']);
     } elseif (isset($params['rootpage'])) {
         $statusRootPage = $importer->setTargetRootPage($params['rootpage']);
         if (!$statusRootPage->isGood()) {
             $this->dieStatus($statusRootPage);
         }
     }
     $reporter = new ApiImportReporter($importer, $isUpload, $params['interwikisource'], $params['summary']);
     try {
         $importer->doImport();
     } catch (Exception $e) {
         $this->dieUsageMsg(array('import-unknownerror', $e->getMessage()));
     }
     $resultData = $reporter->getData();
     $result = $this->getResult();
     ApiResult::setIndexedTagName($resultData, 'page');
     $result->addValue(null, $this->getModuleName(), $resultData);
 }
Esempio n. 8
0
 /**
  * Format a message for output
  * @param array &$res Result array
  * @param string $key Result key
  * @param Message $message
  */
 private function formatMessage(array &$res, $key, Message $message)
 {
     switch ($this->messageFormat) {
         case 'none':
             break;
         case 'wikitext':
             $res[$key] = $message->setContext($this->module)->text();
             break;
         case 'html':
             $res[$key] = $message->setContext($this->module)->parseAsBlock();
             $res[$key] = Parser::stripOuterParagraph($res[$key]);
             break;
         case 'raw':
             $res[$key] = ['key' => $message->getKey(), 'params' => $message->getParams()];
             ApiResult::setIndexedTagName($res[$key]['params'], 'param');
             break;
     }
 }
Esempio n. 9
0
 public function execute()
 {
     $pUser = $this->getUser();
     // Deny if the user is blocked and doesn't have the full 'userrights' permission.
     // This matches what Special:UserRights does for the web UI.
     if ($pUser->isBlocked() && !$pUser->isAllowed('userrights')) {
         $this->dieBlocked($pUser->getBlock());
     }
     $params = $this->extractRequestParams();
     $user = $this->getUrUser($params);
     $form = $this->getUserRightsPage();
     $form->setContext($this->getContext());
     $r['user'] = $user->getName();
     $r['userid'] = $user->getId();
     list($r['added'], $r['removed']) = $form->doSaveUserGroups($user, (array) $params['add'], (array) $params['remove'], $params['reason']);
     $result = $this->getResult();
     ApiResult::setIndexedTagName($r['added'], 'group');
     ApiResult::setIndexedTagName($r['removed'], 'group');
     $result->addValue(null, $this->getModuleName(), $r);
 }
 public function execute()
 {
     $conf = $this->getConfig();
     $params = $this->extractRequestParams();
     $props = array_flip($params['prop']);
     $repos = [];
     $repoGroup = $this->getInitialisedRepoGroup();
     $foreignTargets = $conf->get('ForeignUploadTargets');
     $repoGroup->forEachForeignRepo(function ($repo) use(&$repos, $props, $foreignTargets) {
         $repoProps = $repo->getInfo();
         $repoProps['canUpload'] = in_array($repoProps['name'], $foreignTargets);
         $repos[] = array_intersect_key($repoProps, $props);
     });
     $localInfo = $repoGroup->getLocalRepo()->getInfo();
     $localInfo['canUpload'] = $conf->get('EnableUploads');
     $repos[] = array_intersect_key($localInfo, $props);
     $result = $this->getResult();
     ApiResult::setIndexedTagName($repos, 'repo');
     ApiResult::setArrayTypeRecursive($repos, 'assoc');
     ApiResult::setArrayType($repos, 'array');
     $result->addValue(['query'], 'repos', $repos);
 }
 /**
  *
  * @param array $metadata
  * @param ApiResult $result
  * @return array
  */
 public static function processMetaData($metadata, $result)
 {
     $retval = array();
     if (is_array($metadata)) {
         foreach ($metadata as $key => $value) {
             $r = array('name' => $key);
             if (is_array($value)) {
                 $r['value'] = self::processMetaData($value, $result);
             } else {
                 $r['value'] = $value;
             }
             $retval[] = $r;
         }
     }
     ApiResult::setIndexedTagName($retval, 'metadata');
     return $retval;
 }
Esempio n. 12
0
 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);
 }
Esempio n. 13
0
 public function execute()
 {
     $params = $this->extractRequestParams();
     if (!is_null($params['prop'])) {
         $this->prop = array_flip($params['prop']);
     } else {
         $this->prop = [];
     }
     $users = (array) $params['users'];
     $goodNames = $done = [];
     $result = $this->getResult();
     // Canonicalize user names
     foreach ($users as $u) {
         $n = User::getCanonicalName($u);
         if ($n === false || $n === '') {
             $vals = ['name' => $u, 'invalid' => true];
             $fit = $result->addValue(['query', $this->getModuleName()], null, $vals);
             if (!$fit) {
                 $this->setContinueEnumParameter('users', implode('|', array_diff($users, $done)));
                 $goodNames = [];
                 break;
             }
             $done[] = $u;
         } else {
             $goodNames[] = $n;
         }
     }
     $result = $this->getResult();
     if (count($goodNames)) {
         $this->addTables('user');
         $this->addFields(User::selectFields());
         $this->addWhereFld('user_name', $goodNames);
         $this->showHiddenUsersAddBlockInfo(isset($this->prop['blockinfo']));
         $data = [];
         $res = $this->select(__METHOD__);
         $this->resetQueryParams();
         // get user groups if needed
         if (isset($this->prop['groups']) || isset($this->prop['rights'])) {
             $userGroups = [];
             $this->addTables('user');
             $this->addWhereFld('user_name', $goodNames);
             $this->addTables('user_groups');
             $this->addJoinConds(['user_groups' => ['INNER JOIN', 'ug_user=user_id']]);
             $this->addFields(['user_name', 'ug_group']);
             $userGroupsRes = $this->select(__METHOD__);
             foreach ($userGroupsRes as $row) {
                 $userGroups[$row->user_name][] = $row->ug_group;
             }
         }
         foreach ($res as $row) {
             // create user object and pass along $userGroups if set
             // that reduces the number of database queries needed in User dramatically
             if (!isset($userGroups)) {
                 $user = User::newFromRow($row);
             } else {
                 if (!isset($userGroups[$row->user_name]) || !is_array($userGroups[$row->user_name])) {
                     $userGroups[$row->user_name] = [];
                 }
                 $user = User::newFromRow($row, ['user_groups' => $userGroups[$row->user_name]]);
             }
             $name = $user->getName();
             $data[$name]['userid'] = $user->getId();
             $data[$name]['name'] = $name;
             if (isset($this->prop['editcount'])) {
                 $data[$name]['editcount'] = $user->getEditCount();
             }
             if (isset($this->prop['registration'])) {
                 $data[$name]['registration'] = wfTimestampOrNull(TS_ISO_8601, $user->getRegistration());
             }
             if (isset($this->prop['groups'])) {
                 $data[$name]['groups'] = $user->getEffectiveGroups();
             }
             if (isset($this->prop['implicitgroups'])) {
                 $data[$name]['implicitgroups'] = $user->getAutomaticGroups();
             }
             if (isset($this->prop['rights'])) {
                 $data[$name]['rights'] = $user->getRights();
             }
             if ($row->ipb_deleted) {
                 $data[$name]['hidden'] = true;
             }
             if (isset($this->prop['blockinfo']) && !is_null($row->ipb_by_text)) {
                 $data[$name]['blockid'] = (int) $row->ipb_id;
                 $data[$name]['blockedby'] = $row->ipb_by_text;
                 $data[$name]['blockedbyid'] = (int) $row->ipb_by;
                 $data[$name]['blockedtimestamp'] = wfTimestamp(TS_ISO_8601, $row->ipb_timestamp);
                 $data[$name]['blockreason'] = $row->ipb_reason;
                 $data[$name]['blockexpiry'] = $row->ipb_expiry;
             }
             if (isset($this->prop['emailable'])) {
                 $data[$name]['emailable'] = $user->canReceiveEmail();
             }
             if (isset($this->prop['gender'])) {
                 $gender = $user->getOption('gender');
                 if (strval($gender) === '') {
                     $gender = 'unknown';
                 }
                 $data[$name]['gender'] = $gender;
             }
             if (isset($this->prop['centralids'])) {
                 $data[$name] += ApiQueryUserInfo::getCentralUserInfo($this->getConfig(), $user, $params['attachedwiki']);
             }
             if (!is_null($params['token'])) {
                 $tokenFunctions = $this->getTokenFunctions();
                 foreach ($params['token'] as $t) {
                     $val = call_user_func($tokenFunctions[$t], $user);
                     if ($val === false) {
                         $this->setWarning("Action '{$t}' is not allowed for the current user");
                     } else {
                         $data[$name][$t . 'token'] = $val;
                     }
                 }
             }
         }
     }
     $context = $this->getContext();
     // Second pass: add result data to $retval
     foreach ($goodNames as $u) {
         if (!isset($data[$u])) {
             $data[$u] = ['name' => $u];
             $urPage = new UserrightsPage();
             $urPage->setContext($context);
             $iwUser = $urPage->fetchUser($u);
             if ($iwUser instanceof UserRightsProxy) {
                 $data[$u]['interwiki'] = true;
                 if (!is_null($params['token'])) {
                     $tokenFunctions = $this->getTokenFunctions();
                     foreach ($params['token'] as $t) {
                         $val = call_user_func($tokenFunctions[$t], $iwUser);
                         if ($val === false) {
                             $this->setWarning("Action '{$t}' is not allowed for the current user");
                         } else {
                             $data[$u][$t . 'token'] = $val;
                         }
                     }
                 }
             } else {
                 $data[$u]['missing'] = true;
                 if (isset($this->prop['cancreate'])) {
                     $status = MediaWiki\Auth\AuthManager::singleton()->canCreateAccount($u);
                     $data[$u]['cancreate'] = $status->isGood();
                     if (!$status->isGood()) {
                         $data[$u]['cancreateerror'] = $this->getErrorFormatter()->arrayFromStatus($status);
                     }
                 }
             }
         } else {
             if (isset($this->prop['groups']) && isset($data[$u]['groups'])) {
                 ApiResult::setArrayType($data[$u]['groups'], 'array');
                 ApiResult::setIndexedTagName($data[$u]['groups'], 'g');
             }
             if (isset($this->prop['implicitgroups']) && isset($data[$u]['implicitgroups'])) {
                 ApiResult::setArrayType($data[$u]['implicitgroups'], 'array');
                 ApiResult::setIndexedTagName($data[$u]['implicitgroups'], 'g');
             }
             if (isset($this->prop['rights']) && isset($data[$u]['rights'])) {
                 ApiResult::setArrayType($data[$u]['rights'], 'array');
                 ApiResult::setIndexedTagName($data[$u]['rights'], 'r');
             }
         }
         $fit = $result->addValue(['query', $this->getModuleName()], null, $data[$u]);
         if (!$fit) {
             $this->setContinueEnumParameter('users', implode('|', array_diff($users, $done)));
             break;
         }
         $done[] = $u;
     }
     $result->addIndexedTagName(['query', $this->getModuleName()], 'user');
 }
 /**
  * @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');
     }
 }
Esempio n. 15
0
 public function arrayFromStatus(Status $status, $type = 'error', $format = null)
 {
     if ($status->isGood() || !$status->errors) {
         return [];
     }
     $result = [];
     foreach ($status->getErrorsByType($type) as $error) {
         if ($error['message'] instanceof Message) {
             $error = ['message' => $error['message']->getKey(), 'params' => $error['message']->getParams()] + $error;
         }
         ApiResult::setIndexedTagName($error['params'], 'param');
         $result[] = $error;
     }
     ApiResult::setIndexedTagName($result, $type);
     return $result;
 }
Esempio n. 16
0
 /**
  * @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');
     }
 }
 /**
  * @dataProvider provideApiParamFormatting
  * @covers LogFormatter::formatParametersForApi
  * @covers LogFormatter::formatParameterValueForApi
  */
 public function testApiParamFormatting($key, $value, $expected)
 {
     $entry = $this->newLogEntry('param', array($key => $value));
     $formatter = LogFormatter::newFromEntry($entry);
     $formatter->setContext($this->context);
     ApiResult::setIndexedTagName($expected, 'param');
     ApiResult::setArrayType($expected, 'assoc');
     $this->assertEquals($expected, $formatter->formatParametersForApi());
 }
Esempio n. 18
0
 /**
  * Perform the actual upload. Returns a suitable result array on success;
  * dies on failure.
  *
  * @param array $warnings Array of Api upload warnings
  * @return array
  */
 protected function performUpload($warnings)
 {
     // Use comment as initial page text by default
     if (is_null($this->mParams['text'])) {
         $this->mParams['text'] = $this->mParams['comment'];
     }
     /** @var $file File */
     $file = $this->mUpload->getLocalFile();
     // For preferences mode, we want to watch if 'watchdefault' is set or
     // if the *file* doesn't exist and 'watchcreations' is set. But
     // getWatchlistValue()'s automatic handling checks if the *title*
     // exists or not, so we need to check both prefs manually.
     $watch = $this->getWatchlistValue($this->mParams['watchlist'], $file->getTitle(), 'watchdefault');
     if (!$watch && $this->mParams['watchlist'] == 'preferences' && !$file->exists()) {
         $watch = $this->getWatchlistValue($this->mParams['watchlist'], $file->getTitle(), 'watchcreations');
     }
     // Deprecated parameters
     if ($this->mParams['watch']) {
         $this->logFeatureUsage('action=upload&watch');
         $watch = true;
     }
     // No errors, no warnings: do the upload
     if ($this->mParams['async']) {
         $progress = UploadBase::getSessionStatus($this->getUser(), $this->mParams['filekey']);
         if ($progress && $progress['result'] === 'Poll') {
             $this->dieUsage("Upload from stash already in progress.", 'publishfailed');
         }
         UploadBase::setSessionStatus($this->getUser(), $this->mParams['filekey'], array('result' => 'Poll', 'stage' => 'queued', 'status' => Status::newGood()));
         JobQueueGroup::singleton()->push(new PublishStashedFileJob(Title::makeTitle(NS_FILE, $this->mParams['filename']), array('filename' => $this->mParams['filename'], 'filekey' => $this->mParams['filekey'], 'comment' => $this->mParams['comment'], 'text' => $this->mParams['text'], 'watch' => $watch, 'session' => $this->getContext()->exportSession())));
         $result['result'] = 'Poll';
         $result['stage'] = 'queued';
     } else {
         /** @var $status Status */
         $status = $this->mUpload->performUpload($this->mParams['comment'], $this->mParams['text'], $watch, $this->getUser());
         if (!$status->isGood()) {
             $error = $status->getErrorsArray();
             if (count($error) == 1 && $error[0][0] == 'async') {
                 // The upload can not be performed right now, because the user
                 // requested so
                 return array('result' => 'Queued', 'statuskey' => $error[0][1]);
             }
             ApiResult::setIndexedTagName($error, 'error');
             $this->dieUsage('An internal error occurred', 'internal-error', 0, $error);
         }
         $result['result'] = 'Success';
     }
     $result['filename'] = $file->getName();
     if ($warnings && count($warnings) > 0) {
         $result['warnings'] = $warnings;
     }
     return $result;
 }
Esempio n. 19
0
 /**
  * Perform the actual upload. Returns a suitable result array on success;
  * dies on failure.
  *
  * @param array $warnings Array of Api upload warnings
  * @return array
  */
 protected function performUpload($warnings)
 {
     // Use comment as initial page text by default
     if (is_null($this->mParams['text'])) {
         $this->mParams['text'] = $this->mParams['comment'];
     }
     /** @var $file File */
     $file = $this->mUpload->getLocalFile();
     // For preferences mode, we want to watch if 'watchdefault' is set,
     // or if the *file* doesn't exist, and either 'watchuploads' or
     // 'watchcreations' is set. But getWatchlistValue()'s automatic
     // handling checks if the *title* exists or not, so we need to check
     // all three preferences manually.
     $watch = $this->getWatchlistValue($this->mParams['watchlist'], $file->getTitle(), 'watchdefault');
     if (!$watch && $this->mParams['watchlist'] == 'preferences' && !$file->exists()) {
         $watch = $this->getWatchlistValue('preferences', $file->getTitle(), 'watchuploads') || $this->getWatchlistValue('preferences', $file->getTitle(), 'watchcreations');
     }
     // Deprecated parameters
     if ($this->mParams['watch']) {
         $watch = true;
     }
     if ($this->mParams['tags']) {
         $status = ChangeTags::canAddTagsAccompanyingChange($this->mParams['tags'], $this->getUser());
         if (!$status->isOK()) {
             $this->dieStatus($status);
         }
     }
     // No errors, no warnings: do the upload
     if ($this->mParams['async']) {
         $progress = UploadBase::getSessionStatus($this->getUser(), $this->mParams['filekey']);
         if ($progress && $progress['result'] === 'Poll') {
             $this->dieUsage('Upload from stash already in progress.', 'publishfailed');
         }
         UploadBase::setSessionStatus($this->getUser(), $this->mParams['filekey'], ['result' => 'Poll', 'stage' => 'queued', 'status' => Status::newGood()]);
         JobQueueGroup::singleton()->push(new PublishStashedFileJob(Title::makeTitle(NS_FILE, $this->mParams['filename']), ['filename' => $this->mParams['filename'], 'filekey' => $this->mParams['filekey'], 'comment' => $this->mParams['comment'], 'tags' => $this->mParams['tags'], 'text' => $this->mParams['text'], 'watch' => $watch, 'session' => $this->getContext()->exportSession()]));
         $result['result'] = 'Poll';
         $result['stage'] = 'queued';
     } else {
         /** @var $status Status */
         $status = $this->mUpload->performUpload($this->mParams['comment'], $this->mParams['text'], $watch, $this->getUser(), $this->mParams['tags']);
         if (!$status->isGood()) {
             $error = $status->getErrorsArray();
             ApiResult::setIndexedTagName($error, 'error');
             $this->dieUsage('An internal error occurred', 'internal-error', 0, $error);
         }
         $result['result'] = 'Success';
     }
     $result['filename'] = $file->getName();
     if ($warnings && count($warnings) > 0) {
         $result['warnings'] = $warnings;
     }
     return $result;
 }
Esempio n. 20
0
 private function extractRowInfo($row)
 {
     $logEntry = DatabaseLogEntry::newFromRow($row);
     $vals = array(ApiResult::META_TYPE => 'assoc');
     $anyHidden = false;
     $user = $this->getUser();
     if ($this->fld_ids) {
         $vals['logid'] = intval($row->log_id);
     }
     if ($this->fld_title || $this->fld_parsedcomment) {
         $title = Title::makeTitle($row->log_namespace, $row->log_title);
     }
     if ($this->fld_title || $this->fld_ids || $this->fld_details && $row->log_params !== '') {
         if (LogEventsList::isDeleted($row, LogPage::DELETED_ACTION)) {
             $vals['actionhidden'] = true;
             $anyHidden = true;
         }
         if (LogEventsList::userCan($row, LogPage::DELETED_ACTION, $user)) {
             if ($this->fld_title) {
                 ApiQueryBase::addTitleInfo($vals, $title);
             }
             if ($this->fld_ids) {
                 $vals['pageid'] = intval($row->page_id);
                 $vals['logpage'] = intval($row->log_page);
             }
             if ($this->fld_details) {
                 $vals['params'] = LogFormatter::newFromEntry($logEntry)->formatParametersForApi();
             }
         }
     }
     if ($this->fld_type) {
         $vals['type'] = $row->log_type;
         $vals['action'] = $row->log_action;
     }
     if ($this->fld_user || $this->fld_userid) {
         if (LogEventsList::isDeleted($row, LogPage::DELETED_USER)) {
             $vals['userhidden'] = true;
             $anyHidden = true;
         }
         if (LogEventsList::userCan($row, LogPage::DELETED_USER, $user)) {
             if ($this->fld_user) {
                 $vals['user'] = $row->user_name === null ? $row->log_user_text : $row->user_name;
             }
             if ($this->fld_userid) {
                 $vals['userid'] = intval($row->log_user);
             }
             if (!$row->log_user) {
                 $vals['anon'] = true;
             }
         }
     }
     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'] = true;
             $anyHidden = true;
         }
         if (LogEventsList::userCan($row, LogPage::DELETED_COMMENT, $user)) {
             if ($this->fld_comment) {
                 $vals['comment'] = $row->log_comment;
             }
             if ($this->fld_parsedcomment) {
                 $vals['parsedcomment'] = Linker::formatComment($row->log_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();
         }
     }
     if ($anyHidden && LogEventsList::isDeleted($row, LogPage::DELETED_RESTRICTED)) {
         $vals['suppressed'] = true;
     }
     return $vals;
 }
Esempio n. 21
0
 /**
  * Add a sub-element under the page element with the given page ID
  * @param int $pageId Page ID
  * @param array $data Data array à la ApiResult
  * @return bool Whether the element fit in the result
  */
 protected function addPageSubItems($pageId, $data)
 {
     $result = $this->getResult();
     ApiResult::setIndexedTagName($data, $this->getModulePrefix());
     return $result->addValue(array('query', 'pages', intval($pageId)), $this->getModuleName(), $data);
 }
 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;
 }
Esempio n. 23
0
 /**
  * Format parameters for API output
  *
  * The result array should generally map named keys to values. Index and
  * type should be omitted, e.g. "4::foo" should be returned as "foo" in the
  * output. Values should generally be unformatted.
  *
  * Renames or removals of keys besides from the legacy numeric format to
  * modern named style should be avoided. Any renames should be announced to
  * the mediawiki-api-announce mailing list.
  *
  * @since 1.25
  * @return array
  */
 public function formatParametersForApi()
 {
     $logParams = array();
     foreach ($this->getParametersForApi() as $key => $value) {
         $vals = explode(':', $key, 3);
         if (count($vals) !== 3) {
             $logParams[$key] = $value;
             continue;
         }
         $logParams += $this->formatParameterValueForApi($vals[2], $vals[1], $value);
     }
     ApiResult::setIndexedTagName($logParams, 'param');
     ApiResult::setArrayType($logParams, 'assoc');
     return $logParams;
 }
Esempio n. 24
0
 private function setIndexedTagNames(&$array, $mapping)
 {
     foreach ($mapping as $key => $name) {
         if (isset($array[$key])) {
             ApiResult::setIndexedTagName($array[$key], $name);
         }
     }
 }
Esempio n. 25
0
 /**
  * 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;
 }
Esempio n. 26
0
 public function execute()
 {
     $user = $this->getUser();
     $params = $this->extractRequestParams();
     if (is_null($params['text']) && is_null($params['appendtext']) && is_null($params['prependtext']) && $params['undo'] == 0) {
         $this->dieUsageMsg('missingtext');
     }
     $pageObj = $this->getTitleOrPageId($params);
     $titleObj = $pageObj->getTitle();
     $apiResult = $this->getResult();
     if ($params['redirect']) {
         if ($params['prependtext'] === null && $params['appendtext'] === null && $params['section'] !== 'new') {
             $this->dieUsage('You have attempted to edit using the "redirect"-following' . ' mode, which must be used in conjuction with section=new, prependtext' . ', or appendtext.', 'redirect-appendonly');
         }
         if ($titleObj->isRedirect()) {
             $oldTitle = $titleObj;
             $titles = Revision::newFromTitle($oldTitle, false, Revision::READ_LATEST)->getContent(Revision::FOR_THIS_USER, $user)->getRedirectChain();
             // array_shift( $titles );
             $redirValues = array();
             /** @var $newTitle Title */
             foreach ($titles as $id => $newTitle) {
                 if (!isset($titles[$id - 1])) {
                     $titles[$id - 1] = $oldTitle;
                 }
                 $redirValues[] = array('from' => $titles[$id - 1]->getPrefixedText(), 'to' => $newTitle->getPrefixedText());
                 $titleObj = $newTitle;
             }
             ApiResult::setIndexedTagName($redirValues, 'r');
             $apiResult->addValue(null, 'redirects', $redirValues);
             // Since the page changed, update $pageObj
             $pageObj = WikiPage::factory($titleObj);
         }
     }
     if (!isset($params['contentmodel']) || $params['contentmodel'] == '') {
         $contentHandler = $pageObj->getContentHandler();
     } else {
         $contentHandler = ContentHandler::getForModelID($params['contentmodel']);
     }
     $name = $titleObj->getPrefixedDBkey();
     $model = $contentHandler->getModelID();
     if ($contentHandler->supportsDirectApiEditing() === false) {
         $this->dieUsage("Direct editing via API is not supported for content model {$model} used by {$name}", 'no-direct-editing');
     }
     if (!isset($params['contentformat']) || $params['contentformat'] == '') {
         $params['contentformat'] = $contentHandler->getDefaultFormat();
     }
     $contentFormat = $params['contentformat'];
     if (!$contentHandler->isSupportedFormat($contentFormat)) {
         $this->dieUsage("The requested format {$contentFormat} is not supported for content model " . " {$model} used by {$name}", 'badformat');
     }
     if ($params['createonly'] && $titleObj->exists()) {
         $this->dieUsageMsg('createonly-exists');
     }
     if ($params['nocreate'] && !$titleObj->exists()) {
         $this->dieUsageMsg('nocreate-missing');
     }
     // Now let's check whether we're even allowed to do this
     $errors = $titleObj->getUserPermissionsErrors('edit', $user);
     if (!$titleObj->exists()) {
         $errors = array_merge($errors, $titleObj->getUserPermissionsErrors('create', $user));
     }
     if (count($errors)) {
         if (is_array($errors[0])) {
             switch ($errors[0][0]) {
                 case 'blockedtext':
                     $this->dieUsage('You have been blocked from editing', 'blocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($user->getBlock())));
                     break;
                 case 'autoblockedtext':
                     $this->dieUsage('Your IP address has been blocked automatically, because it was used by a blocked user', 'autoblocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($user->getBlock())));
                     break;
                 default:
                     $this->dieUsageMsg($errors[0]);
             }
         } else {
             $this->dieUsageMsg($errors[0]);
         }
     }
     $toMD5 = $params['text'];
     if (!is_null($params['appendtext']) || !is_null($params['prependtext'])) {
         $content = $pageObj->getContent();
         if (!$content) {
             if ($titleObj->getNamespace() == NS_MEDIAWIKI) {
                 # If this is a MediaWiki:x message, then load the messages
                 # and return the message value for x.
                 $text = $titleObj->getDefaultMessageText();
                 if ($text === false) {
                     $text = '';
                 }
                 try {
                     $content = ContentHandler::makeContent($text, $this->getTitle());
                 } catch (MWContentSerializationException $ex) {
                     $this->dieUsage($ex->getMessage(), 'parseerror');
                     return;
                 }
             } else {
                 # Otherwise, make a new empty content.
                 $content = $contentHandler->makeEmptyContent();
             }
         }
         // @todo Add support for appending/prepending to the Content interface
         if (!$content instanceof TextContent) {
             $mode = $contentHandler->getModelID();
             $this->dieUsage("Can't append to pages using content model {$mode}", 'appendnotsupported');
         }
         if (!is_null($params['section'])) {
             if (!$contentHandler->supportsSections()) {
                 $modelName = $contentHandler->getModelID();
                 $this->dieUsage("Sections are not supported for this content model: {$modelName}.", 'sectionsnotsupported');
             }
             if ($params['section'] == 'new') {
                 // DWIM if they're trying to prepend/append to a new section.
                 $content = null;
             } else {
                 // Process the content for section edits
                 $section = $params['section'];
                 $content = $content->getSection($section);
                 if (!$content) {
                     $this->dieUsage("There is no section {$section}.", 'nosuchsection');
                 }
             }
         }
         if (!$content) {
             $text = '';
         } else {
             $text = $content->serialize($contentFormat);
         }
         $params['text'] = $params['prependtext'] . $text . $params['appendtext'];
         $toMD5 = $params['prependtext'] . $params['appendtext'];
     }
     if ($params['undo'] > 0) {
         if ($params['undoafter'] > 0) {
             if ($params['undo'] < $params['undoafter']) {
                 list($params['undo'], $params['undoafter']) = array($params['undoafter'], $params['undo']);
             }
             $undoafterRev = Revision::newFromId($params['undoafter']);
         }
         $undoRev = Revision::newFromId($params['undo']);
         if (is_null($undoRev) || $undoRev->isDeleted(Revision::DELETED_TEXT)) {
             $this->dieUsageMsg(array('nosuchrevid', $params['undo']));
         }
         if ($params['undoafter'] == 0) {
             $undoafterRev = $undoRev->getPrevious();
         }
         if (is_null($undoafterRev) || $undoafterRev->isDeleted(Revision::DELETED_TEXT)) {
             $this->dieUsageMsg(array('nosuchrevid', $params['undoafter']));
         }
         if ($undoRev->getPage() != $pageObj->getID()) {
             $this->dieUsageMsg(array('revwrongpage', $undoRev->getID(), $titleObj->getPrefixedText()));
         }
         if ($undoafterRev->getPage() != $pageObj->getID()) {
             $this->dieUsageMsg(array('revwrongpage', $undoafterRev->getID(), $titleObj->getPrefixedText()));
         }
         $newContent = $contentHandler->getUndoContent($pageObj->getRevision(), $undoRev, $undoafterRev);
         if (!$newContent) {
             $this->dieUsageMsg('undo-failure');
         }
         $params['text'] = $newContent->serialize($params['contentformat']);
         // If no summary was given and we only undid one rev,
         // use an autosummary
         if (is_null($params['summary']) && $titleObj->getNextRevisionID($undoafterRev->getID()) == $params['undo']) {
             $params['summary'] = wfMessage('undo-summary')->params($params['undo'], $undoRev->getUserText())->inContentLanguage()->text();
         }
     }
     // See if the MD5 hash checks out
     if (!is_null($params['md5']) && md5($toMD5) !== $params['md5']) {
         $this->dieUsageMsg('hashcheckfailed');
     }
     // EditPage wants to parse its stuff from a WebRequest
     // That interface kind of sucks, but it's workable
     $requestArray = array('wpTextbox1' => $params['text'], 'format' => $contentFormat, 'model' => $contentHandler->getModelID(), 'wpEditToken' => $params['token'], 'wpIgnoreBlankSummary' => true, 'wpIgnoreBlankArticle' => true, 'wpIgnoreSelfRedirect' => true, 'bot' => $params['bot']);
     if (!is_null($params['summary'])) {
         $requestArray['wpSummary'] = $params['summary'];
     }
     if (!is_null($params['sectiontitle'])) {
         $requestArray['wpSectionTitle'] = $params['sectiontitle'];
     }
     // TODO: Pass along information from 'undoafter' as well
     if ($params['undo'] > 0) {
         $requestArray['wpUndidRevision'] = $params['undo'];
     }
     // Watch out for basetimestamp == '' or '0'
     // It gets treated as NOW, almost certainly causing an edit conflict
     if ($params['basetimestamp'] !== null && (bool) $this->getMain()->getVal('basetimestamp')) {
         $requestArray['wpEdittime'] = $params['basetimestamp'];
     } else {
         $requestArray['wpEdittime'] = $pageObj->getTimestamp();
     }
     if ($params['starttimestamp'] !== null) {
         $requestArray['wpStarttime'] = $params['starttimestamp'];
     } else {
         $requestArray['wpStarttime'] = wfTimestampNow();
         // Fake wpStartime
     }
     if ($params['minor'] || !$params['notminor'] && $user->getOption('minordefault')) {
         $requestArray['wpMinoredit'] = '';
     }
     if ($params['recreate']) {
         $requestArray['wpRecreate'] = '';
     }
     if (!is_null($params['section'])) {
         $section = $params['section'];
         if (!preg_match('/^((T-)?\\d+|new)$/', $section)) {
             $this->dieUsage("The section parameter must be a valid section id or 'new'", "invalidsection");
         }
         $content = $pageObj->getContent();
         if ($section !== '0' && $section != 'new' && (!$content || !$content->getSection($section))) {
             $this->dieUsage("There is no section {$section}.", 'nosuchsection');
         }
         $requestArray['wpSection'] = $params['section'];
     } else {
         $requestArray['wpSection'] = '';
     }
     $watch = $this->getWatchlistValue($params['watchlist'], $titleObj);
     // Deprecated parameters
     if ($params['watch']) {
         $this->logFeatureUsage('action=edit&watch');
         $watch = true;
     } elseif ($params['unwatch']) {
         $this->logFeatureUsage('action=edit&unwatch');
         $watch = false;
     }
     if ($watch) {
         $requestArray['wpWatchthis'] = '';
     }
     // Apply change tags
     if (count($params['tags'])) {
         if ($user->isAllowed('applychangetags')) {
             $requestArray['wpChangeTags'] = implode(',', $params['tags']);
         } else {
             $this->dieUsage('You don\'t have permission to set change tags.', 'taggingnotallowed');
         }
     }
     // Pass through anything else we might have been given, to support extensions
     // This is kind of a hack but it's the best we can do to make extensions work
     $requestArray += $this->getRequest()->getValues();
     global $wgTitle, $wgRequest;
     $req = new DerivativeRequest($this->getRequest(), $requestArray, true);
     // Some functions depend on $wgTitle == $ep->mTitle
     // TODO: Make them not or check if they still do
     $wgTitle = $titleObj;
     $articleContext = new RequestContext();
     $articleContext->setRequest($req);
     $articleContext->setWikiPage($pageObj);
     $articleContext->setUser($this->getUser());
     /** @var $articleObject Article */
     $articleObject = Article::newFromWikiPage($pageObj, $articleContext);
     $ep = new EditPage($articleObject);
     $ep->setApiEditOverride(true);
     $ep->setContextTitle($titleObj);
     $ep->importFormData($req);
     $content = $ep->textbox1;
     // The following is needed to give the hook the full content of the
     // new revision rather than just the current section. (Bug 52077)
     if (!is_null($params['section']) && $contentHandler->supportsSections() && $titleObj->exists()) {
         // If sectiontitle is set, use it, otherwise use the summary as the section title (for
         // backwards compatibility with old forms/bots).
         if ($ep->sectiontitle !== '') {
             $sectionTitle = $ep->sectiontitle;
         } else {
             $sectionTitle = $ep->summary;
         }
         $contentObj = $contentHandler->unserializeContent($content, $contentFormat);
         $fullContentObj = $articleObject->replaceSectionContent($params['section'], $contentObj, $sectionTitle);
         if ($fullContentObj) {
             $content = $fullContentObj->serialize($contentFormat);
         } else {
             // This most likely means we have an edit conflict which means that the edit
             // wont succeed anyway.
             $this->dieUsageMsg('editconflict');
         }
     }
     // Run hooks
     // Handle APIEditBeforeSave parameters
     $r = array();
     if (!Hooks::run('APIEditBeforeSave', array($ep, $content, &$r))) {
         if (count($r)) {
             $r['result'] = 'Failure';
             $apiResult->addValue(null, $this->getModuleName(), $r);
             return;
         }
         $this->dieUsageMsg('hookaborted');
     }
     // Do the actual save
     $oldRevId = $articleObject->getRevIdFetched();
     $result = null;
     // Fake $wgRequest for some hooks inside EditPage
     // @todo FIXME: This interface SUCKS
     $oldRequest = $wgRequest;
     $wgRequest = $req;
     $status = $ep->attemptSave($result);
     $wgRequest = $oldRequest;
     switch ($status->value) {
         case EditPage::AS_HOOK_ERROR:
         case EditPage::AS_HOOK_ERROR_EXPECTED:
             if (isset($status->apiHookResult)) {
                 $r = $status->apiHookResult;
                 $r['result'] = 'Failure';
                 $apiResult->addValue(null, $this->getModuleName(), $r);
                 return;
             } else {
                 $this->dieUsageMsg('hookaborted');
             }
         case EditPage::AS_PARSE_ERROR:
             $this->dieUsage($status->getMessage(), 'parseerror');
         case EditPage::AS_IMAGE_REDIRECT_ANON:
             $this->dieUsageMsg('noimageredirect-anon');
         case EditPage::AS_IMAGE_REDIRECT_LOGGED:
             $this->dieUsageMsg('noimageredirect-logged');
         case EditPage::AS_SPAM_ERROR:
             $this->dieUsageMsg(array('spamdetected', $result['spam']));
         case EditPage::AS_BLOCKED_PAGE_FOR_USER:
             $this->dieUsage('You have been blocked from editing', 'blocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($user->getBlock())));
         case EditPage::AS_MAX_ARTICLE_SIZE_EXCEEDED:
         case EditPage::AS_CONTENT_TOO_BIG:
             $this->dieUsageMsg(array('contenttoobig', $this->getConfig()->get('MaxArticleSize')));
         case EditPage::AS_READ_ONLY_PAGE_ANON:
             $this->dieUsageMsg('noedit-anon');
         case EditPage::AS_READ_ONLY_PAGE_LOGGED:
             $this->dieUsageMsg('noedit');
         case EditPage::AS_READ_ONLY_PAGE:
             $this->dieReadOnly();
         case EditPage::AS_RATE_LIMITED:
             $this->dieUsageMsg('actionthrottledtext');
         case EditPage::AS_ARTICLE_WAS_DELETED:
             $this->dieUsageMsg('wasdeleted');
         case EditPage::AS_NO_CREATE_PERMISSION:
             $this->dieUsageMsg('nocreate-loggedin');
         case EditPage::AS_NO_CHANGE_CONTENT_MODEL:
             $this->dieUsageMsg('cantchangecontentmodel');
         case EditPage::AS_BLANK_ARTICLE:
             $this->dieUsageMsg('blankpage');
         case EditPage::AS_CONFLICT_DETECTED:
             $this->dieUsageMsg('editconflict');
         case EditPage::AS_TEXTBOX_EMPTY:
             $this->dieUsageMsg('emptynewsection');
         case EditPage::AS_CHANGE_TAG_ERROR:
             $this->dieStatus($status);
         case EditPage::AS_SUCCESS_NEW_ARTICLE:
             $r['new'] = true;
             // fall-through
         // fall-through
         case EditPage::AS_SUCCESS_UPDATE:
             $r['result'] = 'Success';
             $r['pageid'] = intval($titleObj->getArticleID());
             $r['title'] = $titleObj->getPrefixedText();
             $r['contentmodel'] = $articleObject->getContentModel();
             $newRevId = $articleObject->getLatest();
             if ($newRevId == $oldRevId) {
                 $r['nochange'] = true;
             } else {
                 $r['oldrevid'] = intval($oldRevId);
                 $r['newrevid'] = intval($newRevId);
                 $r['newtimestamp'] = wfTimestamp(TS_ISO_8601, $pageObj->getTimestamp());
             }
             break;
         case EditPage::AS_SUMMARY_NEEDED:
             // Shouldn't happen since we set wpIgnoreBlankSummary, but just in case
             $this->dieUsageMsg('summaryrequired');
         case EditPage::AS_END:
         default:
             // $status came from WikiPage::doEdit()
             $errors = $status->getErrorsArray();
             $this->dieUsageMsg($errors[0]);
             // TODO: Add new errors to message map
             break;
     }
     $apiResult->addValue(null, $this->getModuleName(), $r);
 }
Esempio n. 27
0
 public function execute()
 {
     $this->useTransactionalTimeLimit();
     $user = $this->getUser();
     $params = $this->extractRequestParams();
     $this->requireOnlyOneParameter($params, 'from', 'fromid');
     if (isset($params['from'])) {
         $fromTitle = Title::newFromText($params['from']);
         if (!$fromTitle || $fromTitle->isExternal()) {
             $this->dieUsageMsg(array('invalidtitle', $params['from']));
         }
     } elseif (isset($params['fromid'])) {
         $fromTitle = Title::newFromID($params['fromid']);
         if (!$fromTitle) {
             $this->dieUsageMsg(array('nosuchpageid', $params['fromid']));
         }
     }
     if (!$fromTitle->exists()) {
         $this->dieUsageMsg('notanarticle');
     }
     $fromTalk = $fromTitle->getTalkPage();
     $toTitle = Title::newFromText($params['to']);
     if (!$toTitle || $toTitle->isExternal()) {
         $this->dieUsageMsg(array('invalidtitle', $params['to']));
     }
     $toTalk = $toTitle->getTalkPage();
     if ($toTitle->getNamespace() == NS_FILE && !RepoGroup::singleton()->getLocalRepo()->findFile($toTitle) && wfFindFile($toTitle)) {
         if (!$params['ignorewarnings'] && $user->isAllowed('reupload-shared')) {
             $this->dieUsageMsg('sharedfile-exists');
         } elseif (!$user->isAllowed('reupload-shared')) {
             $this->dieUsageMsg('cantoverwrite-sharedfile');
         }
     }
     // Rate limit
     if ($user->pingLimiter('move')) {
         $this->dieUsageMsg('actionthrottledtext');
     }
     // Move the page
     $toTitleExists = $toTitle->exists();
     $status = $this->movePage($fromTitle, $toTitle, $params['reason'], !$params['noredirect']);
     if (!$status->isOK()) {
         $this->dieStatus($status);
     }
     $r = array('from' => $fromTitle->getPrefixedText(), 'to' => $toTitle->getPrefixedText(), 'reason' => $params['reason']);
     //NOTE: we assume that if the old title exists, it's because it was re-created as
     // a redirect to the new title. This is not safe, but what we did before was
     // even worse: we just determined whether a redirect should have been created,
     // and reported that it was created if it should have, without any checks.
     // Also note that isRedirect() is unreliable because of bug 37209.
     $r['redirectcreated'] = $fromTitle->exists();
     $r['moveoverredirect'] = $toTitleExists;
     // Move the talk page
     if ($params['movetalk'] && $fromTalk->exists() && !$fromTitle->isTalkPage()) {
         $toTalkExists = $toTalk->exists();
         $status = $this->movePage($fromTalk, $toTalk, $params['reason'], !$params['noredirect']);
         if ($status->isOK()) {
             $r['talkfrom'] = $fromTalk->getPrefixedText();
             $r['talkto'] = $toTalk->getPrefixedText();
             $r['talkmoveoverredirect'] = $toTalkExists;
         } else {
             // We're not gonna dieUsage() on failure, since we already changed something
             $error = $this->getErrorFromStatus($status);
             $r['talkmove-error-code'] = $error[0];
             $r['talkmove-error-info'] = $error[1];
         }
     }
     $result = $this->getResult();
     // Move subpages
     if ($params['movesubpages']) {
         $r['subpages'] = $this->moveSubpages($fromTitle, $toTitle, $params['reason'], $params['noredirect']);
         ApiResult::setIndexedTagName($r['subpages'], 'subpage');
         if ($params['movetalk']) {
             $r['subpages-talk'] = $this->moveSubpages($fromTalk, $toTalk, $params['reason'], $params['noredirect']);
             ApiResult::setIndexedTagName($r['subpages-talk'], 'subpage');
         }
     }
     $watch = 'preferences';
     if (isset($params['watchlist'])) {
         $watch = $params['watchlist'];
     } elseif ($params['watch']) {
         $watch = 'watch';
         $this->logFeatureUsage('action=move&watch');
     } elseif ($params['unwatch']) {
         $watch = 'unwatch';
         $this->logFeatureUsage('action=move&unwatch');
     }
     // Watch pages
     $this->setWatch($watch, $fromTitle, 'watchmoves');
     $this->setWatch($watch, $toTitle, 'watchmoves');
     $result->addValue(null, $this->getModuleName(), $r);
 }
 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);
 }
 /**
  * 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;
 }
 /**
  * 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;
 }