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