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