xref: /dokuwiki/inc/Feed/FeedCreator.php (revision fe9d054b302adfcd4618e9e33079c36cccb90d10)
1*fe9d054bSAndreas Gohr<?php
2*fe9d054bSAndreas Gohr
3*fe9d054bSAndreas Gohrnamespace dokuwiki\Feed;
4*fe9d054bSAndreas Gohr
5*fe9d054bSAndreas Gohruse dokuwiki\Extension\Event;
6*fe9d054bSAndreas Gohr
7*fe9d054bSAndreas Gohrclass FeedCreator
8*fe9d054bSAndreas Gohr{
9*fe9d054bSAndreas Gohr
10*fe9d054bSAndreas Gohr    /** @var \UniversalFeedCreator */
11*fe9d054bSAndreas Gohr    protected $feed;
12*fe9d054bSAndreas Gohr
13*fe9d054bSAndreas Gohr    /** @var FeedCreatorOptions */
14*fe9d054bSAndreas Gohr    protected $options;
15*fe9d054bSAndreas Gohr
16*fe9d054bSAndreas Gohr    /**
17*fe9d054bSAndreas Gohr     * @param FeedCreatorOptions $options
18*fe9d054bSAndreas Gohr     */
19*fe9d054bSAndreas Gohr    public function __construct(FeedCreatorOptions $options)
20*fe9d054bSAndreas Gohr    {
21*fe9d054bSAndreas Gohr        $this->options = $options;
22*fe9d054bSAndreas Gohr
23*fe9d054bSAndreas Gohr        $this->feed = new \UniversalFeedCreator();
24*fe9d054bSAndreas Gohr        $this->feed->title = $this->options->get('title');
25*fe9d054bSAndreas Gohr        $this->feed->link = DOKU_URL;
26*fe9d054bSAndreas Gohr        $this->feed->syndicationURL = DOKU_URL . 'feed.php';
27*fe9d054bSAndreas Gohr        $this->feed->cssStyleSheet = DOKU_URL . 'lib/exe/css.php?s=feed';
28*fe9d054bSAndreas Gohr
29*fe9d054bSAndreas Gohr        $this->initLogo();
30*fe9d054bSAndreas Gohr    }
31*fe9d054bSAndreas Gohr
32*fe9d054bSAndreas Gohr    /**
33*fe9d054bSAndreas Gohr     * Build the feed
34*fe9d054bSAndreas Gohr     *
35*fe9d054bSAndreas Gohr     * @return string The raw XML for the feed
36*fe9d054bSAndreas Gohr     */
37*fe9d054bSAndreas Gohr    public function build()
38*fe9d054bSAndreas Gohr    {
39*fe9d054bSAndreas Gohr        switch ($this->options->get('feed_mode')) {
40*fe9d054bSAndreas Gohr            case 'list':
41*fe9d054bSAndreas Gohr                $items = $this->fetchItemsFromNamespace();
42*fe9d054bSAndreas Gohr                break;
43*fe9d054bSAndreas Gohr            case 'search':
44*fe9d054bSAndreas Gohr                $items = $this->fetchItemsFromSearch();
45*fe9d054bSAndreas Gohr                break;
46*fe9d054bSAndreas Gohr            case 'recent':
47*fe9d054bSAndreas Gohr                $items = $this->fetchItemsFromRecentChanges();
48*fe9d054bSAndreas Gohr                break;
49*fe9d054bSAndreas Gohr            default:
50*fe9d054bSAndreas Gohr                $items = $this->fetchItemsFromPlugin();
51*fe9d054bSAndreas Gohr        }
52*fe9d054bSAndreas Gohr
53*fe9d054bSAndreas Gohr        foreach ($items as $item) {
54*fe9d054bSAndreas Gohr            $this->createAndAddItem($item);
55*fe9d054bSAndreas Gohr        }
56*fe9d054bSAndreas Gohr
57*fe9d054bSAndreas Gohr        return $this->feed->createFeed($this->options->get('type'));
58*fe9d054bSAndreas Gohr    }
59*fe9d054bSAndreas Gohr
60*fe9d054bSAndreas Gohr    /**
61*fe9d054bSAndreas Gohr     * Process the raw data, create feed item and add it to the feed
62*fe9d054bSAndreas Gohr     *
63*fe9d054bSAndreas Gohr     * @param array|string $data raw item data
64*fe9d054bSAndreas Gohr     * @return \FeedItem
65*fe9d054bSAndreas Gohr     * @triggers FEED_ITEM_ADD
66*fe9d054bSAndreas Gohr     */
67*fe9d054bSAndreas Gohr    protected function createAndAddItem($data)
68*fe9d054bSAndreas Gohr    {
69*fe9d054bSAndreas Gohr        if (is_string($data)) {
70*fe9d054bSAndreas Gohr            $data = ['id' => $data];
71*fe9d054bSAndreas Gohr        }
72*fe9d054bSAndreas Gohr
73*fe9d054bSAndreas Gohr        if (($data['mode'] ?? '') == 'media' || isset($data['media'])) {
74*fe9d054bSAndreas Gohr            $data['id'] = $data['media'] ?? $data['id'];
75*fe9d054bSAndreas Gohr            $proc = new FeedMediaProcessor($data);
76*fe9d054bSAndreas Gohr        } else {
77*fe9d054bSAndreas Gohr            $proc = new FeedPageProcessor($data);
78*fe9d054bSAndreas Gohr        }
79*fe9d054bSAndreas Gohr
80*fe9d054bSAndreas Gohr        $item = new \FeedItem();
81*fe9d054bSAndreas Gohr        $item->title = $proc->getTitle();
82*fe9d054bSAndreas Gohr        if ($this->options->get('show_summary') && $proc->getSummary()) {
83*fe9d054bSAndreas Gohr            $item->title .= ' - ' . $proc->getSummary();
84*fe9d054bSAndreas Gohr        }
85*fe9d054bSAndreas Gohr        [$item->authorEmail, $item->author] = $proc->getAuthor();
86*fe9d054bSAndreas Gohr        $item->link = $proc->getURL($this->options->get('link_to'));
87*fe9d054bSAndreas Gohr        $item->description = $proc->getBody($this->options->get('item_content'));
88*fe9d054bSAndreas Gohr
89*fe9d054bSAndreas Gohr        $evdata = [
90*fe9d054bSAndreas Gohr            'item' => $item,
91*fe9d054bSAndreas Gohr            'opt' => &$this->options->options,
92*fe9d054bSAndreas Gohr            'ditem' => &$data,
93*fe9d054bSAndreas Gohr            'rss' => $this->feed,
94*fe9d054bSAndreas Gohr        ];
95*fe9d054bSAndreas Gohr
96*fe9d054bSAndreas Gohr        $evt = new Event('FEED_ITEM_ADD', $evdata);
97*fe9d054bSAndreas Gohr        if ($evt->advise_before()) {
98*fe9d054bSAndreas Gohr            $this->feed->addItem($item);
99*fe9d054bSAndreas Gohr        }
100*fe9d054bSAndreas Gohr        $evt->advise_after();
101*fe9d054bSAndreas Gohr
102*fe9d054bSAndreas Gohr        return $item;
103*fe9d054bSAndreas Gohr    }
104*fe9d054bSAndreas Gohr
105*fe9d054bSAndreas Gohr    /**
106*fe9d054bSAndreas Gohr     * Read all pages from a namespace
107*fe9d054bSAndreas Gohr     *
108*fe9d054bSAndreas Gohr     * @todo this currently does not honor the rss_media setting and only ever lists pages
109*fe9d054bSAndreas Gohr     * @return array
110*fe9d054bSAndreas Gohr     */
111*fe9d054bSAndreas Gohr    protected function fetchItemsFromNamespace()
112*fe9d054bSAndreas Gohr    {
113*fe9d054bSAndreas Gohr        global $conf;
114*fe9d054bSAndreas Gohr
115*fe9d054bSAndreas Gohr        $ns = ':' . cleanID($this->options->get('namespace'));
116*fe9d054bSAndreas Gohr        $ns = utf8_encodeFN(str_replace(':', '/', $ns));
117*fe9d054bSAndreas Gohr        $data = [];
118*fe9d054bSAndreas Gohr        $search_opts = [
119*fe9d054bSAndreas Gohr            'depth' => 1,
120*fe9d054bSAndreas Gohr            'pagesonly' => true,
121*fe9d054bSAndreas Gohr            'listfiles' => true
122*fe9d054bSAndreas Gohr        ];
123*fe9d054bSAndreas Gohr        search(
124*fe9d054bSAndreas Gohr            $data,
125*fe9d054bSAndreas Gohr            $conf['datadir'],
126*fe9d054bSAndreas Gohr            'search_universal',
127*fe9d054bSAndreas Gohr            $search_opts,
128*fe9d054bSAndreas Gohr            $ns,
129*fe9d054bSAndreas Gohr            $lvl = 1,
130*fe9d054bSAndreas Gohr            $this->options->get('sort')
131*fe9d054bSAndreas Gohr        );
132*fe9d054bSAndreas Gohr
133*fe9d054bSAndreas Gohr        return $data;
134*fe9d054bSAndreas Gohr    }
135*fe9d054bSAndreas Gohr
136*fe9d054bSAndreas Gohr    /**
137*fe9d054bSAndreas Gohr     * Add the result of a full text search to the feed object
138*fe9d054bSAndreas Gohr     *
139*fe9d054bSAndreas Gohr     * @return array
140*fe9d054bSAndreas Gohr     */
141*fe9d054bSAndreas Gohr    protected function fetchItemsFromSearch()
142*fe9d054bSAndreas Gohr    {
143*fe9d054bSAndreas Gohr        if (!actionOK('search')) throw new \RuntimeException('search is disabled');
144*fe9d054bSAndreas Gohr        if (!$this->options->get('search_query')) return [];
145*fe9d054bSAndreas Gohr
146*fe9d054bSAndreas Gohr        $data = ft_pageSearch($this->options->get('search_query'), $poswords);
147*fe9d054bSAndreas Gohr        return array_keys($data);
148*fe9d054bSAndreas Gohr    }
149*fe9d054bSAndreas Gohr
150*fe9d054bSAndreas Gohr    /**
151*fe9d054bSAndreas Gohr     * Add recent changed pages to the feed object
152*fe9d054bSAndreas Gohr     *
153*fe9d054bSAndreas Gohr     * @return array
154*fe9d054bSAndreas Gohr     */
155*fe9d054bSAndreas Gohr    protected function fetchItemsFromRecentChanges()
156*fe9d054bSAndreas Gohr    {
157*fe9d054bSAndreas Gohr        global $conf;
158*fe9d054bSAndreas Gohr        $flags = 0;
159*fe9d054bSAndreas Gohr        if (!$this->options->get('show_deleted')) $flags += RECENTS_SKIP_DELETED;
160*fe9d054bSAndreas Gohr        if (!$this->options->get('show_minor')) $flags += RECENTS_SKIP_MINORS;
161*fe9d054bSAndreas Gohr        if ($this->options->get('only_new')) $flags += RECENTS_ONLY_CREATION;
162*fe9d054bSAndreas Gohr        if ($this->options->get('content_type') == 'media' && $conf['mediarevisions']) {
163*fe9d054bSAndreas Gohr            $flags += RECENTS_MEDIA_CHANGES;
164*fe9d054bSAndreas Gohr        }
165*fe9d054bSAndreas Gohr        if ($this->options->get('content_type') == 'both' && $conf['mediarevisions']) {
166*fe9d054bSAndreas Gohr            $flags += RECENTS_MEDIA_PAGES_MIXED;
167*fe9d054bSAndreas Gohr        }
168*fe9d054bSAndreas Gohr
169*fe9d054bSAndreas Gohr        return getRecents(0, $this->options->get('items'), $this->options->get('namespace'), $flags);
170*fe9d054bSAndreas Gohr    }
171*fe9d054bSAndreas Gohr
172*fe9d054bSAndreas Gohr    /**
173*fe9d054bSAndreas Gohr     * Add items from a plugin to the feed object
174*fe9d054bSAndreas Gohr     *
175*fe9d054bSAndreas Gohr     * @triggers FEED_MODE_UNKNOWN
176*fe9d054bSAndreas Gohr     * @return array
177*fe9d054bSAndreas Gohr     */
178*fe9d054bSAndreas Gohr    protected function fetchItemsFromPlugin()
179*fe9d054bSAndreas Gohr    {
180*fe9d054bSAndreas Gohr        $eventData = [
181*fe9d054bSAndreas Gohr            'opt' => $this->options->options,
182*fe9d054bSAndreas Gohr            'data' => [],
183*fe9d054bSAndreas Gohr        ];
184*fe9d054bSAndreas Gohr        $event = new Event('FEED_MODE_UNKNOWN', $eventData);
185*fe9d054bSAndreas Gohr        if ($event->advise_before(true)) {
186*fe9d054bSAndreas Gohr            throw new \RuntimeException('unknown feed mode');
187*fe9d054bSAndreas Gohr        }
188*fe9d054bSAndreas Gohr        $event->advise_after();
189*fe9d054bSAndreas Gohr
190*fe9d054bSAndreas Gohr        return $eventData['data'];
191*fe9d054bSAndreas Gohr    }
192*fe9d054bSAndreas Gohr
193*fe9d054bSAndreas Gohr    /**
194*fe9d054bSAndreas Gohr     * Add a logo to the feed
195*fe9d054bSAndreas Gohr     *
196*fe9d054bSAndreas Gohr     * Looks at different possible candidates for a logo and adds the first one
197*fe9d054bSAndreas Gohr     *
198*fe9d054bSAndreas Gohr     * @return void
199*fe9d054bSAndreas Gohr     */
200*fe9d054bSAndreas Gohr    protected function initLogo()
201*fe9d054bSAndreas Gohr    {
202*fe9d054bSAndreas Gohr        global $conf;
203*fe9d054bSAndreas Gohr
204*fe9d054bSAndreas Gohr        $this->feed->image = new \FeedImage();
205*fe9d054bSAndreas Gohr        $this->feed->image->title = $conf['title'];
206*fe9d054bSAndreas Gohr        $this->feed->image->link = DOKU_URL;
207*fe9d054bSAndreas Gohr        $this->feed->image->url = tpl_getMediaFile([
208*fe9d054bSAndreas Gohr            ':wiki:logo.svg',
209*fe9d054bSAndreas Gohr            ':logo.svg',
210*fe9d054bSAndreas Gohr            ':wiki:logo.png',
211*fe9d054bSAndreas Gohr            ':logo.png',
212*fe9d054bSAndreas Gohr            ':wiki:logo.jpg',
213*fe9d054bSAndreas Gohr            ':logo.jpg',
214*fe9d054bSAndreas Gohr            ':wiki:favicon.ico',
215*fe9d054bSAndreas Gohr            ':favicon.ico',
216*fe9d054bSAndreas Gohr            'images/favicon.ico'
217*fe9d054bSAndreas Gohr        ], true);
218*fe9d054bSAndreas Gohr    }
219*fe9d054bSAndreas Gohr}
220