Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
48.08% |
50 / 104 |
|
28.57% |
2 / 7 |
CRAP | |
0.00% |
0 / 1 |
ApiFeedRecentChanges | |
48.08% |
50 / 104 |
|
28.57% |
2 / 7 |
57.46 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getCustomPrinter | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 28 |
|
0.00% |
0 / 1 |
56 | |||
getFeedObject | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
20 | |||
getAllowedParams | |
100.00% |
47 / 47 |
|
100.00% |
1 / 1 |
2 | |||
getExamplesMessages | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
getHelpUrls | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | * @since 1.23 |
20 | */ |
21 | |
22 | use MediaWiki\Feed\ChannelFeed; |
23 | use MediaWiki\MainConfigNames; |
24 | use MediaWiki\Request\DerivativeRequest; |
25 | use MediaWiki\SpecialPage\SpecialPage; |
26 | use MediaWiki\SpecialPage\SpecialPageFactory; |
27 | use MediaWiki\Title\Title; |
28 | use MediaWiki\User\TempUser\TempUserConfig; |
29 | use Wikimedia\ParamValidator\ParamValidator; |
30 | use Wikimedia\ParamValidator\TypeDef\IntegerDef; |
31 | |
32 | /** |
33 | * Recent changes feed. |
34 | * |
35 | * @ingroup API |
36 | */ |
37 | class ApiFeedRecentChanges extends ApiBase { |
38 | |
39 | private $params; |
40 | |
41 | private SpecialPageFactory $specialPageFactory; |
42 | private TempUserConfig $tempUserConfig; |
43 | |
44 | /** |
45 | * @param ApiMain $mainModule |
46 | * @param string $moduleName |
47 | * @param SpecialPageFactory $specialPageFactory |
48 | * @param TempUserConfig $tempUserConfig |
49 | */ |
50 | public function __construct( |
51 | ApiMain $mainModule, |
52 | string $moduleName, |
53 | SpecialPageFactory $specialPageFactory, |
54 | TempUserConfig $tempUserConfig |
55 | ) { |
56 | parent::__construct( $mainModule, $moduleName ); |
57 | $this->specialPageFactory = $specialPageFactory; |
58 | $this->tempUserConfig = $tempUserConfig; |
59 | } |
60 | |
61 | /** |
62 | * This module uses a custom feed wrapper printer. |
63 | * |
64 | * @return ApiFormatFeedWrapper |
65 | */ |
66 | public function getCustomPrinter() { |
67 | return new ApiFormatFeedWrapper( $this->getMain() ); |
68 | } |
69 | |
70 | /** |
71 | * Format the rows (generated by SpecialRecentchanges or SpecialRecentchangeslinked) |
72 | * as an RSS/Atom feed. |
73 | */ |
74 | public function execute() { |
75 | $config = $this->getConfig(); |
76 | |
77 | $this->params = $this->extractRequestParams(); |
78 | |
79 | if ( !$config->get( MainConfigNames::Feed ) ) { |
80 | $this->dieWithError( 'feed-unavailable' ); |
81 | } |
82 | |
83 | $feedClasses = $config->get( MainConfigNames::FeedClasses ); |
84 | if ( !isset( $feedClasses[$this->params['feedformat']] ) ) { |
85 | $this->dieWithError( 'feed-invalid' ); |
86 | } |
87 | |
88 | $this->getMain()->setCacheMode( 'public' ); |
89 | if ( !$this->getMain()->getParameter( 'smaxage' ) ) { |
90 | // T65249: This page gets hit a lot, cache at least 15 seconds. |
91 | $this->getMain()->setCacheMaxAge( 15 ); |
92 | } |
93 | |
94 | $feedFormat = $this->params['feedformat']; |
95 | $specialPageName = $this->params['target'] !== null |
96 | ? 'Recentchangeslinked' |
97 | : 'Recentchanges'; |
98 | |
99 | $formatter = $this->getFeedObject( $feedFormat, $specialPageName ); |
100 | |
101 | // Parameters are passed via the request in the context… :( |
102 | $context = new DerivativeContext( $this ); |
103 | $context->setRequest( new DerivativeRequest( |
104 | $this->getRequest(), |
105 | $this->params, |
106 | $this->getRequest()->wasPosted() |
107 | ) ); |
108 | |
109 | // The row-getting functionality should be factored out of ChangesListSpecialPage too… |
110 | $rc = $this->specialPageFactory->getPage( $specialPageName ); |
111 | if ( $rc === null ) { |
112 | throw new RuntimeException( __METHOD__ . ' not able to instance special page ' . $specialPageName ); |
113 | } |
114 | '@phan-var \MediaWiki\SpecialPage\ChangesListSpecialPage $rc'; |
115 | $rc->setContext( $context ); |
116 | $rows = $rc->getRows(); |
117 | |
118 | $feedItems = $rows ? ChangesFeed::buildItems( $rows ) : []; |
119 | |
120 | ApiFormatFeedWrapper::setResult( $this->getResult(), $formatter, $feedItems ); |
121 | } |
122 | |
123 | /** |
124 | * Return a MediaWiki\Feed\ChannelFeed object. |
125 | * |
126 | * @param string $feedFormat Feed's format (either 'rss' or 'atom') |
127 | * @param string $specialPageName Relevant special page name (either 'Recentchanges' or |
128 | * 'Recentchangeslinked') |
129 | * @return ChannelFeed |
130 | */ |
131 | private function getFeedObject( $feedFormat, $specialPageName ) { |
132 | if ( $specialPageName === 'Recentchangeslinked' ) { |
133 | $title = Title::newFromText( $this->params['target'] ); |
134 | if ( !$title || $title->isExternal() ) { |
135 | $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $this->params['target'] ) ] ); |
136 | } |
137 | |
138 | $feed = new ChangesFeed( $feedFormat ); |
139 | $feedObj = $feed->getFeedObject( |
140 | $this->msg( 'recentchangeslinked-title', $title->getPrefixedText() ) |
141 | ->inContentLanguage()->text(), |
142 | $this->msg( 'recentchangeslinked-feed' )->inContentLanguage()->text(), |
143 | SpecialPage::getTitleFor( 'Recentchangeslinked' )->getFullURL() |
144 | ); |
145 | } else { |
146 | $feed = new ChangesFeed( $feedFormat ); |
147 | $feedObj = $feed->getFeedObject( |
148 | $this->msg( 'recentchanges' )->inContentLanguage()->text(), |
149 | $this->msg( 'recentchanges-feed-description' )->inContentLanguage()->text(), |
150 | SpecialPage::getTitleFor( 'Recentchanges' )->getFullURL() |
151 | ); |
152 | } |
153 | |
154 | return $feedObj; |
155 | } |
156 | |
157 | public function getAllowedParams() { |
158 | $config = $this->getConfig(); |
159 | $feedFormatNames = array_keys( $config->get( MainConfigNames::FeedClasses ) ); |
160 | |
161 | return [ |
162 | 'feedformat' => [ |
163 | ParamValidator::PARAM_DEFAULT => 'rss', |
164 | ParamValidator::PARAM_TYPE => $feedFormatNames, |
165 | ], |
166 | |
167 | 'namespace' => [ |
168 | ParamValidator::PARAM_TYPE => 'namespace', |
169 | ], |
170 | // TODO: Rename this option to 'invertnamespaces'? |
171 | 'invert' => false, |
172 | 'associated' => false, |
173 | |
174 | 'days' => [ |
175 | ParamValidator::PARAM_DEFAULT => 7, |
176 | IntegerDef::PARAM_MIN => 1, |
177 | ParamValidator::PARAM_TYPE => 'integer', |
178 | ], |
179 | 'limit' => [ |
180 | ParamValidator::PARAM_DEFAULT => 50, |
181 | IntegerDef::PARAM_MIN => 1, |
182 | IntegerDef::PARAM_MAX => $config->get( MainConfigNames::FeedLimit ), |
183 | ParamValidator::PARAM_TYPE => 'integer', |
184 | ], |
185 | 'from' => [ |
186 | ParamValidator::PARAM_TYPE => 'timestamp', |
187 | ], |
188 | |
189 | 'hideminor' => false, |
190 | 'hidebots' => false, |
191 | 'hideanons' => [ |
192 | ParamValidator::PARAM_DEFAULT => false, |
193 | ApiBase::PARAM_HELP_MSG => $this->tempUserConfig->isEnabled() ? |
194 | 'apihelp-feedrecentchanges-param-hideanons-temp' : |
195 | 'apihelp-feedrecentchanges-param-hideanons', |
196 | ], |
197 | 'hideliu' => false, |
198 | 'hidepatrolled' => false, |
199 | 'hidemyself' => false, |
200 | 'hidecategorization' => false, |
201 | |
202 | 'tagfilter' => [ |
203 | ParamValidator::PARAM_TYPE => 'string', |
204 | ], |
205 | 'inverttags' => false, |
206 | |
207 | 'target' => [ |
208 | ParamValidator::PARAM_TYPE => 'string', |
209 | ], |
210 | 'showlinkedto' => false, |
211 | ]; |
212 | } |
213 | |
214 | protected function getExamplesMessages() { |
215 | return [ |
216 | 'action=feedrecentchanges' |
217 | => 'apihelp-feedrecentchanges-example-simple', |
218 | 'action=feedrecentchanges&days=30' |
219 | => 'apihelp-feedrecentchanges-example-30days', |
220 | ]; |
221 | } |
222 | |
223 | public function getHelpUrls() { |
224 | return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Feedrecentchanges'; |
225 | } |
226 | } |