Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
80.51% covered (warning)
80.51%
95 / 118
50.00% covered (danger)
50.00%
4 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiRevisionDelete
80.51% covered (warning)
80.51%
95 / 118
50.00% covered (danger)
50.00%
4 / 8
35.23
0.00% covered (danger)
0.00%
0 / 1
 execute
84.38% covered (warning)
84.38%
54 / 64
0.00% covered (danger)
0.00%
0 / 1
20.38
 extractStatusInfo
80.00% covered (warning)
80.00%
8 / 10
0.00% covered (danger)
0.00%
0 / 1
4.13
 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%
30 / 30
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 / 10
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 © 2013 Wikimedia Foundation and contributors
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 * @since 1.23
22 */
23
24use MediaWiki\Revision\RevisionRecord;
25use MediaWiki\Status\Status;
26use MediaWiki\Title\Title;
27use Wikimedia\ParamValidator\ParamValidator;
28
29/**
30 * API interface to RevDel. The API equivalent of Special:RevisionDelete.
31 * Requires API write mode to be enabled.
32 *
33 * @ingroup API
34 */
35class ApiRevisionDelete extends ApiBase {
36
37    public function execute() {
38        $this->useTransactionalTimeLimit();
39
40        $params = $this->extractRequestParams();
41        $user = $this->getUser();
42        $this->checkUserRightsAny( RevisionDeleter::getRestriction( $params['type'] ) );
43
44        if ( !$params['ids'] ) {
45            $this->dieWithError( [ 'apierror-paramempty', 'ids' ], 'paramempty_ids' );
46        }
47
48        // Check if user can add tags
49        if ( $params['tags'] ) {
50            $ableToTag = ChangeTags::canAddTagsAccompanyingChange( $params['tags'], $this->getAuthority() );
51            if ( !$ableToTag->isOK() ) {
52                $this->dieStatus( $ableToTag );
53            }
54        }
55
56        $hide = $params['hide'] ?: [];
57        $show = $params['show'] ?: [];
58        if ( array_intersect( $hide, $show ) ) {
59            $this->dieWithError( 'apierror-revdel-mutuallyexclusive', 'badparams' );
60        } elseif ( !$hide && !$show ) {
61            $this->dieWithError( 'apierror-revdel-paramneeded', 'badparams' );
62        }
63        $bits = [
64            'content' => RevisionDeleter::getRevdelConstant( $params['type'] ),
65            'comment' => RevisionRecord::DELETED_COMMENT,
66            'user' => RevisionRecord::DELETED_USER,
67        ];
68        $bitfield = [];
69        foreach ( $bits as $key => $bit ) {
70            if ( in_array( $key, $hide ) ) {
71                $bitfield[$bit] = 1;
72            } elseif ( in_array( $key, $show ) ) {
73                $bitfield[$bit] = 0;
74            } else {
75                $bitfield[$bit] = -1;
76            }
77        }
78
79        if ( $params['suppress'] === 'yes' ) {
80            $this->checkUserRightsAny( 'suppressrevision' );
81            $bitfield[RevisionRecord::DELETED_RESTRICTED] = 1;
82        } elseif ( $params['suppress'] === 'no' ) {
83            $bitfield[RevisionRecord::DELETED_RESTRICTED] = 0;
84        } else {
85            $bitfield[RevisionRecord::DELETED_RESTRICTED] = -1;
86        }
87
88        $targetObj = null;
89        if ( $params['target'] ) {
90            $targetObj = Title::newFromText( $params['target'] );
91        }
92        $targetObj = RevisionDeleter::suggestTarget( $params['type'], $targetObj, $params['ids'] );
93        if ( $targetObj === null ) {
94            $this->dieWithError( [ 'apierror-revdel-needtarget' ], 'needtarget' );
95        }
96
97        // TODO: replace use of PermissionManager
98        if ( $this->getPermissionManager()->isBlockedFrom( $user, $targetObj ) ) {
99            // @phan-suppress-next-line PhanTypeMismatchArgumentNullable Block is checked and not null
100            $this->dieBlocked( $user->getBlock() );
101        }
102
103        $list = RevisionDeleter::createList(
104            $params['type'], $this->getContext(), $targetObj, $params['ids']
105        );
106        $status = $list->setVisibility( [
107            'value' => $bitfield,
108            'comment' => $params['reason'] ?? '',
109            'perItemStatus' => true,
110            'tags' => $params['tags']
111        ] );
112
113        $result = $this->getResult();
114        $data = $this->extractStatusInfo( $status );
115        $data['target'] = $targetObj->getFullText();
116        $data['items'] = [];
117
118        foreach ( $status->getValue()['itemStatuses'] as $id => $s ) {
119            $data['items'][$id] = $this->extractStatusInfo( $s );
120            $data['items'][$id]['id'] = $id;
121        }
122
123        $list->reloadFromPrimary();
124        for ( $item = $list->reset(); $list->current(); $item = $list->next() ) {
125            $data['items'][$item->getId()] += $item->getApiData( $this->getResult() );
126        }
127
128        $data['items'] = array_values( $data['items'] );
129        ApiResult::setIndexedTagName( $data['items'], 'i' );
130        $result->addValue( null, $this->getModuleName(), $data );
131    }
132
133    private function extractStatusInfo( Status $status ) {
134        $ret = [
135            'status' => $status->isOK() ? 'Success' : 'Fail',
136        ];
137
138        $errors = $this->getErrorFormatter()->arrayFromStatus( $status, 'error' );
139        if ( $errors ) {
140            $ret['errors'] = $errors;
141        }
142        $warnings = $this->getErrorFormatter()->arrayFromStatus( $status, 'warning' );
143        if ( $warnings ) {
144            $ret['warnings'] = $warnings;
145        }
146
147        return $ret;
148    }
149
150    public function mustBePosted() {
151        return true;
152    }
153
154    public function isWriteMode() {
155        return true;
156    }
157
158    public function getAllowedParams() {
159        return [
160            'type' => [
161                ParamValidator::PARAM_TYPE => RevisionDeleter::getTypes(),
162                ParamValidator::PARAM_REQUIRED => true
163            ],
164            'target' => null,
165            'ids' => [
166                ParamValidator::PARAM_ISMULTI => true,
167                ParamValidator::PARAM_REQUIRED => true
168            ],
169            'hide' => [
170                ParamValidator::PARAM_TYPE => [ 'content', 'comment', 'user' ],
171                ParamValidator::PARAM_ISMULTI => true,
172            ],
173            'show' => [
174                ParamValidator::PARAM_TYPE => [ 'content', 'comment', 'user' ],
175                ParamValidator::PARAM_ISMULTI => true,
176            ],
177            'suppress' => [
178                ParamValidator::PARAM_TYPE => [ 'yes', 'no', 'nochange' ],
179                ParamValidator::PARAM_DEFAULT => 'nochange',
180            ],
181            'reason' => [
182                ParamValidator::PARAM_TYPE => 'string'
183            ],
184            'tags' => [
185                ParamValidator::PARAM_TYPE => 'tags',
186                ParamValidator::PARAM_ISMULTI => true,
187            ],
188        ];
189    }
190
191    public function needsToken() {
192        return 'csrf';
193    }
194
195    protected function getExamplesMessages() {
196        $title = Title::newMainPage()->getPrefixedText();
197        $mp = rawurlencode( $title );
198
199        return [
200            "action=revisiondelete&target={$mp}&type=revision&ids=12345&" .
201                'hide=content&token=123ABC'
202                => 'apihelp-revisiondelete-example-revision',
203            'action=revisiondelete&type=logging&ids=67890&hide=content|comment|user&' .
204                'reason=BLP%20violation&token=123ABC'
205                => 'apihelp-revisiondelete-example-log',
206        ];
207    }
208
209    public function getHelpUrls() {
210        return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Revisiondelete';
211    }
212}