Completed
Branch master (939199)
by
unknown
39:35
created

includes/api/ApiFeedWatchlist.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 *
4
 *
5
 * Created on Oct 13, 2006
6
 *
7
 * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License along
20
 * with this program; if not, write to the Free Software Foundation, Inc.,
21
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
 * http://www.gnu.org/copyleft/gpl.html
23
 *
24
 * @file
25
 */
26
27
/**
28
 * This action allows users to get their watchlist items in RSS/Atom formats.
29
 * When executed, it performs a nested call to the API to get the needed data,
30
 * and formats it in a proper format.
31
 *
32
 * @ingroup API
33
 */
34
class ApiFeedWatchlist extends ApiBase {
35
36
	private $watchlistModule = null;
37
	private $linkToSections = false;
38
39
	/**
40
	 * This module uses a custom feed wrapper printer.
41
	 *
42
	 * @return ApiFormatFeedWrapper
43
	 */
44
	public function getCustomPrinter() {
45
		return new ApiFormatFeedWrapper( $this->getMain() );
46
	}
47
48
	/**
49
	 * Make a nested call to the API to request watchlist items in the last $hours.
50
	 * Wrap the result as an RSS/Atom feed.
51
	 */
52
	public function execute() {
53
		$config = $this->getConfig();
54
		$feedClasses = $config->get( 'FeedClasses' );
55
		try {
56
			$params = $this->extractRequestParams();
57
58
			if ( !$config->get( 'Feed' ) ) {
59
				$this->dieUsage( 'Syndication feeds are not available', 'feed-unavailable' );
60
			}
61
62
			if ( !isset( $feedClasses[$params['feedformat']] ) ) {
63
				$this->dieUsage( 'Invalid subscription feed type', 'feed-invalid' );
64
			}
65
66
			// limit to the number of hours going from now back
67
			$endTime = wfTimestamp( TS_MW, time() - intval( $params['hours'] * 60 * 60 ) );
68
69
			// Prepare parameters for nested request
70
			$fauxReqArr = [
71
				'action' => 'query',
72
				'meta' => 'siteinfo',
73
				'siprop' => 'general',
74
				'list' => 'watchlist',
75
				'wlprop' => 'title|user|comment|timestamp|ids',
76
				'wldir' => 'older', // reverse order - from newest to oldest
77
				'wlend' => $endTime, // stop at this time
78
				'wllimit' => min( 50, $this->getConfig()->get( 'FeedLimit' ) )
79
			];
80
81
			if ( $params['wlowner'] !== null ) {
82
				$fauxReqArr['wlowner'] = $params['wlowner'];
83
			}
84
			if ( $params['wltoken'] !== null ) {
85
				$fauxReqArr['wltoken'] = $params['wltoken'];
86
			}
87
			if ( $params['wlexcludeuser'] !== null ) {
88
				$fauxReqArr['wlexcludeuser'] = $params['wlexcludeuser'];
89
			}
90
			if ( $params['wlshow'] !== null ) {
91
				$fauxReqArr['wlshow'] = $params['wlshow'];
92
			}
93
			if ( $params['wltype'] !== null ) {
94
				$fauxReqArr['wltype'] = $params['wltype'];
95
			}
96
97
			// Support linking directly to sections when possible
98
			// (possible only if section name is present in comment)
99
			if ( $params['linktosections'] ) {
100
				$this->linkToSections = true;
101
			}
102
103
			// Check for 'allrev' parameter, and if found, show all revisions to each page on wl.
104
			if ( $params['allrev'] ) {
105
				$fauxReqArr['wlallrev'] = '';
106
			}
107
108
			// Create the request
109
			$fauxReq = new FauxRequest( $fauxReqArr );
110
111
			// Execute
112
			$module = new ApiMain( $fauxReq );
113
			$module->execute();
114
115
			$data = $module->getResult()->getResultData( [ 'query', 'watchlist' ] );
116
			$feedItems = [];
117
			foreach ( (array)$data as $key => $info ) {
118
				if ( ApiResult::isMetadataKey( $key ) ) {
119
					continue;
120
				}
121
				$feedItem = $this->createFeedItem( $info );
122
				if ( $feedItem ) {
123
					$feedItems[] = $feedItem;
124
				}
125
			}
126
127
			$msg = wfMessage( 'watchlist' )->inContentLanguage()->text();
128
129
			$feedTitle = $this->getConfig()->get( 'Sitename' ) . ' - ' . $msg .
130
				' [' . $this->getConfig()->get( 'LanguageCode' ) . ']';
131
			$feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullURL();
132
133
			$feed = new $feedClasses[$params['feedformat']] (
134
				$feedTitle,
135
				htmlspecialchars( $msg ),
136
				$feedUrl
137
			);
138
139
			ApiFormatFeedWrapper::setResult( $this->getResult(), $feed, $feedItems );
140
		} catch ( Exception $e ) {
141
			// Error results should not be cached
142
			$this->getMain()->setCacheMaxAge( 0 );
143
144
			// @todo FIXME: Localise  brackets
145
			$feedTitle = $this->getConfig()->get( 'Sitename' ) . ' - Error - ' .
146
				wfMessage( 'watchlist' )->inContentLanguage()->text() .
147
				' [' . $this->getConfig()->get( 'LanguageCode' ) . ']';
148
			$feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullURL();
149
150
			$feedFormat = isset( $params['feedformat'] ) ? $params['feedformat'] : 'rss';
151
			$msg = wfMessage( 'watchlist' )->inContentLanguage()->escaped();
152
			$feed = new $feedClasses[$feedFormat] ( $feedTitle, $msg, $feedUrl );
153
154
			if ( $e instanceof UsageException ) {
155
				$errorCode = $e->getCodeString();
156
			} else {
157
				// Something is seriously wrong
158
				$errorCode = 'internal_api_error';
159
			}
160
161
			$errorText = $e->getMessage();
162
			$feedItems[] = new FeedItem( "Error ($errorCode)", $errorText, '', '', '' );
0 ignored issues
show
The variable $feedItems does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
163
			ApiFormatFeedWrapper::setResult( $this->getResult(), $feed, $feedItems );
164
		}
165
	}
166
167
	/**
168
	 * @param array $info
169
	 * @return FeedItem
170
	 */
171
	private function createFeedItem( $info ) {
172
		if ( !isset( $info['title'] ) ) {
173
			// Probably a revdeled log entry, skip it.
174
			return null;
175
		}
176
177
		$titleStr = $info['title'];
178
		$title = Title::newFromText( $titleStr );
179
		$curidParam = [];
180
		if ( !$title || $title->isExternal() ) {
181
			// Probably a formerly-valid title that's now conflicting with an
182
			// interwiki prefix or the like.
183
			if ( isset( $info['pageid'] ) ) {
184
				$title = Title::newFromID( $info['pageid'] );
185
				$curidParam = [ 'curid' => $info['pageid'] ];
186
			}
187
			if ( !$title || $title->isExternal() ) {
188
				return null;
189
			}
190
		}
191
		if ( isset( $info['revid'] ) ) {
192
			$titleUrl = $title->getFullURL( [ 'diff' => $info['revid'] ] );
193
		} else {
194
			$titleUrl = $title->getFullURL( $curidParam );
195
		}
196
		$comment = isset( $info['comment'] ) ? $info['comment'] : null;
197
198
		// Create an anchor to section.
199
		// The anchor won't work for sections that have dupes on page
200
		// as there's no way to strip that info from ApiWatchlist (apparently?).
201
		// RegExp in the line below is equal to Linker::formatAutocomments().
202
		if ( $this->linkToSections && $comment !== null &&
203
			preg_match( '!(.*)/\*\s*(.*?)\s*\*/(.*)!', $comment, $matches )
204
		) {
205
			global $wgParser;
206
207
			$sectionTitle = $wgParser->stripSectionName( $matches[2] );
208
			$sectionTitle = Sanitizer::normalizeSectionNameWhitespace( $sectionTitle );
209
			$titleUrl .= Title::newFromText( '#' . $sectionTitle )->getFragmentForURL();
210
		}
211
212
		$timestamp = $info['timestamp'];
213
214
		if ( isset( $info['user'] ) ) {
215
			$user = $info['user'];
216
			$completeText = "$comment ($user)";
217
		} else {
218
			$user = '';
219
			$completeText = (string)$comment;
220
		}
221
222
		return new FeedItem( $titleStr, $completeText, $titleUrl, $timestamp, $user );
223
	}
224
225
	private function getWatchlistModule() {
226
		if ( $this->watchlistModule === null ) {
227
			$this->watchlistModule = $this->getMain()->getModuleManager()->getModule( 'query' )
228
				->getModuleManager()->getModule( 'watchlist' );
229
		}
230
231
		return $this->watchlistModule;
232
	}
233
234
	public function getAllowedParams( $flags = 0 ) {
235
		$feedFormatNames = array_keys( $this->getConfig()->get( 'FeedClasses' ) );
236
		$ret = [
237
			'feedformat' => [
238
				ApiBase::PARAM_DFLT => 'rss',
239
				ApiBase::PARAM_TYPE => $feedFormatNames
240
			],
241
			'hours' => [
242
				ApiBase::PARAM_DFLT => 24,
243
				ApiBase::PARAM_TYPE => 'integer',
244
				ApiBase::PARAM_MIN => 1,
245
				ApiBase::PARAM_MAX => 72,
246
			],
247
			'linktosections' => false,
248
		];
249
250
		$copyParams = [
251
			'allrev' => 'allrev',
252
			'owner' => 'wlowner',
253
			'token' => 'wltoken',
254
			'show' => 'wlshow',
255
			'type' => 'wltype',
256
			'excludeuser' => 'wlexcludeuser',
257
		];
258
		if ( $flags ) {
259
			$wlparams = $this->getWatchlistModule()->getAllowedParams( $flags );
260
			foreach ( $copyParams as $from => $to ) {
261
				$p = $wlparams[$from];
262
				if ( !is_array( $p ) ) {
263
					$p = [ ApiBase::PARAM_DFLT => $p ];
264
				}
265
				if ( !isset( $p[ApiBase::PARAM_HELP_MSG] ) ) {
266
					$p[ApiBase::PARAM_HELP_MSG] = "apihelp-query+watchlist-param-$from";
267
				}
268
				if ( isset( $p[ApiBase::PARAM_TYPE] ) && is_array( $p[ApiBase::PARAM_TYPE] ) &&
269
					isset( $p[ApiBase::PARAM_HELP_MSG_PER_VALUE] )
270
				) {
271
					foreach ( $p[ApiBase::PARAM_TYPE] as $v ) {
272
						if ( !isset( $p[ApiBase::PARAM_HELP_MSG_PER_VALUE][$v] ) ) {
273
							$p[ApiBase::PARAM_HELP_MSG_PER_VALUE][$v] = "apihelp-query+watchlist-paramvalue-$from-$v";
274
						}
275
					}
276
				}
277
				$ret[$to] = $p;
278
			}
279
		} else {
280
			foreach ( $copyParams as $from => $to ) {
281
				$ret[$to] = null;
282
			}
283
		}
284
285
		return $ret;
286
	}
287
288
	protected function getExamplesMessages() {
289
		return [
290
			'action=feedwatchlist'
291
				=> 'apihelp-feedwatchlist-example-default',
292
			'action=feedwatchlist&allrev=&hours=6'
293
				=> 'apihelp-feedwatchlist-example-all6hrs',
294
		];
295
	}
296
297
	public function getHelpUrls() {
298
		return 'https://www.mediawiki.org/wiki/API:Watchlist_feed';
299
	}
300
}
301