MediaWiki REL1_37
ApiFeedWatchlist.php
Go to the documentation of this file.
1<?php
31
32 private $watchlistModule = null;
33 private $linkToSections = false;
34
36 private $parser;
37
43 public function __construct(
44 ApiMain $main,
45 $action,
47 ) {
48 parent::__construct( $main, $action );
49 $this->parser = $parser;
50 }
51
57 public function getCustomPrinter() {
58 return new ApiFormatFeedWrapper( $this->getMain() );
59 }
60
65 public function execute() {
66 $config = $this->getConfig();
67 $feedClasses = $config->get( 'FeedClasses' );
68 $params = [];
69 $feedItems = [];
70 try {
71 $params = $this->extractRequestParams();
72
73 if ( !$config->get( 'Feed' ) ) {
74 $this->dieWithError( 'feed-unavailable' );
75 }
76
77 if ( !isset( $feedClasses[$params['feedformat']] ) ) {
78 $this->dieWithError( 'feed-invalid' );
79 }
80
81 // limit to the number of hours going from now back
82 $endTime = wfTimestamp( TS_MW, time() - (int)$params['hours'] * 60 * 60 );
83
84 // Prepare parameters for nested request
85 $fauxReqArr = [
86 'action' => 'query',
87 'meta' => 'siteinfo',
88 'siprop' => 'general',
89 'list' => 'watchlist',
90 'wlprop' => 'title|user|comment|timestamp|ids|loginfo',
91 'wldir' => 'older', // reverse order - from newest to oldest
92 'wlend' => $endTime, // stop at this time
93 'wllimit' => min( 50, $this->getConfig()->get( 'FeedLimit' ) )
94 ];
95
96 if ( $params['wlowner'] !== null ) {
97 $fauxReqArr['wlowner'] = $params['wlowner'];
98 }
99 if ( $params['wltoken'] !== null ) {
100 $fauxReqArr['wltoken'] = $params['wltoken'];
101 }
102 if ( $params['wlexcludeuser'] !== null ) {
103 $fauxReqArr['wlexcludeuser'] = $params['wlexcludeuser'];
104 }
105 if ( $params['wlshow'] !== null ) {
106 $fauxReqArr['wlshow'] = $params['wlshow'];
107 }
108 if ( $params['wltype'] !== null ) {
109 $fauxReqArr['wltype'] = $params['wltype'];
110 }
111
112 // Support linking directly to sections when possible
113 // (possible only if section name is present in comment)
114 if ( $params['linktosections'] ) {
115 $this->linkToSections = true;
116 }
117
118 // Check for 'allrev' parameter, and if found, show all revisions to each page on wl.
119 if ( $params['allrev'] ) {
120 $fauxReqArr['wlallrev'] = '';
121 }
122
123 $fauxReq = new FauxRequest( $fauxReqArr );
124
125 $module = new ApiMain( $fauxReq );
126 $module->execute();
127
128 $data = $module->getResult()->getResultData( [ 'query', 'watchlist' ] );
129 foreach ( (array)$data as $key => $info ) {
130 if ( ApiResult::isMetadataKey( $key ) ) {
131 continue;
132 }
133 $feedItem = $this->createFeedItem( $info );
134 if ( $feedItem ) {
135 $feedItems[] = $feedItem;
136 }
137 }
138
139 $msg = wfMessage( 'watchlist' )->inContentLanguage()->text();
140
141 $feedTitle = $this->getConfig()->get( 'Sitename' ) . ' - ' . $msg .
142 ' [' . $this->getConfig()->get( 'LanguageCode' ) . ']';
143 $feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullURL();
144
145 $feed = new $feedClasses[$params['feedformat']] (
146 $feedTitle,
147 htmlspecialchars( $msg ),
148 $feedUrl
149 );
150
151 ApiFormatFeedWrapper::setResult( $this->getResult(), $feed, $feedItems );
152 } catch ( Exception $e ) {
153 // Error results should not be cached
154 $this->getMain()->setCacheMaxAge( 0 );
155
156 // @todo FIXME: Localise brackets
157 $feedTitle = $this->getConfig()->get( 'Sitename' ) . ' - Error - ' .
158 wfMessage( 'watchlist' )->inContentLanguage()->text() .
159 ' [' . $this->getConfig()->get( 'LanguageCode' ) . ']';
160 $feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullURL();
161
162 $feedFormat = $params['feedformat'] ?? 'rss';
163 $msg = wfMessage( 'watchlist' )->inContentLanguage()->escaped();
164 $feed = new $feedClasses[$feedFormat] ( $feedTitle, $msg, $feedUrl );
165
166 if ( $e instanceof ApiUsageException ) {
167 foreach ( $e->getStatusValue()->getErrors() as $error ) {
168 // @phan-suppress-next-line PhanUndeclaredMethod
169 $msg = ApiMessage::create( $error )
170 ->inLanguage( $this->getLanguage() );
171 $errorTitle = $this->msg( 'api-feed-error-title', $msg->getApiCode() );
172 $errorText = $msg->text();
173 $feedItems[] = new FeedItem( $errorTitle, $errorText, '', '', '' );
174 }
175 } else {
176 // Something is seriously wrong
177 $errorCode = 'internal_api_error';
178 $errorTitle = $this->msg( 'api-feed-error-title', $errorCode );
179 $errorText = $e->getMessage();
180 $feedItems[] = new FeedItem( $errorTitle, $errorText, '', '', '' );
181 }
182
183 ApiFormatFeedWrapper::setResult( $this->getResult(), $feed, $feedItems );
184 }
185 }
186
191 private function createFeedItem( $info ) {
192 if ( !isset( $info['title'] ) ) {
193 // Probably a revdeled log entry, skip it.
194 return null;
195 }
196
197 $titleStr = $info['title'];
198 $title = Title::newFromText( $titleStr );
199 $curidParam = [];
200 if ( !$title || $title->isExternal() ) {
201 // Probably a formerly-valid title that's now conflicting with an
202 // interwiki prefix or the like.
203 if ( isset( $info['pageid'] ) ) {
204 $title = Title::newFromID( $info['pageid'] );
205 $curidParam = [ 'curid' => $info['pageid'] ];
206 }
207 if ( !$title || $title->isExternal() ) {
208 return null;
209 }
210 }
211 if ( isset( $info['revid'] ) ) {
212 if ( $info['revid'] === 0 && isset( $info['logid'] ) ) {
213 $logTitle = Title::makeTitle( NS_SPECIAL, 'Log' );
214 $titleUrl = $logTitle->getFullURL( [ 'logid' => $info['logid'] ] );
215 } else {
216 $titleUrl = $title->getFullURL( [ 'diff' => $info['revid'] ] );
217 }
218 } else {
219 $titleUrl = $title->getFullURL( $curidParam );
220 }
221 $comment = $info['comment'] ?? null;
222
223 // Create an anchor to section.
224 // The anchor won't work for sections that have dupes on page
225 // as there's no way to strip that info from ApiWatchlist (apparently?).
226 // RegExp in the line below is equal to Linker::formatAutocomments().
227 if ( $this->linkToSections && $comment !== null &&
228 preg_match( '!(.*)/\*\s*(.*?)\s*\*/(.*)!', $comment, $matches )
229 ) {
230 $titleUrl .= $this->parser->guessSectionNameFromWikiText( $matches[ 2 ] );
231 }
232
233 $timestamp = $info['timestamp'];
234
235 if ( isset( $info['user'] ) ) {
236 $user = $info['user'];
237 $completeText = "$comment ($user)";
238 } else {
239 $user = '';
240 $completeText = (string)$comment;
241 }
242
243 return new FeedItem( $titleStr, $completeText, $titleUrl, $timestamp, $user );
244 }
245
246 private function getWatchlistModule() {
247 if ( $this->watchlistModule === null ) {
248 $this->watchlistModule = $this->getMain()->getModuleManager()->getModule( 'query' )
249 ->getModuleManager()->getModule( 'watchlist' );
250 }
251
253 }
254
255 public function getAllowedParams( $flags = 0 ) {
256 $feedFormatNames = array_keys( $this->getConfig()->get( 'FeedClasses' ) );
257 $ret = [
258 'feedformat' => [
259 ApiBase::PARAM_DFLT => 'rss',
260 ApiBase::PARAM_TYPE => $feedFormatNames
261 ],
262 'hours' => [
264 ApiBase::PARAM_TYPE => 'integer',
266 ApiBase::PARAM_MAX => 72,
267 ],
268 'linktosections' => false,
269 ];
270
271 $copyParams = [
272 'allrev' => 'allrev',
273 'owner' => 'wlowner',
274 'token' => 'wltoken',
275 'show' => 'wlshow',
276 'type' => 'wltype',
277 'excludeuser' => 'wlexcludeuser',
278 ];
279 if ( $flags ) {
280 // @phan-suppress-next-line PhanParamTooMany
281 $wlparams = $this->getWatchlistModule()->getAllowedParams( $flags );
282 foreach ( $copyParams as $from => $to ) {
283 $p = $wlparams[$from];
284 if ( !is_array( $p ) ) {
285 $p = [ ApiBase::PARAM_DFLT => $p ];
286 }
287 if ( !isset( $p[ApiBase::PARAM_HELP_MSG] ) ) {
288 $p[ApiBase::PARAM_HELP_MSG] = "apihelp-query+watchlist-param-$from";
289 }
290 if ( isset( $p[ApiBase::PARAM_TYPE] ) && is_array( $p[ApiBase::PARAM_TYPE] ) &&
292 ) {
293 foreach ( $p[ApiBase::PARAM_TYPE] as $v ) {
294 if ( !isset( $p[ApiBase::PARAM_HELP_MSG_PER_VALUE][$v] ) ) {
295 $p[ApiBase::PARAM_HELP_MSG_PER_VALUE][$v] = "apihelp-query+watchlist-paramvalue-$from-$v";
296 }
297 }
298 }
299 $ret[$to] = $p;
300 }
301 } else {
302 foreach ( $copyParams as $from => $to ) {
303 $ret[$to] = null;
304 }
305 }
306
307 return $ret;
308 }
309
310 protected function getExamplesMessages() {
311 return [
312 'action=feedwatchlist'
313 => 'apihelp-feedwatchlist-example-default',
314 'action=feedwatchlist&allrev=&hours=6'
315 => 'apihelp-feedwatchlist-example-all6hrs',
316 ];
317 }
318
319 public function getHelpUrls() {
320 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Watchlist_feed';
321 }
322}
const NS_SPECIAL
Definition Defines.php:53
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:55
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1436
const PARAM_MAX
Definition ApiBase.php:85
getMain()
Get the main module.
Definition ApiBase.php:513
const PARAM_TYPE
Definition ApiBase.php:81
const PARAM_DFLT
Definition ApiBase.php:73
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, this is an array mapping those values to $msg...
Definition ApiBase.php:195
const PARAM_MIN
Definition ApiBase.php:93
getResult()
Get the result object.
Definition ApiBase.php:628
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:764
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:162
This action allows users to get their watchlist items in RSS/Atom formats.
getAllowedParams( $flags=0)
__construct(ApiMain $main, $action, Parser $parser)
getExamplesMessages()
Returns usage examples for this module.
getHelpUrls()
Return links to more detailed help pages about the module.
getCustomPrinter()
This module uses a custom feed wrapper printer.
execute()
Make a nested call to the API to request watchlist items in the last $hours.
This printer is used to wrap an instance of the Feed class.
static setResult( $result, $feed, $feedItems)
Call this method to initialize output data.
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:49
Exception used to abort API execution with an error.
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
WebRequest clone which takes values from a provided array.
A base class for outputting syndication feeds (e.g.
Definition FeedItem.php:33
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition Parser.php:91
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...