output: Narrow Title type hint to LinkTarget
[lhc/web/wiklou.git] / includes / api / ApiTag.php
1 <?php
2
3 /**
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.
8 *
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.
13 *
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
18 *
19 * @file
20 */
21
22 use MediaWiki\MediaWikiServices;
23 use MediaWiki\Revision\RevisionStore;
24
25 /**
26 * @ingroup API
27 * @since 1.25
28 */
29 class ApiTag extends ApiBase {
30
31 /** @var RevisionStore */
32 private $revisionStore;
33
34 public function execute() {
35 $this->revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
36
37 $params = $this->extractRequestParams();
38 $user = $this->getUser();
39
40 // make sure the user is allowed
41 $this->checkUserRightsAny( 'changetags' );
42
43 // @TODO Use PermissionManager::isBlockedFrom() instead.
44 $block = $user->getBlock();
45 if ( $block ) {
46 $this->dieBlocked( $block );
47 }
48
49 // Check if user can add tags
50 if ( $params['tags'] ) {
51 $ableToTag = ChangeTags::canAddTagsAccompanyingChange( $params['tags'], $user );
52 if ( !$ableToTag->isOK() ) {
53 $this->dieStatus( $ableToTag );
54 }
55 }
56
57 // validate and process each revid, rcid and logid
58 $this->requireAtLeastOneParameter( $params, 'revid', 'rcid', 'logid' );
59 $ret = [];
60 if ( $params['revid'] ) {
61 foreach ( $params['revid'] as $id ) {
62 $ret[] = $this->processIndividual( 'revid', $params, $id );
63 }
64 }
65 if ( $params['rcid'] ) {
66 foreach ( $params['rcid'] as $id ) {
67 $ret[] = $this->processIndividual( 'rcid', $params, $id );
68 }
69 }
70 if ( $params['logid'] ) {
71 foreach ( $params['logid'] as $id ) {
72 $ret[] = $this->processIndividual( 'logid', $params, $id );
73 }
74 }
75
76 ApiResult::setIndexedTagName( $ret, 'result' );
77 $this->getResult()->addValue( null, $this->getModuleName(), $ret );
78 }
79
80 protected static function validateLogId( $logid ) {
81 $dbr = wfGetDB( DB_REPLICA );
82 $result = $dbr->selectField( 'logging', 'log_id', [ 'log_id' => $logid ],
83 __METHOD__ );
84 return (bool)$result;
85 }
86
87 protected function processIndividual( $type, $params, $id ) {
88 $idResult = [ $type => $id ];
89
90 // validate the ID
91 $valid = false;
92 switch ( $type ) {
93 case 'rcid':
94 $valid = RecentChange::newFromId( $id );
95 break;
96 case 'revid':
97 $valid = $this->revisionStore->getRevisionById( $id );
98 break;
99 case 'logid':
100 $valid = self::validateLogId( $id );
101 break;
102 }
103
104 if ( !$valid ) {
105 $idResult['status'] = 'error';
106 // Messages: apierror-nosuchrcid apierror-nosuchrevid apierror-nosuchlogid
107 $idResult += $this->getErrorFormatter()->formatMessage( [ "apierror-nosuch$type", $id ] );
108 return $idResult;
109 }
110
111 $status = ChangeTags::updateTagsWithChecks( $params['add'],
112 $params['remove'],
113 ( $type === 'rcid' ? $id : null ),
114 ( $type === 'revid' ? $id : null ),
115 ( $type === 'logid' ? $id : null ),
116 null,
117 $params['reason'],
118 $this->getUser() );
119
120 if ( !$status->isOK() ) {
121 if ( $status->hasMessage( 'actionthrottledtext' ) ) {
122 $idResult['status'] = 'skipped';
123 } else {
124 $idResult['status'] = 'failure';
125 $idResult['errors'] = $this->getErrorFormatter()->arrayFromStatus( $status, 'error' );
126 }
127 } else {
128 $idResult['status'] = 'success';
129 if ( is_null( $status->value->logId ) ) {
130 $idResult['noop'] = true;
131 } else {
132 $idResult['actionlogid'] = $status->value->logId;
133 $idResult['added'] = $status->value->addedTags;
134 ApiResult::setIndexedTagName( $idResult['added'], 't' );
135 $idResult['removed'] = $status->value->removedTags;
136 ApiResult::setIndexedTagName( $idResult['removed'], 't' );
137
138 if ( $params['tags'] ) {
139 ChangeTags::addTags( $params['tags'], null, null, $status->value->logId );
140 }
141 }
142 }
143 return $idResult;
144 }
145
146 public function mustBePosted() {
147 return true;
148 }
149
150 public function isWriteMode() {
151 return true;
152 }
153
154 public function getAllowedParams() {
155 return [
156 'rcid' => [
157 ApiBase::PARAM_TYPE => 'integer',
158 ApiBase::PARAM_ISMULTI => true,
159 ],
160 'revid' => [
161 ApiBase::PARAM_TYPE => 'integer',
162 ApiBase::PARAM_ISMULTI => true,
163 ],
164 'logid' => [
165 ApiBase::PARAM_TYPE => 'integer',
166 ApiBase::PARAM_ISMULTI => true,
167 ],
168 'add' => [
169 ApiBase::PARAM_TYPE => 'tags',
170 ApiBase::PARAM_ISMULTI => true,
171 ],
172 'remove' => [
173 ApiBase::PARAM_TYPE => 'string',
174 ApiBase::PARAM_ISMULTI => true,
175 ],
176 'reason' => [
177 ApiBase::PARAM_DFLT => '',
178 ],
179 'tags' => [
180 ApiBase::PARAM_TYPE => 'tags',
181 ApiBase::PARAM_ISMULTI => true,
182 ],
183 ];
184 }
185
186 public function needsToken() {
187 return 'csrf';
188 }
189
190 protected function getExamplesMessages() {
191 return [
192 'action=tag&revid=123&add=vandalism&token=123ABC'
193 => 'apihelp-tag-example-rev',
194 'action=tag&logid=123&remove=spam&reason=Wrongly+applied&token=123ABC'
195 => 'apihelp-tag-example-log',
196 ];
197 }
198
199 public function getHelpUrls() {
200 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Tag';
201 }
202 }