4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 * http://www.gnu.org/copyleft/gpl.html
22 use MediaWiki\MediaWikiServices
;
28 class ApiTag
extends ApiBase
{
30 use ApiBlockInfoTrait
;
32 /** @var \MediaWiki\Revision\RevisionStore */
33 private $revisionStore;
35 public function execute() {
36 $this->revisionStore
= MediaWikiServices
::getInstance()->getRevisionStore();
38 $params = $this->extractRequestParams();
39 $user = $this->getUser();
41 // make sure the user is allowed
42 $this->checkUserRightsAny( 'changetags' );
44 // Fail early if the user is sitewide blocked.
45 $block = $user->getBlock();
46 if ( $block && $block->isSitewide() ) {
47 $this->dieBlocked( $block );
50 // Check if user can add tags
51 if ( $params['tags'] ) {
52 $ableToTag = ChangeTags
::canAddTagsAccompanyingChange( $params['tags'], $user );
53 if ( !$ableToTag->isOK() ) {
54 $this->dieStatus( $ableToTag );
58 // validate and process each revid, rcid and logid
59 $this->requireAtLeastOneParameter( $params, 'revid', 'rcid', 'logid' );
61 if ( $params['revid'] ) {
62 foreach ( $params['revid'] as $id ) {
63 $ret[] = $this->processIndividual( 'revid', $params, $id );
66 if ( $params['rcid'] ) {
67 foreach ( $params['rcid'] as $id ) {
68 $ret[] = $this->processIndividual( 'rcid', $params, $id );
71 if ( $params['logid'] ) {
72 foreach ( $params['logid'] as $id ) {
73 $ret[] = $this->processIndividual( 'logid', $params, $id );
77 ApiResult
::setIndexedTagName( $ret, 'result' );
78 $this->getResult()->addValue( null, $this->getModuleName(), $ret );
81 protected static function validateLogId( $logid ) {
82 $dbr = wfGetDB( DB_REPLICA
);
83 $result = $dbr->selectField( 'logging', 'log_id', [ 'log_id' => $logid ],
88 protected function processIndividual( $type, $params, $id ) {
89 $user = $this->getUser();
90 $idResult = [ $type => $id ];
96 $valid = RecentChange
::newFromId( $id );
97 if ( $valid && $this->getPermissionManager()->isBlockedFrom( $user, $valid->getTitle() ) ) {
98 $idResult['status'] = 'error';
99 // @phan-suppress-next-line PhanTypeMismatchArgument
100 $idResult +
= $this->getErrorFormatter()->formatMessage( ApiMessage
::create(
103 [ 'blockinfo' => $this->getBlockDetails( $user->getBlock() ) ]
109 $valid = $this->revisionStore
->getRevisionById( $id );
112 $this->getPermissionManager()->isBlockedFrom( $user, $valid->getPageAsLinkTarget() )
114 $idResult['status'] = 'error';
115 // @phan-suppress-next-line PhanTypeMismatchArgument
116 $idResult +
= $this->getErrorFormatter()->formatMessage( ApiMessage
::create(
119 [ 'blockinfo' => $this->getBlockDetails( $user->getBlock() ) ]
125 $valid = self
::validateLogId( $id );
130 $idResult['status'] = 'error';
131 // Messages: apierror-nosuchrcid apierror-nosuchrevid apierror-nosuchlogid
132 $idResult +
= $this->getErrorFormatter()->formatMessage( [ "apierror-nosuch$type", $id ] );
136 $status = ChangeTags
::updateTagsWithChecks( $params['add'],
138 ( $type === 'rcid' ?
$id : null ),
139 ( $type === 'revid' ?
$id : null ),
140 ( $type === 'logid' ?
$id : null ),
145 if ( !$status->isOK() ) {
146 if ( $status->hasMessage( 'actionthrottledtext' ) ) {
147 $idResult['status'] = 'skipped';
149 $idResult['status'] = 'failure';
150 $idResult['errors'] = $this->getErrorFormatter()->arrayFromStatus( $status, 'error' );
153 $idResult['status'] = 'success';
154 if ( is_null( $status->value
->logId
) ) {
155 $idResult['noop'] = true;
157 $idResult['actionlogid'] = $status->value
->logId
;
158 $idResult['added'] = $status->value
->addedTags
;
159 ApiResult
::setIndexedTagName( $idResult['added'], 't' );
160 $idResult['removed'] = $status->value
->removedTags
;
161 ApiResult
::setIndexedTagName( $idResult['removed'], 't' );
163 if ( $params['tags'] ) {
164 ChangeTags
::addTags( $params['tags'], null, null, $status->value
->logId
);
171 public function mustBePosted() {
175 public function isWriteMode() {
179 public function getAllowedParams() {
182 ApiBase
::PARAM_TYPE
=> 'integer',
183 ApiBase
::PARAM_ISMULTI
=> true,
186 ApiBase
::PARAM_TYPE
=> 'integer',
187 ApiBase
::PARAM_ISMULTI
=> true,
190 ApiBase
::PARAM_TYPE
=> 'integer',
191 ApiBase
::PARAM_ISMULTI
=> true,
194 ApiBase
::PARAM_TYPE
=> 'tags',
195 ApiBase
::PARAM_ISMULTI
=> true,
198 ApiBase
::PARAM_TYPE
=> 'string',
199 ApiBase
::PARAM_ISMULTI
=> true,
202 ApiBase
::PARAM_DFLT
=> '',
205 ApiBase
::PARAM_TYPE
=> 'tags',
206 ApiBase
::PARAM_ISMULTI
=> true,
211 public function needsToken() {
215 protected function getExamplesMessages() {
217 'action=tag&revid=123&add=vandalism&token=123ABC'
218 => 'apihelp-tag-example-rev',
219 'action=tag&logid=123&remove=spam&reason=Wrongly+applied&token=123ABC'
220 => 'apihelp-tag-example-log',
224 public function getHelpUrls() {
225 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Tag';