Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
72.22% covered (warning)
72.22%
52 / 72
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
CargoFeedFormat
72.22% covered (warning)
72.22%
52 / 72
0.00% covered (danger)
0.00%
0 / 4
28.57
0.00% covered (danger)
0.00%
0 / 1
 allowedParameters
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 queryAndDisplay
76.92% covered (warning)
76.92%
10 / 13
0.00% covered (danger)
0.00%
0 / 1
7.60
 getFeedType
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 outputFeed
89.36% covered (warning)
89.36%
42 / 47
0.00% covered (danger)
0.00%
0 / 1
10.12
1<?php
2/**
3 * @ingroup Cargo
4 * @file
5 */
6
7use MediaWiki\Feed\AtomFeed;
8use MediaWiki\Feed\FeedItem;
9use MediaWiki\Feed\RSSFeed;
10use MediaWiki\Html\Html;
11use MediaWiki\MediaWikiServices;
12use MediaWiki\Title\Title;
13
14/**
15 * Handle the feed export format.
16 * @since 3.5
17 */
18class CargoFeedFormat extends CargoDeferredFormat {
19
20    public static function allowedParameters() {
21        return [
22            'feed type' => [ 'values' => [ 'atom', 'rss' ] ],
23            'link text' => [ 'type' => 'string' ],
24            'feed title' => [ 'type' => 'string' ],
25            'feed description' => [ 'type' => 'string' ],
26        ];
27    }
28
29    /**
30     * @param CargoSQLQuery[] $sqlQueries
31     * @param string[] $displayParams Unused
32     * @param string[]|null $querySpecificParams Unused
33     * @return string An HTML link to Special:CargoExport with the required query string.
34     */
35    public function queryAndDisplay( $sqlQueries, $displayParams, $querySpecificParams = null ) {
36        $queryParams = $this->sqlQueriesToQueryParams( $sqlQueries );
37        $queryParams['format'] = 'feed';
38
39        // Feed type. Defaults to the first one set in $wgAdvertisedFeedTypes.
40        $queryParams['feed type'] = $this->getFeedType( $displayParams['feed type'] ?? null );
41
42        // Feed title.
43        if ( isset( $displayParams['feed title'] ) && trim( $displayParams['feed title'] ) != '' ) {
44            $queryParams['feed title'] = $displayParams['feed title'];
45        }
46
47        // Feed description.
48        if ( isset( $displayParams['feed description'] ) && trim( $displayParams['feed description'] ) !== '' ) {
49            $queryParams['feed description'] = $displayParams['feed description'];
50        }
51
52        // Link.
53        if ( isset( $displayParams['link text'] ) && $displayParams['link text'] ) {
54            $linkText = $displayParams['link text'];
55        } else {
56            // The following messages can be used here:
57            // * feed-rss
58            // * feed-atom
59            $feedName = wfMessage( 'feed-' . $queryParams['feed type'] );
60            $linkText = wfMessage( 'cargo-viewfeed', [ $feedName ] )->parse();
61        }
62
63        // Output full anchor element. The array_filter is to avoid empty params.
64        $export = SpecialPage::getTitleFor( 'CargoExport' );
65        return Html::element( 'a', [ 'href' => $export->getFullURL( array_filter( $queryParams ) ) ], $linkText );
66    }
67
68    /**
69     * Determine the feed type (Atom or RSS).
70     * @param string|null $in The user-provided value.
71     * @return string Either 'atom' or 'rss'.
72     */
73    private function getFeedType( ?string $in = null ): string {
74        $config = MediaWikiServices::getInstance()->getMainConfig();
75        $types = array_keys( $config->get( 'FeedClasses' ) );
76
77        // User-provided (if it's valid).
78        $inType = strtolower( trim( $in ?? '' ) );
79        if ( in_array( $inType, $types ) ) {
80            return $inType;
81        }
82
83        // Otherwise, fall back to Atom.
84        return 'atom';
85    }
86
87    /**
88     * Get the iCalendar format output.
89     * @param WebRequest $request
90     * @param CargoSQLQuery[] $sqlQueries
91     */
92    public function outputFeed( WebRequest $request, $sqlQueries ) {
93        global $wgOut;
94
95        $queryParams = $this->sqlQueriesToQueryParams( $sqlQueries );
96        $queryParams['format'] = 'feed';
97        $feedType = $this->getFeedType( $request->getText( 'feed_type' ) );
98        $title = $request->getText( 'feed_title', 'News feed' );
99        $description = $request->getText( 'feed_description', '' );
100
101        $feedClasses = MediaWikiServices::getInstance()->getMainConfig()->get( 'FeedClasses' );
102        /** @var RSSFeed|AtomFeed $feed */
103        $feed = new $feedClasses[$feedType]( $title, $description, $request->getFullRequestURL() );
104
105        $services = MediaWikiServices::getInstance();
106        $parser = $services->getParser();
107        $pageTitle = $parser->getTitle();
108        $parserOptions = ParserOptions::newFromAnon();
109        $parserOptions->setSuppressSectionEditLinks();
110        $contentRenderer = $services->getContentRenderer();
111        $items = [];
112        foreach ( $sqlQueries as $sqlQuery ) {
113            $dateFields = $sqlQuery->getMainStartAndEndDateFields();
114
115            $queryResults = $sqlQuery->run();
116            foreach ( $queryResults as $queryResult ) {
117                $title = Title::newFromText( $queryResult['_pageName'] );
118                if ( isset( $queryResult['description'] ) ) {
119                    $description = $queryResult['description'];
120                    $parserOutput = $parser->parse( $description, $pageTitle, $parserOptions );
121                } else {
122                    $wikiPage = new WikiPage( $title );
123                    $parserOutput = $contentRenderer->getParserOutput( $wikiPage->getContent(), $title, null, $parserOptions );
124                }
125                $description = $parserOutput->runOutputPipeline( $parserOptions )->getContentHolderText();
126                $item = new FeedItem(
127                    htmlspecialchars_decode( $queryResult['title'] ?? $queryResult['_pageName'] ?? '' ),
128                    $description,
129                    $queryResult['url'] ?? $title->getCanonicalURL(),
130                    $queryResult[$dateFields[0]] ?? '',
131                    $queryResult['author'] ?? '',
132                    $queryResult['comments'] ?? ''
133                );
134                if ( isset( $queryResult['id'] ) && !empty( $queryResult['id'] ) ) {
135                    $item->setUniqueId( $queryResult['id'] );
136                } else {
137                    $item->setUniqueId( $title->getCanonicalURL(), true );
138                }
139                $items[$queryResult[$dateFields[0]]] = $item;
140            }
141        }
142
143        // Output feed content.
144        if ( method_exists( $feed, 'outputHeader' ) ) {
145            // MW 1.46+
146            $feed->outputHeader( $wgOut );
147        } else {
148            $feed->outHeader();
149        }
150        foreach ( $items as $item ) {
151            if ( method_exists( $feed, 'outputItem' ) ) {
152                // MW 1.46+
153                $feed->outputItem( $item, $wgOut );
154            } else {
155                $feed->outItem( $item );
156            }
157        }
158        if ( method_exists( $feed, 'outputFooter' ) ) {
159            // MW 1.46+
160            $feed->outputFooter( $wgOut );
161        } else {
162            $feed->outFooter();
163        }
164    }
165}