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