MediaWiki  master
ApiTag.php
Go to the documentation of this file.
1 <?php
2 
27 
32 class 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:63
checkUserRightsAny( $rights, $user=null)
Helper function for permission-denied errors.
Definition: ApiBase.php:1632
getErrorFormatter()
Definition: ApiBase.php:679
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
Definition: ApiBase.php:728
requireAtLeastOneParameter( $params,... $required)
Die if 0 of a certain set of parameters is set and not false.
Definition: ApiBase.php:1007
getResult()
Get the result object.
Definition: ApiBase.php:668
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:808
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:529
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition: ApiBase.php:1571
dieBlocked(Block $block)
Throw an ApiUsageException, which will (if uncaught) call the main module's error handler and die wit...
Definition: ApiBase.php:1545
This is the main API class, used for both external and internal processing.
Definition: ApiMain.php:64
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
Definition: ApiMessage.php:45
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:604
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,...
Definition: ChangeTags.php:520
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...
Definition: ChangeTags.php:396
Gateway class for change_tags table.
Service for looking up page revisions.
static newFromId( $rcid)
Obtain the recent change with a given rc_id value.
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