Merge "Some improvements to Special:MergeHistory"
[lhc/web/wiklou.git] / includes / api / ApiSetNotificationTimestamp.php
1 <?php
2
3 /**
4 * API for MediaWiki 1.14+
5 *
6 * Created on Jun 18, 2012
7 *
8 * Copyright © 2012 Brad Jorsch
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 * http://www.gnu.org/copyleft/gpl.html
24 *
25 * @file
26 */
27
28 /**
29 * API interface for setting the wl_notificationtimestamp field
30 * @ingroup API
31 */
32 class ApiSetNotificationTimestamp extends ApiBase {
33
34 private $mPageSet;
35
36 public function execute() {
37 $user = $this->getUser();
38
39 if ( $user->isAnon() ) {
40 $this->dieUsage( 'Anonymous users cannot use watchlist change notifications', 'notloggedin' );
41 }
42 if ( !$user->isAllowed( 'editmywatchlist' ) ) {
43 $this->dieUsage( 'You don\'t have permission to edit your watchlist', 'permissiondenied' );
44 }
45
46 $params = $this->extractRequestParams();
47 $this->requireMaxOneParameter( $params, 'timestamp', 'torevid', 'newerthanrevid' );
48
49 $pageSet = $this->getPageSet();
50 if ( $params['entirewatchlist'] && $pageSet->getDataSource() !== null ) {
51 $this->dieUsage(
52 "Cannot use 'entirewatchlist' at the same time as '{$pageSet->getDataSource()}'",
53 'multisource'
54 );
55 }
56
57 $dbw = wfGetDB( DB_MASTER, 'api' );
58
59 $timestamp = null;
60 if ( isset( $params['timestamp'] ) ) {
61 $timestamp = $dbw->timestamp( $params['timestamp'] );
62 }
63
64 if ( !$params['entirewatchlist'] ) {
65 $pageSet->execute();
66 }
67
68 if ( isset( $params['torevid'] ) ) {
69 if ( $params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1 ) {
70 $this->dieUsage( 'torevid may only be used with a single page', 'multpages' );
71 }
72 $title = reset( $pageSet->getGoodTitles() );
73 $timestamp = Revision::getTimestampFromId( $title, $params['torevid'] );
74 if ( $timestamp ) {
75 $timestamp = $dbw->timestamp( $timestamp );
76 } else {
77 $timestamp = null;
78 }
79 } elseif ( isset( $params['newerthanrevid'] ) ) {
80 if ( $params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1 ) {
81 $this->dieUsage( 'newerthanrevid may only be used with a single page', 'multpages' );
82 }
83 $title = reset( $pageSet->getGoodTitles() );
84 $revid = $title->getNextRevisionID( $params['newerthanrevid'] );
85 if ( $revid ) {
86 $timestamp = $dbw->timestamp( Revision::getTimestampFromId( $title, $revid ) );
87 } else {
88 $timestamp = null;
89 }
90 }
91
92 $apiResult = $this->getResult();
93 $result = array();
94 if ( $params['entirewatchlist'] ) {
95 // Entire watchlist mode: Just update the thing and return a success indicator
96 $dbw->update( 'watchlist', array( 'wl_notificationtimestamp' => $timestamp ),
97 array( 'wl_user' => $user->getID() ),
98 __METHOD__
99 );
100
101 $result['notificationtimestamp'] = is_null( $timestamp )
102 ? ''
103 : wfTimestamp( TS_ISO_8601, $timestamp );
104 } else {
105 // First, log the invalid titles
106 foreach ( $pageSet->getInvalidTitles() as $title ) {
107 $r = array();
108 $r['title'] = $title;
109 $r['invalid'] = '';
110 $result[] = $r;
111 }
112 foreach ( $pageSet->getMissingPageIDs() as $p ) {
113 $page = array();
114 $page['pageid'] = $p;
115 $page['missing'] = '';
116 $page['notwatched'] = '';
117 $result[] = $page;
118 }
119 foreach ( $pageSet->getMissingRevisionIDs() as $r ) {
120 $rev = array();
121 $rev['revid'] = $r;
122 $rev['missing'] = '';
123 $rev['notwatched'] = '';
124 $result[] = $rev;
125 }
126
127 // Now process the valid titles
128 $lb = new LinkBatch( $pageSet->getTitles() );
129 $dbw->update( 'watchlist', array( 'wl_notificationtimestamp' => $timestamp ),
130 array( 'wl_user' => $user->getID(), $lb->constructSet( 'wl', $dbw ) ),
131 __METHOD__
132 );
133
134 // Query the results of our update
135 $timestamps = array();
136 $res = $dbw->select(
137 'watchlist',
138 array( 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ),
139 array( 'wl_user' => $user->getID(), $lb->constructSet( 'wl', $dbw ) ),
140 __METHOD__
141 );
142 foreach ( $res as $row ) {
143 $timestamps[$row->wl_namespace][$row->wl_title] = $row->wl_notificationtimestamp;
144 }
145
146 // Now, put the valid titles into the result
147 /** @var $title Title */
148 foreach ( $pageSet->getTitles() as $title ) {
149 $ns = $title->getNamespace();
150 $dbkey = $title->getDBkey();
151 $r = array(
152 'ns' => intval( $ns ),
153 'title' => $title->getPrefixedText(),
154 );
155 if ( !$title->exists() ) {
156 $r['missing'] = '';
157 }
158 if ( isset( $timestamps[$ns] ) && array_key_exists( $dbkey, $timestamps[$ns] ) ) {
159 $r['notificationtimestamp'] = '';
160 if ( $timestamps[$ns][$dbkey] !== null ) {
161 $r['notificationtimestamp'] = wfTimestamp( TS_ISO_8601, $timestamps[$ns][$dbkey] );
162 }
163 } else {
164 $r['notwatched'] = '';
165 }
166 $result[] = $r;
167 }
168
169 $apiResult->setIndexedTagName( $result, 'page' );
170 }
171 $apiResult->addValue( null, $this->getModuleName(), $result );
172 }
173
174 /**
175 * Get a cached instance of an ApiPageSet object
176 * @return ApiPageSet
177 */
178 private function getPageSet() {
179 if ( !isset( $this->mPageSet ) ) {
180 $this->mPageSet = new ApiPageSet( $this );
181 }
182
183 return $this->mPageSet;
184 }
185
186 public function mustBePosted() {
187 return true;
188 }
189
190 public function isWriteMode() {
191 return true;
192 }
193
194 public function needsToken() {
195 return true;
196 }
197
198 public function getTokenSalt() {
199 return '';
200 }
201
202 public function getAllowedParams( $flags = 0 ) {
203 $result = array(
204 'entirewatchlist' => array(
205 ApiBase::PARAM_TYPE => 'boolean'
206 ),
207 'token' => null,
208 'timestamp' => array(
209 ApiBase::PARAM_TYPE => 'timestamp'
210 ),
211 'torevid' => array(
212 ApiBase::PARAM_TYPE => 'integer'
213 ),
214 'newerthanrevid' => array(
215 ApiBase::PARAM_TYPE => 'integer'
216 ),
217 );
218 if ( $flags ) {
219 $result += $this->getPageSet()->getFinalParams( $flags );
220 }
221
222 return $result;
223 }
224
225 public function getParamDescription() {
226 return $this->getPageSet()->getFinalParamDescription() + array(
227 'entirewatchlist' => 'Work on all watched pages',
228 'timestamp' => 'Timestamp to which to set the notification timestamp',
229 'torevid' => 'Revision to set the notification timestamp to (one page only)',
230 'newerthanrevid' => 'Revision to set the notification timestamp newer than (one page only)',
231 'token' => 'A token previously acquired via prop=info',
232 );
233 }
234
235 public function getResultProperties() {
236 return array(
237 ApiBase::PROP_LIST => true,
238 ApiBase::PROP_ROOT => array(
239 'notificationtimestamp' => array(
240 ApiBase::PROP_TYPE => 'timestamp',
241 ApiBase::PROP_NULLABLE => true
242 )
243 ),
244 '' => array(
245 'ns' => array(
246 ApiBase::PROP_TYPE => 'namespace',
247 ApiBase::PROP_NULLABLE => true
248 ),
249 'title' => array(
250 ApiBase::PROP_TYPE => 'string',
251 ApiBase::PROP_NULLABLE => true
252 ),
253 'pageid' => array(
254 ApiBase::PROP_TYPE => 'integer',
255 ApiBase::PROP_NULLABLE => true
256 ),
257 'revid' => array(
258 ApiBase::PROP_TYPE => 'integer',
259 ApiBase::PROP_NULLABLE => true
260 ),
261 'invalid' => 'boolean',
262 'missing' => 'boolean',
263 'notwatched' => 'boolean',
264 'notificationtimestamp' => array(
265 ApiBase::PROP_TYPE => 'timestamp',
266 ApiBase::PROP_NULLABLE => true
267 )
268 )
269 );
270 }
271
272 public function getDescription() {
273 return array( 'Update the notification timestamp for watched pages.',
274 'This affects the highlighting of changed pages in the watchlist and history,',
275 'and the sending of email when the "Email me when a page on my watchlist is',
276 'changed" preference is enabled.'
277 );
278 }
279
280 public function getPossibleErrors() {
281 $ps = $this->getPageSet();
282
283 return array_merge(
284 parent::getPossibleErrors(),
285 $ps->getFinalPossibleErrors(),
286 $this->getRequireMaxOneParameterErrorMessages(
287 array( 'timestamp', 'torevid', 'newerthanrevid' ) ),
288 $this->getRequireOnlyOneParameterErrorMessages(
289 array_merge( array( 'entirewatchlist' ), array_keys( $ps->getFinalParams() ) ) ),
290 array(
291 array( 'code' => 'notloggedin', 'info'
292 => 'Anonymous users cannot use watchlist change notifications' ),
293 array( 'code' => 'multpages', 'info' => 'torevid may only be used with a single page' ),
294 array( 'code' => 'multpages', 'info' => 'newerthanrevid may only be used with a single page' ),
295 )
296 );
297 }
298
299 public function getExamples() {
300 return array(
301 'api.php?action=setnotificationtimestamp&entirewatchlist=&token=123ABC'
302 => 'Reset the notification status for the entire watchlist',
303 'api.php?action=setnotificationtimestamp&titles=Main_page&token=123ABC'
304 => 'Reset the notification status for "Main page"',
305 'api.php?action=setnotificationtimestamp&titles=Main_page&' .
306 'timestamp=2012-01-01T00:00:00Z&token=123ABC'
307 => 'Set the notification timestamp for "Main page" so all edits ' .
308 'since 1 January 2012 are unviewed',
309 );
310 }
311
312 public function getHelpUrls() {
313 return 'https://www.mediawiki.org/wiki/API:SetNotificationTimestamp';
314 }
315 }