Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 123 |
|
0.00% |
0 / 10 |
CRAP | |
0.00% |
0 / 1 |
FeaturedFeedChannel | |
0.00% |
0 / 123 |
|
0.00% |
0 / 10 |
1122 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
fromArray | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
12 | |||
toArray | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
12 | |||
msg | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isOK | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getLanguage | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
init | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
30 | |||
getFeedItems | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
56 | |||
getFeedItem | |
0.00% |
0 / 32 |
|
0.00% |
0 / 1 |
42 | |||
getURL | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\FeaturedFeeds; |
4 | |
5 | use MediaWiki\Content\TextContent; |
6 | use MediaWiki\Language\Language; |
7 | use MediaWiki\MediaWikiServices; |
8 | use MediaWiki\Message\Message; |
9 | use MediaWiki\Parser\Parser; |
10 | use MediaWiki\Parser\ParserOptions; |
11 | use MediaWiki\Revision\SlotRecord; |
12 | use MediaWiki\SpecialPage\SpecialPage; |
13 | use MediaWiki\Title\Title; |
14 | use MediaWiki\Utils\MWTimestamp; |
15 | use UnexpectedValueException; |
16 | |
17 | class FeaturedFeedChannel { |
18 | /** |
19 | * Class version, increment it when changing class internals. |
20 | */ |
21 | public const VERSION = 2; |
22 | |
23 | /** |
24 | * @var Parser |
25 | */ |
26 | private static $parser; |
27 | /** @var string */ |
28 | private $languageCode; |
29 | |
30 | /** @var string */ |
31 | private $name; |
32 | /** @var array */ |
33 | private $options; |
34 | /** |
35 | * @var FeaturedFeedItem[]|false |
36 | */ |
37 | private $items = false; |
38 | /** @var string|false */ |
39 | private $page = false; |
40 | /** @var string|false */ |
41 | private $entryName = false; |
42 | /** @var Title|false */ |
43 | private $titleForParse = false; |
44 | |
45 | /** @var string|false */ |
46 | public $title = false; |
47 | /** @var string|false */ |
48 | public $shortTitle = false; |
49 | /** @var string|false */ |
50 | public $description = false; |
51 | |
52 | /** |
53 | * @param string $name |
54 | * @param array $options |
55 | * @param string $languageCode |
56 | */ |
57 | public function __construct( $name, $options, $languageCode ) { |
58 | if ( !self::$parser ) { |
59 | self::$parser = MediaWikiServices::getInstance()->getParserFactory()->create(); |
60 | } |
61 | |
62 | $this->name = $name; |
63 | $this->options = $options; |
64 | if ( $options['inUserLanguage'] ) { |
65 | $this->languageCode = $languageCode; |
66 | } else { |
67 | $contLang = MediaWikiServices::getInstance()->getContentLanguage(); |
68 | $this->languageCode = $contLang->getCode(); |
69 | } |
70 | } |
71 | |
72 | public static function fromArray( array $array ): self { |
73 | $channel = new self( |
74 | $array['name'], |
75 | $array['options'], |
76 | $array['lang'] |
77 | ); |
78 | |
79 | if ( $array['items'] !== false ) { |
80 | $channel->items = []; |
81 | foreach ( $array['items'] as $item ) { |
82 | $channel->items[] = FeaturedFeedItem::fromArray( $item ); |
83 | } |
84 | } |
85 | |
86 | $channel->page = $array['page']; |
87 | $channel->entryName = $array['entryName']; |
88 | $channel->titleForParse = $array['titleForParse']; |
89 | $channel->title = $array['title']; |
90 | $channel->shortTitle = $array['shortTitle']; |
91 | $channel->description = $array['description']; |
92 | |
93 | return $channel; |
94 | } |
95 | |
96 | public function toArray(): array { |
97 | $items = false; |
98 | if ( $this->items !== false ) { |
99 | $items = []; |
100 | |
101 | foreach ( $this->items as $item ) { |
102 | $items[] = $item->toArray(); |
103 | } |
104 | } |
105 | |
106 | return [ |
107 | 'name' => $this->name, |
108 | 'options' => $this->options, |
109 | 'lang' => $this->languageCode, |
110 | 'items' => $items, |
111 | 'page' => $this->page, |
112 | 'entryName' => $this->entryName, |
113 | 'titleForParse' => $this->titleForParse, |
114 | 'title' => $this->title, |
115 | 'shortTitle' => $this->shortTitle, |
116 | 'description' => $this->description, |
117 | ]; |
118 | } |
119 | |
120 | /** |
121 | * @param string $key |
122 | * @return Message |
123 | */ |
124 | private function msg( $key ) { |
125 | return wfMessage( $key )->inLanguage( $this->languageCode ); |
126 | } |
127 | |
128 | /** |
129 | * @return bool |
130 | */ |
131 | public function isOK() { |
132 | $this->init(); |
133 | return $this->page !== false; |
134 | } |
135 | |
136 | /** |
137 | * Returns language used by the feed |
138 | * @return Language |
139 | */ |
140 | public function getLanguage() { |
141 | return MediaWikiServices::getInstance()->getLanguageFactory() |
142 | ->getLanguage( $this->languageCode ); |
143 | } |
144 | |
145 | public function init() { |
146 | global $wgLanguageCode; |
147 | if ( $this->title !== false ) { |
148 | return; |
149 | } |
150 | $this->title = $this->msg( $this->options['title'] )->text(); |
151 | $this->shortTitle = $this->msg( $this->options['short-title'] )->text(); |
152 | $this->description = $this->msg( $this->options['description'] )->text(); |
153 | $pageMsg = $this->msg( $this->options['page'] )->params( $this->languageCode ); |
154 | if ( $pageMsg->isDisabled() ) { |
155 | // fall back manually, messages can be existent but empty |
156 | if ( $this->languageCode != $wgLanguageCode ) { |
157 | $pageMsg = wfMessage( $this->options['page'] ) |
158 | ->params( $this->languageCode ) |
159 | ->inContentLanguage(); |
160 | } |
161 | } |
162 | if ( $pageMsg->isDisabled() ) { |
163 | return; |
164 | } |
165 | $this->page = $pageMsg->plain(); |
166 | $this->page = str_replace( '$LANGUAGE', $this->languageCode, $this->page ); |
167 | $this->entryName = $this->msg( $this->options['entryName'] )->plain(); |
168 | } |
169 | |
170 | /** |
171 | * @return FeaturedFeedItem[] |
172 | */ |
173 | public function getFeedItems() { |
174 | $this->init(); |
175 | if ( $this->items === false ) { |
176 | $this->items = []; |
177 | switch ( $this->options['frequency'] ) { |
178 | case 'daily': |
179 | $ratio = 1; |
180 | $baseTime = FeaturedFeeds::todaysStart(); |
181 | break; |
182 | case 'weekly': |
183 | $ratio = 7; |
184 | $baseTime = FeaturedFeeds::startOfThisWeek(); |
185 | break; |
186 | default: |
187 | throw new UnexpectedValueException( "'{$this->options['frequency']}' is not a valid frequency" ); |
188 | } |
189 | for ( $i = 1 - $this->options['limit']; $i <= 0; $i++ ) { |
190 | $timestamp = $baseTime + $i * $ratio * 24 * 3600; |
191 | $item = $this->getFeedItem( $timestamp ); |
192 | if ( $item ) { |
193 | $this->items[] = $item; |
194 | } |
195 | } |
196 | } |
197 | return $this->items; |
198 | } |
199 | |
200 | /** |
201 | * |
202 | * @param int $date |
203 | * @return FeaturedFeedItem|false |
204 | */ |
205 | public function getFeedItem( $date ) { |
206 | $ts = new MWTimestamp( $date ); |
207 | $timestamp = $ts->getTimestamp( TS_MW ); |
208 | $parserOptions = ParserOptions::newFromAnon(); |
209 | $parserOptions->setTimestamp( $timestamp ); |
210 | $parserOptions->setUserLang( $this->getLanguage() ); |
211 | |
212 | if ( $this->titleForParse === false ) { |
213 | // parsing with such title makes stuff like {{CURRENTMONTH}} localised |
214 | $this->titleForParse = Title::newFromText( 'MediaWiki:Dummy/' . $this->languageCode ); |
215 | } |
216 | |
217 | $titleText = self::$parser->transformMsg( |
218 | $this->page, $parserOptions, $this->titleForParse ); |
219 | $title = Title::newFromText( $titleText ); |
220 | if ( !$title ) { |
221 | return false; |
222 | } |
223 | $rev = MediaWikiServices::getInstance()->getRevisionLookup() |
224 | ->getRevisionByTitle( $title ); |
225 | if ( !$rev ) { |
226 | // Page does not exist |
227 | return false; |
228 | } |
229 | |
230 | $content = $rev->getContent( SlotRecord::MAIN ); |
231 | $text = ( $content instanceof TextContent ) ? $content->getText() : null; |
232 | |
233 | if ( !$text ) { |
234 | return false; |
235 | } |
236 | $text = self::$parser->parse( $text, $title, $parserOptions )->runOutputPipeline( $parserOptions, [ |
237 | 'enableSectionEditLinks' => false, |
238 | ] )->getContentHolderText(); |
239 | $url = SpecialPage::getTitleFor( 'FeedItem', |
240 | $this->name . '/' . $timestamp . '/' . $this->languageCode |
241 | )->getFullURL(); |
242 | |
243 | return new FeaturedFeedItem( |
244 | self::$parser->transformMsg( $this->entryName, $parserOptions, $this->titleForParse ), |
245 | wfExpandUrl( $url ), |
246 | $text, |
247 | $timestamp |
248 | ); |
249 | } |
250 | |
251 | /** |
252 | * Returns a URL to the feed |
253 | * |
254 | * @param string $format Feed format, 'rss' or 'atom' |
255 | * @return string |
256 | */ |
257 | public function getURL( $format ) { |
258 | $contLang = MediaWikiServices::getInstance()->getContentLanguage(); |
259 | |
260 | $options = [ |
261 | 'action' => 'featuredfeed', |
262 | 'feed' => $this->name, |
263 | 'feedformat' => $format, |
264 | ]; |
265 | if ( $this->options['inUserLanguage'] && $this->languageCode != $contLang->getCode() ) { |
266 | $options['language'] = $this->languageCode; |
267 | } |
268 | return wfScript( 'api' ) . '?' . wfArrayToCgi( $options ); |
269 | } |
270 | } |