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