Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
53.21% |
83 / 156 |
|
55.56% |
5 / 9 |
CRAP | |
0.00% |
0 / 1 |
ApiSetNotificationTimestamp | |
53.21% |
83 / 156 |
|
55.56% |
5 / 9 |
194.86 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
execute | |
47.75% |
53 / 111 |
|
0.00% |
0 / 1 |
158.40 | |||
getPageSet | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
mustBePosted | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isWriteMode | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
needsToken | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getAllowedParams | |
95.00% |
19 / 20 |
|
0.00% |
0 / 1 |
2 | |||
getExamplesMessages | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
2 | |||
getHelpUrls | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | /** |
4 | * API for MediaWiki 1.14+ |
5 | * |
6 | * Copyright © 2012 Wikimedia Foundation and contributors |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. |
12 | * |
13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU General Public License along |
19 | * with this program; if not, write to the Free Software Foundation, Inc., |
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
21 | * http://www.gnu.org/copyleft/gpl.html |
22 | * |
23 | * @file |
24 | */ |
25 | |
26 | use MediaWiki\Revision\RevisionStore; |
27 | use MediaWiki\Title\Title; |
28 | use MediaWiki\Title\TitleFactory; |
29 | use MediaWiki\Title\TitleFormatter; |
30 | use Wikimedia\ParamValidator\ParamValidator; |
31 | use Wikimedia\Rdbms\IConnectionProvider; |
32 | |
33 | /** |
34 | * API interface for setting the wl_notificationtimestamp field |
35 | * @ingroup API |
36 | */ |
37 | class ApiSetNotificationTimestamp extends ApiBase { |
38 | |
39 | private $mPageSet = null; |
40 | |
41 | private RevisionStore $revisionStore; |
42 | private IConnectionProvider $dbProvider; |
43 | private WatchedItemStoreInterface $watchedItemStore; |
44 | private TitleFormatter $titleFormatter; |
45 | private TitleFactory $titleFactory; |
46 | |
47 | /** |
48 | * @param ApiMain $main |
49 | * @param string $action |
50 | * @param IConnectionProvider $dbProvider |
51 | * @param RevisionStore $revisionStore |
52 | * @param WatchedItemStoreInterface $watchedItemStore |
53 | * @param TitleFormatter $titleFormatter |
54 | * @param TitleFactory $titleFactory |
55 | */ |
56 | public function __construct( |
57 | ApiMain $main, |
58 | $action, |
59 | IConnectionProvider $dbProvider, |
60 | RevisionStore $revisionStore, |
61 | WatchedItemStoreInterface $watchedItemStore, |
62 | TitleFormatter $titleFormatter, |
63 | TitleFactory $titleFactory |
64 | ) { |
65 | parent::__construct( $main, $action ); |
66 | |
67 | $this->dbProvider = $dbProvider; |
68 | $this->revisionStore = $revisionStore; |
69 | $this->watchedItemStore = $watchedItemStore; |
70 | $this->titleFormatter = $titleFormatter; |
71 | $this->titleFactory = $titleFactory; |
72 | } |
73 | |
74 | public function execute() { |
75 | $user = $this->getUser(); |
76 | |
77 | if ( !$user->isRegistered() ) { |
78 | $this->dieWithError( 'watchlistanontext', 'notloggedin' ); |
79 | } |
80 | $this->checkUserRightsAny( 'editmywatchlist' ); |
81 | |
82 | $params = $this->extractRequestParams(); |
83 | $this->requireMaxOneParameter( $params, 'timestamp', 'torevid', 'newerthanrevid' ); |
84 | |
85 | $continuationManager = new ApiContinuationManager( $this, [], [] ); |
86 | $this->setContinuationManager( $continuationManager ); |
87 | |
88 | $pageSet = $this->getPageSet(); |
89 | if ( $params['entirewatchlist'] && $pageSet->getDataSource() !== null ) { |
90 | $this->dieWithError( |
91 | [ |
92 | 'apierror-invalidparammix-cannotusewith', |
93 | $this->encodeParamName( 'entirewatchlist' ), |
94 | $pageSet->encodeParamName( $pageSet->getDataSource() ) |
95 | ], |
96 | 'multisource' |
97 | ); |
98 | } |
99 | |
100 | $dbw = $this->dbProvider->getPrimaryDatabase(); |
101 | |
102 | $timestamp = null; |
103 | if ( isset( $params['timestamp'] ) ) { |
104 | $timestamp = $dbw->timestamp( $params['timestamp'] ); |
105 | } |
106 | |
107 | if ( !$params['entirewatchlist'] ) { |
108 | $pageSet->execute(); |
109 | } |
110 | |
111 | if ( isset( $params['torevid'] ) ) { |
112 | if ( $params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1 ) { |
113 | $this->dieWithError( [ 'apierror-multpages', $this->encodeParamName( 'torevid' ) ] ); |
114 | } |
115 | $titles = $pageSet->getGoodPages(); |
116 | $title = reset( $titles ); |
117 | if ( $title ) { |
118 | // XXX $title isn't actually used, can we just get rid of the previous six lines? |
119 | $timestamp = $this->revisionStore->getTimestampFromId( |
120 | $params['torevid'], |
121 | IDBAccessObject::READ_LATEST |
122 | ); |
123 | if ( $timestamp ) { |
124 | $timestamp = $dbw->timestamp( $timestamp ); |
125 | } else { |
126 | $timestamp = null; |
127 | } |
128 | } |
129 | } elseif ( isset( $params['newerthanrevid'] ) ) { |
130 | if ( $params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1 ) { |
131 | $this->dieWithError( [ 'apierror-multpages', $this->encodeParamName( 'newerthanrevid' ) ] ); |
132 | } |
133 | $titles = $pageSet->getGoodPages(); |
134 | $title = reset( $titles ); |
135 | if ( $title ) { |
136 | $timestamp = null; |
137 | $currRev = $this->revisionStore->getRevisionById( |
138 | $params['newerthanrevid'], |
139 | IDBAccessObject::READ_LATEST |
140 | ); |
141 | if ( $currRev ) { |
142 | $nextRev = $this->revisionStore->getNextRevision( |
143 | $currRev, |
144 | IDBAccessObject::READ_LATEST |
145 | ); |
146 | if ( $nextRev ) { |
147 | $timestamp = $dbw->timestamp( $nextRev->getTimestamp() ); |
148 | } |
149 | } |
150 | } |
151 | } |
152 | |
153 | $apiResult = $this->getResult(); |
154 | $result = []; |
155 | if ( $params['entirewatchlist'] ) { |
156 | // Entire watchlist mode: Just update the thing and return a success indicator |
157 | $this->watchedItemStore->resetAllNotificationTimestampsForUser( $user, $timestamp ); |
158 | |
159 | $result['notificationtimestamp'] = $timestamp === null |
160 | ? '' |
161 | : wfTimestamp( TS_ISO_8601, $timestamp ); |
162 | } else { |
163 | // First, log the invalid titles |
164 | foreach ( $pageSet->getInvalidTitlesAndReasons() as $r ) { |
165 | $r['invalid'] = true; |
166 | $result[] = $r; |
167 | } |
168 | foreach ( $pageSet->getMissingPageIDs() as $p ) { |
169 | $page = []; |
170 | $page['pageid'] = $p; |
171 | $page['missing'] = true; |
172 | $page['notwatched'] = true; |
173 | $result[] = $page; |
174 | } |
175 | foreach ( $pageSet->getMissingRevisionIDs() as $r ) { |
176 | $rev = []; |
177 | $rev['revid'] = $r; |
178 | $rev['missing'] = true; |
179 | $rev['notwatched'] = true; |
180 | $result[] = $rev; |
181 | } |
182 | |
183 | $pages = $pageSet->getPages(); |
184 | if ( $pages ) { |
185 | // Now process the valid titles |
186 | $this->watchedItemStore->setNotificationTimestampsForUser( |
187 | $user, |
188 | $timestamp, |
189 | $pages |
190 | ); |
191 | |
192 | // Query the results of our update |
193 | $timestamps = $this->watchedItemStore->getNotificationTimestampsBatch( |
194 | $user, |
195 | $pages |
196 | ); |
197 | |
198 | // Now, put the valid titles into the result |
199 | /** @var \MediaWiki\Page\PageIdentity $page */ |
200 | foreach ( $pages as $page ) { |
201 | $ns = $page->getNamespace(); |
202 | $dbkey = $page->getDBkey(); |
203 | $r = [ |
204 | 'ns' => $ns, |
205 | 'title' => $this->titleFormatter->getPrefixedText( $page ), |
206 | ]; |
207 | if ( !$page->exists() ) { |
208 | $r['missing'] = true; |
209 | $title = $this->titleFactory->newFromPageIdentity( $page ); |
210 | if ( $title->isKnown() ) { |
211 | $r['known'] = true; |
212 | } |
213 | } |
214 | if ( isset( $timestamps[$ns] ) && array_key_exists( $dbkey, $timestamps[$ns] ) |
215 | && $timestamps[$ns][$dbkey] !== false |
216 | ) { |
217 | $r['notificationtimestamp'] = ''; |
218 | if ( $timestamps[$ns][$dbkey] !== null ) { |
219 | $r['notificationtimestamp'] = wfTimestamp( TS_ISO_8601, $timestamps[$ns][$dbkey] ); |
220 | } |
221 | } else { |
222 | $r['notwatched'] = true; |
223 | } |
224 | $result[] = $r; |
225 | } |
226 | } |
227 | |
228 | ApiResult::setIndexedTagName( $result, 'page' ); |
229 | } |
230 | $apiResult->addValue( null, $this->getModuleName(), $result ); |
231 | |
232 | $this->setContinuationManager( null ); |
233 | $continuationManager->setContinuationIntoResult( $apiResult ); |
234 | } |
235 | |
236 | /** |
237 | * Get a cached instance of an ApiPageSet object |
238 | * @return ApiPageSet |
239 | */ |
240 | private function getPageSet() { |
241 | $this->mPageSet ??= new ApiPageSet( $this ); |
242 | |
243 | return $this->mPageSet; |
244 | } |
245 | |
246 | public function mustBePosted() { |
247 | return true; |
248 | } |
249 | |
250 | public function isWriteMode() { |
251 | return true; |
252 | } |
253 | |
254 | public function needsToken() { |
255 | return 'csrf'; |
256 | } |
257 | |
258 | public function getAllowedParams( $flags = 0 ) { |
259 | $result = [ |
260 | 'entirewatchlist' => [ |
261 | ParamValidator::PARAM_TYPE => 'boolean' |
262 | ], |
263 | 'timestamp' => [ |
264 | ParamValidator::PARAM_TYPE => 'timestamp' |
265 | ], |
266 | 'torevid' => [ |
267 | ParamValidator::PARAM_TYPE => 'integer' |
268 | ], |
269 | 'newerthanrevid' => [ |
270 | ParamValidator::PARAM_TYPE => 'integer' |
271 | ], |
272 | 'continue' => [ |
273 | ApiBase::PARAM_HELP_MSG => 'api-help-param-continue', |
274 | ], |
275 | ]; |
276 | if ( $flags ) { |
277 | $result += $this->getPageSet()->getFinalParams( $flags ); |
278 | } |
279 | |
280 | return $result; |
281 | } |
282 | |
283 | protected function getExamplesMessages() { |
284 | $title = Title::newMainPage()->getPrefixedText(); |
285 | $mp = rawurlencode( $title ); |
286 | |
287 | return [ |
288 | 'action=setnotificationtimestamp&entirewatchlist=&token=123ABC' |
289 | => 'apihelp-setnotificationtimestamp-example-all', |
290 | "action=setnotificationtimestamp&titles={$mp}&token=123ABC" |
291 | => 'apihelp-setnotificationtimestamp-example-page', |
292 | "action=setnotificationtimestamp&titles={$mp}&" . |
293 | 'timestamp=2012-01-01T00:00:00Z&token=123ABC' |
294 | => 'apihelp-setnotificationtimestamp-example-pagetimestamp', |
295 | 'action=setnotificationtimestamp&generator=allpages&gapnamespace=2&token=123ABC' |
296 | => 'apihelp-setnotificationtimestamp-example-allpages', |
297 | ]; |
298 | } |
299 | |
300 | public function getHelpUrls() { |
301 | return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:SetNotificationTimestamp'; |
302 | } |
303 | } |