Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
75.56% covered (warning)
75.56%
68 / 90
62.50% covered (warning)
62.50%
5 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiUndelete
76.40% covered (warning)
76.40%
68 / 89
62.50% covered (warning)
62.50%
5 / 8
29.95
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 execute
76.60% covered (warning)
76.60%
36 / 47
0.00% covered (danger)
0.00%
0 / 1
19.28
 mustBePosted
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isWriteMode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAllowedParams
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
1
 needsToken
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 getHelpUrls
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Copyright © 2007 Roan Kattouw <roan.kattouw@gmail.com>
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 */
8
9namespace MediaWiki\Api;
10
11use MediaWiki\MainConfigNames;
12use MediaWiki\Page\UndeletePage;
13use MediaWiki\Page\UndeletePageFactory;
14use MediaWiki\Page\WikiPageFactory;
15use MediaWiki\Title\Title;
16use MediaWiki\User\Options\UserOptionsLookup;
17use MediaWiki\Watchlist\WatchedItemStoreInterface;
18use MediaWiki\Watchlist\WatchlistManager;
19use Wikimedia\ParamValidator\ParamValidator;
20use Wikimedia\Rdbms\IDBAccessObject;
21use Wikimedia\Timestamp\TimestampFormat as TS;
22
23/**
24 * @ingroup API
25 */
26class ApiUndelete extends ApiBase {
27
28    use ApiWatchlistTrait;
29
30    private UndeletePageFactory $undeletePageFactory;
31    private WikiPageFactory $wikiPageFactory;
32
33    public function __construct(
34        ApiMain $mainModule,
35        string $moduleName,
36        WatchlistManager $watchlistManager,
37        WatchedItemStoreInterface $watchedItemStore,
38        UserOptionsLookup $userOptionsLookup,
39        UndeletePageFactory $undeletePageFactory,
40        WikiPageFactory $wikiPageFactory
41    ) {
42        parent::__construct( $mainModule, $moduleName );
43
44        // Variables needed in ApiWatchlistTrait trait
45        $this->watchlistExpiryEnabled = $this->getConfig()->get( MainConfigNames::WatchlistExpiry );
46        $this->watchlistMaxDuration =
47            $this->getConfig()->get( MainConfigNames::WatchlistExpiryMaxDuration );
48        $this->watchlistManager = $watchlistManager;
49        $this->watchedItemStore = $watchedItemStore;
50        $this->userOptionsLookup = $userOptionsLookup;
51        $this->undeletePageFactory = $undeletePageFactory;
52        $this->wikiPageFactory = $wikiPageFactory;
53    }
54
55    public function execute() {
56        $this->useTransactionalTimeLimit();
57
58        $params = $this->extractRequestParams();
59
60        $user = $this->getUser();
61        $block = $user->getBlock( IDBAccessObject::READ_LATEST );
62        if ( $block && $block->isSitewide() ) {
63            $this->dieBlocked( $block );
64        }
65
66        $titleObj = Title::newFromText( $params['title'] );
67        if ( !$titleObj || $titleObj->isExternal() ) {
68            $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['title'] ) ] );
69        }
70        if ( !$titleObj->canExist() ) {
71            $this->dieWithError( 'apierror-pagecannotexist' );
72        }
73
74        // Convert timestamps
75        if ( !isset( $params['timestamps'] ) ) {
76            $params['timestamps'] = [];
77        }
78        if ( !is_array( $params['timestamps'] ) ) {
79            $params['timestamps'] = [ $params['timestamps'] ];
80        }
81        foreach ( $params['timestamps'] as $i => $ts ) {
82            $params['timestamps'][$i] = wfTimestamp( TS::MW, $ts );
83        }
84
85        $undeletePage = $this->undeletePageFactory->newUndeletePage(
86                $this->wikiPageFactory->newFromTitle( $titleObj ),
87                $this->getAuthority()
88            )
89            ->setUndeleteOnlyTimestamps( $params['timestamps'] ?? [] )
90            ->setUndeleteOnlyFileVersions( $params['fileids'] ?: [] )
91            ->setTags( $params['tags'] ?: [] );
92
93        if ( $params['undeletetalk'] ) {
94            $undeletePage->setUndeleteAssociatedTalk( true );
95        }
96
97        $status = $undeletePage->undeleteIfAllowed( $params['reason'] );
98        if ( $status->isOK() ) {
99            // in case there are warnings
100            $this->addMessagesFromStatus( $status );
101        } else {
102            $this->dieStatus( $status );
103        }
104
105        $restoredRevs = $status->getValue()[UndeletePage::REVISIONS_RESTORED];
106        $restoredFiles = $status->getValue()[UndeletePage::FILES_RESTORED];
107
108        if ( $restoredRevs === 0 && $restoredFiles === 0 ) {
109            // BC for code that predates UndeletePage
110            $this->dieWithError( 'apierror-cantundelete' );
111        }
112
113        if ( $restoredFiles ) {
114            $this->getHookRunner()->onFileUndeleteComplete(
115                $titleObj, $params['fileids'],
116                $this->getUser(), $params['reason'] );
117        }
118
119        $watchlistExpiry = $this->getExpiryFromParams( $params, $titleObj, $user );
120        $this->setWatch( $params['watchlist'], $titleObj, $user, null, $watchlistExpiry );
121
122        $info = [
123            'title' => $titleObj->getPrefixedText(),
124            'revisions' => $restoredRevs,
125            'fileversions' => $restoredFiles,
126            'reason' => $params['reason']
127        ];
128        $this->getResult()->addValue( null, $this->getModuleName(), $info );
129    }
130
131    /** @inheritDoc */
132    public function mustBePosted() {
133        return true;
134    }
135
136    /** @inheritDoc */
137    public function isWriteMode() {
138        return true;
139    }
140
141    /** @inheritDoc */
142    public function getAllowedParams() {
143        return [
144            'title' => [
145                ParamValidator::PARAM_TYPE => 'string',
146                ParamValidator::PARAM_REQUIRED => true
147            ],
148            'reason' => '',
149            'tags' => [
150                ParamValidator::PARAM_TYPE => 'tags',
151                ParamValidator::PARAM_ISMULTI => true,
152            ],
153            'timestamps' => [
154                ParamValidator::PARAM_TYPE => 'timestamp',
155                ParamValidator::PARAM_ISMULTI => true,
156            ],
157            'fileids' => [
158                ParamValidator::PARAM_TYPE => 'integer',
159                ParamValidator::PARAM_ISMULTI => true,
160            ],
161            'undeletetalk' => false,
162        ] + $this->getWatchlistParams();
163    }
164
165    /** @inheritDoc */
166    public function needsToken() {
167        return 'csrf';
168    }
169
170    /** @inheritDoc */
171    protected function getExamplesMessages() {
172        $title = Title::newMainPage()->getPrefixedText();
173        $mp = rawurlencode( $title );
174
175        return [
176            "action=undelete&title={$mp}&token=123ABC&reason=Restoring%20{$mp}"
177                => 'apihelp-undelete-example-page',
178            "action=undelete&title={$mp}&token=123ABC" .
179                '&timestamps=2007-07-03T22:00:45Z|2007-07-02T19:48:56Z'
180                => 'apihelp-undelete-example-revisions',
181        ];
182    }
183
184    /** @inheritDoc */
185    public function getHelpUrls() {
186        return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Undelete';
187    }
188}
189
190/** @deprecated class alias since 1.43 */
191class_alias( ApiUndelete::class, 'ApiUndelete' );