MediaWiki master
ApiTag.php
Go to the documentation of this file.
1<?php
2
27
32class ApiTag extends ApiBase {
33
35
36 private IDatabase $dbr;
37 private RevisionStore $revisionStore;
38 private ChangeTagsStore $changeTagsStore;
39
47 public function __construct(
48 ApiMain $main,
49 $action,
50 IConnectionProvider $dbProvider,
51 RevisionStore $revisionStore,
52 ChangeTagsStore $changeTagsStore
53 ) {
54 parent::__construct( $main, $action );
55 $this->dbr = $dbProvider->getReplicaDatabase();
56 $this->revisionStore = $revisionStore;
57 $this->changeTagsStore = $changeTagsStore;
58 }
59
60 public function execute() {
61 $params = $this->extractRequestParams();
62 $user = $this->getUser();
63
64 // make sure the user is allowed
65 $this->checkUserRightsAny( 'changetags' );
66
67 // Fail early if the user is sitewide blocked.
68 $block = $user->getBlock();
69 if ( $block && $block->isSitewide() ) {
70 $this->dieBlocked( $block );
71 }
72
73 // Check if user can add tags
74 if ( $params['tags'] ) {
75 $ableToTag = ChangeTags::canAddTagsAccompanyingChange( $params['tags'], $this->getAuthority() );
76 if ( !$ableToTag->isOK() ) {
77 $this->dieStatus( $ableToTag );
78 }
79 }
80
81 // validate and process each revid, rcid and logid
82 $this->requireAtLeastOneParameter( $params, 'revid', 'rcid', 'logid' );
83 $ret = [];
84 if ( $params['revid'] ) {
85 foreach ( $params['revid'] as $id ) {
86 $ret[] = $this->processIndividual( 'revid', $params, $id );
87 }
88 }
89 if ( $params['rcid'] ) {
90 foreach ( $params['rcid'] as $id ) {
91 $ret[] = $this->processIndividual( 'rcid', $params, $id );
92 }
93 }
94 if ( $params['logid'] ) {
95 foreach ( $params['logid'] as $id ) {
96 $ret[] = $this->processIndividual( 'logid', $params, $id );
97 }
98 }
99
100 ApiResult::setIndexedTagName( $ret, 'result' );
101 $this->getResult()->addValue( null, $this->getModuleName(), $ret );
102 }
103
104 protected function validateLogId( $logid ) {
105 $result = $this->dbr->newSelectQueryBuilder()
106 ->select( 'log_id' )
107 ->from( 'logging' )
108 ->where( [ 'log_id' => $logid ] )
109 ->caller( __METHOD__ )->fetchField();
110 return (bool)$result;
111 }
112
113 protected function processIndividual( $type, $params, $id ) {
114 $user = $this->getUser();
115 $idResult = [ $type => $id ];
116
117 // validate the ID
118 $valid = false;
119 switch ( $type ) {
120 case 'rcid':
121 $valid = RecentChange::newFromId( $id );
122 // TODO: replace use of PermissionManager
123 if ( $valid && $this->getPermissionManager()->isBlockedFrom( $user, $valid->getTitle() ) ) {
124 $idResult['status'] = 'error';
125 // @phan-suppress-next-line PhanTypeMismatchArgument
126 $idResult += $this->getErrorFormatter()->formatMessage( ApiMessage::create(
127 'apierror-blocked',
128 'blocked',
129 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable Block is checked and not null
130 [ 'blockinfo' => $this->getBlockDetails( $user->getBlock() ) ]
131 ) );
132 return $idResult;
133 }
134 break;
135 case 'revid':
136 $valid = $this->revisionStore->getRevisionById( $id );
137 // TODO: replace use of PermissionManager
138 if (
139 $valid &&
140 $this->getPermissionManager()->isBlockedFrom( $user, $valid->getPageAsLinkTarget() )
141 ) {
142 $idResult['status'] = 'error';
143 // @phan-suppress-next-line PhanTypeMismatchArgument
144 $idResult += $this->getErrorFormatter()->formatMessage( ApiMessage::create(
145 'apierror-blocked',
146 'blocked',
147 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable Block is checked and not null
148 [ 'blockinfo' => $this->getBlockDetails( $user->getBlock() ) ]
149 ) );
150 return $idResult;
151 }
152 break;
153 case 'logid':
154 $valid = $this->validateLogId( $id );
155 break;
156 }
157
158 if ( !$valid ) {
159 $idResult['status'] = 'error';
160 // Messages: apierror-nosuchrcid apierror-nosuchrevid apierror-nosuchlogid
161 $idResult += $this->getErrorFormatter()->formatMessage( [ "apierror-nosuch$type", $id ] );
162 return $idResult;
163 }
164
165 $status = ChangeTags::updateTagsWithChecks( $params['add'],
166 $params['remove'],
167 ( $type === 'rcid' ? $id : null ),
168 ( $type === 'revid' ? $id : null ),
169 ( $type === 'logid' ? $id : null ),
170 null,
171 $params['reason'],
172 $this->getAuthority()
173 );
174
175 if ( !$status->isOK() ) {
176 if ( $status->hasMessage( 'actionthrottledtext' ) ) {
177 $idResult['status'] = 'skipped';
178 } else {
179 $idResult['status'] = 'failure';
180 $idResult['errors'] = $this->getErrorFormatter()->arrayFromStatus( $status, 'error' );
181 }
182 } else {
183 $idResult['status'] = 'success';
184 if ( $status->value->logId === null ) {
185 $idResult['noop'] = true;
186 } else {
187 $idResult['actionlogid'] = $status->value->logId;
188 $idResult['added'] = $status->value->addedTags;
189 ApiResult::setIndexedTagName( $idResult['added'], 't' );
190 $idResult['removed'] = $status->value->removedTags;
191 ApiResult::setIndexedTagName( $idResult['removed'], 't' );
192
193 if ( $params['tags'] ) {
194 $this->changeTagsStore->addTags( $params['tags'], null, null, $status->value->logId );
195 }
196 }
197 }
198 return $idResult;
199 }
200
201 public function mustBePosted() {
202 return true;
203 }
204
205 public function isWriteMode() {
206 return true;
207 }
208
209 public function getAllowedParams() {
210 return [
211 'rcid' => [
212 ParamValidator::PARAM_TYPE => 'integer',
213 ParamValidator::PARAM_ISMULTI => true,
214 ],
215 'revid' => [
216 ParamValidator::PARAM_TYPE => 'integer',
217 ParamValidator::PARAM_ISMULTI => true,
218 ],
219 'logid' => [
220 ParamValidator::PARAM_TYPE => 'integer',
221 ParamValidator::PARAM_ISMULTI => true,
222 ],
223 'add' => [
224 ParamValidator::PARAM_TYPE => 'tags',
225 ParamValidator::PARAM_ISMULTI => true,
226 ],
227 'remove' => [
228 ParamValidator::PARAM_TYPE => 'string',
229 ParamValidator::PARAM_ISMULTI => true,
230 ],
231 'reason' => [
232 ParamValidator::PARAM_TYPE => 'string',
233 ParamValidator::PARAM_DEFAULT => '',
234 ],
235 'tags' => [
236 ParamValidator::PARAM_TYPE => 'tags',
237 ParamValidator::PARAM_ISMULTI => true,
238 ],
239 ];
240 }
241
242 public function needsToken() {
243 return 'csrf';
244 }
245
246 protected function getExamplesMessages() {
247 return [
248 'action=tag&revid=123&add=vandalism&token=123ABC'
249 => 'apihelp-tag-example-rev',
250 'action=tag&logid=123&remove=spam&reason=Wrongly+applied&token=123ABC'
251 => 'apihelp-tag-example-log',
252 ];
253 }
254
255 public function getHelpUrls() {
256 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Tag';
257 }
258}
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:64
checkUserRightsAny( $rights)
Helper function for permission-denied errors.
Definition ApiBase.php:1649
getErrorFormatter()
Definition ApiBase.php:682
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
Definition ApiBase.php:731
requireAtLeastOneParameter( $params,... $required)
Die if 0 of a certain set of parameters is set and not false.
Definition ApiBase.php:1010
getResult()
Get the result object.
Definition ApiBase.php:671
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:811
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:532
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition ApiBase.php:1589
dieBlocked(Block $block)
Throw an ApiUsageException, which will (if uncaught) call the main module's error handler and die wit...
Definition ApiBase.php:1562
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:65
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition ApiTag.php:209
processIndividual( $type, $params, $id)
Definition ApiTag.php:113
__construct(ApiMain $main, $action, IConnectionProvider $dbProvider, RevisionStore $revisionStore, ChangeTagsStore $changeTagsStore)
Definition ApiTag.php:47
validateLogId( $logid)
Definition ApiTag.php:104
getExamplesMessages()
Returns usage examples for this module.
Definition ApiTag.php:246
isWriteMode()
Indicates whether this module requires write mode.
Definition ApiTag.php:205
getHelpUrls()
Return links to more detailed help pages about the module.
Definition ApiTag.php:255
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition ApiTag.php:60
mustBePosted()
Indicates whether this module must be called with a POST request.
Definition ApiTag.php:201
needsToken()
Returns the token type this module requires in order to execute.
Definition ApiTag.php:242
static updateTagsWithChecks( $tagsToAdd, $tagsToRemove, $rc_id, $rev_id, $log_id, $params, string $reason, Authority $performer)
Adds and/or removes tags to/from a given change, checking whether it is allowed first,...
static canAddTagsAccompanyingChange(array $tags, Authority $performer=null, $checkBlock=true)
Is it OK to allow the user to apply all the specified tags at the same time as they edit/make the cha...
Gateway class for change_tags table.
Service for looking up page revisions.
Service for formatting and validating API parameters.
trait ApiBlockInfoTrait
Provide primary and replica IDatabase connections.
getReplicaDatabase( $domain=false, $group=null)
Get connection to a replica database.
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:36