3 * Copyright © 2007 Roan Kattouw <roan.kattouw@gmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
23 namespace MediaWiki\Api
;
26 use MediaWiki\Deferred\DeferredUpdates
;
27 use MediaWiki\MainConfigNames
;
28 use MediaWiki\Page\RollbackPageFactory
;
29 use MediaWiki\ParamValidator\TypeDef\UserDef
;
30 use MediaWiki\Title\Title
;
31 use MediaWiki\User\Options\UserOptionsLookup
;
32 use MediaWiki\User\UserIdentity
;
33 use MediaWiki\Watchlist\WatchlistManager
;
35 use Wikimedia\ParamValidator\ParamValidator
;
40 class ApiRollback
extends ApiBase
{
42 use ApiWatchlistTrait
;
44 private RollbackPageFactory
$rollbackPageFactory;
46 public function __construct(
49 RollbackPageFactory
$rollbackPageFactory,
50 WatchlistManager
$watchlistManager,
51 UserOptionsLookup
$userOptionsLookup
53 parent
::__construct( $mainModule, $moduleName );
54 $this->rollbackPageFactory
= $rollbackPageFactory;
56 // Variables needed in ApiWatchlistTrait trait
57 $this->watchlistExpiryEnabled
= $this->getConfig()->get( MainConfigNames
::WatchlistExpiry
);
58 $this->watchlistMaxDuration
=
59 $this->getConfig()->get( MainConfigNames
::WatchlistExpiryMaxDuration
);
60 $this->watchlistManager
= $watchlistManager;
61 $this->userOptionsLookup
= $userOptionsLookup;
67 private $mTitleObj = null;
72 private $mUser = null;
74 public function execute() {
75 $this->useTransactionalTimeLimit();
77 $user = $this->getUser();
78 $params = $this->extractRequestParams();
80 $titleObj = $this->getRbTitle( $params );
82 // If change tagging was requested, check that the user is allowed to tag,
83 // and the tags are valid. TODO: move inside rollback command?
84 if ( $params['tags'] ) {
85 $tagStatus = ChangeTags
::canAddTagsAccompanyingChange( $params['tags'], $this->getAuthority() );
86 if ( !$tagStatus->isOK() ) {
87 $this->dieStatus( $tagStatus );
91 // @TODO: remove this hack once rollback uses POST (T88044)
93 $trxLimits = $this->getConfig()->get( MainConfigNames
::TrxProfilerLimits
);
94 $trxProfiler = Profiler
::instance()->getTransactionProfiler();
95 $trxProfiler->redefineExpectations( $trxLimits['POST'], $fname );
96 DeferredUpdates
::addCallableUpdate( static function () use ( $trxProfiler, $trxLimits, $fname ) {
97 $trxProfiler->redefineExpectations( $trxLimits['PostSend-POST'], $fname );
100 $rollbackResult = $this->rollbackPageFactory
101 ->newRollbackPage( $titleObj, $this->getAuthority(), $this->getRbUser( $params ) )
102 ->setSummary( $params['summary'] )
103 ->markAsBot( $params['markbot'] )
104 ->setChangeTags( $params['tags'] )
105 ->rollbackIfAllowed();
107 if ( !$rollbackResult->isGood() ) {
108 $this->dieStatus( $rollbackResult );
111 $watch = $params['watchlist'] ??
'preferences';
112 $watchlistExpiry = $this->getExpiryFromParams( $params );
115 $this->setWatch( $watch, $titleObj, $user, 'watchrollback', $watchlistExpiry );
117 $details = $rollbackResult->getValue();
118 $currentRevisionRecord = $details['current-revision-record'];
119 $targetRevisionRecord = $details['target-revision-record'];
122 'title' => $titleObj->getPrefixedText(),
123 'pageid' => $currentRevisionRecord->getPageId(),
124 'summary' => $details['summary'],
125 'revid' => (int)$details['newid'],
126 // The revision being reverted (previously the current revision of the page)
127 'old_revid' => $currentRevisionRecord->getID(),
128 // The revision being restored (the last revision before revision(s) by the reverted user)
129 'last_revid' => $targetRevisionRecord->getID()
132 $this->getResult()->addValue( null, $this->getModuleName(), $info );
135 public function mustBePosted() {
139 public function isWriteMode() {
143 public function getAllowedParams() {
147 ParamValidator
::PARAM_TYPE
=> 'integer'
150 ParamValidator
::PARAM_TYPE
=> 'tags',
151 ParamValidator
::PARAM_ISMULTI
=> true,
154 ParamValidator
::PARAM_TYPE
=> 'user',
155 UserDef
::PARAM_ALLOWED_USER_TYPES
=> [ 'name', 'ip', 'temp', 'id', 'interwiki' ],
156 UserDef
::PARAM_RETURN_OBJECT
=> true,
157 ParamValidator
::PARAM_REQUIRED
=> true
163 // Params appear in the docs in the order they are defined,
164 // which is why this is here (we want it above the token param).
165 $params +
= $this->getWatchlistParams();
169 // Standard definition automatically inserted
170 ApiBase
::PARAM_HELP_MSG_APPEND
=> [ 'api-help-param-token-webui' ],
175 public function needsToken() {
180 * @param array $params
182 * @return UserIdentity
184 private function getRbUser( array $params ): UserIdentity
{
185 if ( $this->mUser
!== null ) {
189 $this->mUser
= $params['user'];
195 * @param array $params
199 private function getRbTitle( array $params ) {
200 if ( $this->mTitleObj
!== null ) {
201 return $this->mTitleObj
;
204 $this->requireOnlyOneParameter( $params, 'title', 'pageid' );
206 if ( isset( $params['title'] ) ) {
207 $this->mTitleObj
= Title
::newFromText( $params['title'] );
208 if ( !$this->mTitleObj ||
$this->mTitleObj
->isExternal() ) {
209 $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['title'] ) ] );
211 } elseif ( isset( $params['pageid'] ) ) {
212 $this->mTitleObj
= Title
::newFromID( $params['pageid'] );
213 if ( !$this->mTitleObj
) {
214 $this->dieWithError( [ 'apierror-nosuchpageid', $params['pageid'] ] );
218 if ( !$this->mTitleObj
->exists() ) {
219 $this->dieWithError( 'apierror-missingtitle' );
222 return $this->mTitleObj
;
225 protected function getExamplesMessages() {
226 $title = Title
::newMainPage()->getPrefixedText();
227 $mp = rawurlencode( $title );
230 "action=rollback&title={$mp}&user=Example&token=123ABC" =>
231 'apihelp-rollback-example-simple',
232 "action=rollback&title={$mp}&user=192.0.2.5&" .
233 'token=123ABC&summary=Reverting%20vandalism&markbot=1' =>
234 'apihelp-rollback-example-summary',
238 public function getHelpUrls() {
239 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Rollback';
243 /** @deprecated class alias since 1.43 */
244 class_alias( ApiRollback
::class, 'ApiRollback' );