xref: /dokuwiki/inc/Feed/FeedCreator.php (revision 72c714a3b85fbc49a7d75bd8d9e49e1a2f0c125a)
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
53fe9d054bSAndreas Gohr        foreach ($items as $item) {
54fe9d054bSAndreas Gohr            $this->createAndAddItem($item);
55fe9d054bSAndreas Gohr        }
56fe9d054bSAndreas Gohr
57fe9d054bSAndreas Gohr        return $this->feed->createFeed($this->options->get('type'));
58fe9d054bSAndreas Gohr    }
59fe9d054bSAndreas Gohr
60fe9d054bSAndreas Gohr    /**
61fe9d054bSAndreas Gohr     * Process the raw data, create feed item and add it to the feed
62fe9d054bSAndreas Gohr     *
63fe9d054bSAndreas Gohr     * @param array|string $data raw item data
64fe9d054bSAndreas Gohr     * @return \FeedItem
65fe9d054bSAndreas Gohr     * @triggers FEED_ITEM_ADD
66fe9d054bSAndreas Gohr     */
67fe9d054bSAndreas Gohr    protected function createAndAddItem($data)
68fe9d054bSAndreas Gohr    {
69fe9d054bSAndreas Gohr        if (is_string($data)) {
70fe9d054bSAndreas Gohr            $data = ['id' => $data];
71fe9d054bSAndreas Gohr        }
72fe9d054bSAndreas Gohr
73fe9d054bSAndreas Gohr        if (($data['mode'] ?? '') == 'media' || isset($data['media'])) {
74fe9d054bSAndreas Gohr            $data['id'] = $data['media'] ?? $data['id'];
75fe9d054bSAndreas Gohr            $proc = new FeedMediaProcessor($data);
76fe9d054bSAndreas Gohr        } else {
77fe9d054bSAndreas Gohr            $proc = new FeedPageProcessor($data);
78fe9d054bSAndreas Gohr        }
79fe9d054bSAndreas Gohr
80fe9d054bSAndreas Gohr        $item = new \FeedItem();
81fe9d054bSAndreas Gohr        $item->title = $proc->getTitle();
82fe9d054bSAndreas Gohr        if ($this->options->get('show_summary') && $proc->getSummary()) {
83fe9d054bSAndreas Gohr            $item->title .= ' - ' . $proc->getSummary();
84fe9d054bSAndreas Gohr        }
854554d54dSAndreas Gohr        $item->date = $proc->getRev();
86fe9d054bSAndreas Gohr        [$item->authorEmail, $item->author] = $proc->getAuthor();
87fe9d054bSAndreas Gohr        $item->link = $proc->getURL($this->options->get('link_to'));
88fe9d054bSAndreas Gohr        $item->description = $proc->getBody($this->options->get('item_content'));
89fe9d054bSAndreas Gohr
90fe9d054bSAndreas Gohr        $evdata = [
91fe9d054bSAndreas Gohr            'item' => $item,
92fe9d054bSAndreas Gohr            'opt' => &$this->options->options,
93fe9d054bSAndreas Gohr            'ditem' => &$data,
94fe9d054bSAndreas Gohr            'rss' => $this->feed,
95fe9d054bSAndreas Gohr        ];
96fe9d054bSAndreas Gohr
97fe9d054bSAndreas Gohr        $evt = new Event('FEED_ITEM_ADD', $evdata);
98fe9d054bSAndreas Gohr        if ($evt->advise_before()) {
99fe9d054bSAndreas Gohr            $this->feed->addItem($item);
100fe9d054bSAndreas Gohr        }
101fe9d054bSAndreas Gohr        $evt->advise_after();
102fe9d054bSAndreas Gohr
103fe9d054bSAndreas Gohr        return $item;
104fe9d054bSAndreas Gohr    }
105fe9d054bSAndreas Gohr
106fe9d054bSAndreas Gohr    /**
107fe9d054bSAndreas Gohr     * Read all pages from a namespace
108fe9d054bSAndreas Gohr     *
109fe9d054bSAndreas Gohr     * @todo this currently does not honor the rss_media setting and only ever lists pages
110fe9d054bSAndreas Gohr     * @return array
111fe9d054bSAndreas Gohr     */
112fe9d054bSAndreas Gohr    protected function fetchItemsFromNamespace()
113fe9d054bSAndreas Gohr    {
114fe9d054bSAndreas Gohr        global $conf;
115fe9d054bSAndreas Gohr
116fe9d054bSAndreas Gohr        $ns = ':' . cleanID($this->options->get('namespace'));
117fe9d054bSAndreas Gohr        $ns = utf8_encodeFN(str_replace(':', '/', $ns));
118*72c714a3Ssplitbrain
119fe9d054bSAndreas Gohr        $data = [];
120fe9d054bSAndreas Gohr        $search_opts = [
121fe9d054bSAndreas Gohr            'depth' => 1,
122fe9d054bSAndreas Gohr            'pagesonly' => true,
123fe9d054bSAndreas Gohr            'listfiles' => true
124fe9d054bSAndreas Gohr        ];
125fe9d054bSAndreas Gohr        search(
126fe9d054bSAndreas Gohr            $data,
127fe9d054bSAndreas Gohr            $conf['datadir'],
128fe9d054bSAndreas Gohr            'search_universal',
129fe9d054bSAndreas Gohr            $search_opts,
130fe9d054bSAndreas Gohr            $ns,
131fe9d054bSAndreas Gohr            $lvl = 1,
132fe9d054bSAndreas Gohr            $this->options->get('sort')
133fe9d054bSAndreas Gohr        );
134fe9d054bSAndreas Gohr
135fe9d054bSAndreas Gohr        return $data;
136fe9d054bSAndreas Gohr    }
137fe9d054bSAndreas Gohr
138fe9d054bSAndreas Gohr    /**
139fe9d054bSAndreas Gohr     * Add the result of a full text search to the feed object
140fe9d054bSAndreas Gohr     *
141fe9d054bSAndreas Gohr     * @return array
142fe9d054bSAndreas Gohr     */
143fe9d054bSAndreas Gohr    protected function fetchItemsFromSearch()
144fe9d054bSAndreas Gohr    {
145fe9d054bSAndreas Gohr        if (!actionOK('search')) throw new \RuntimeException('search is disabled');
146fe9d054bSAndreas Gohr        if (!$this->options->get('search_query')) return [];
147fe9d054bSAndreas Gohr
148fe9d054bSAndreas Gohr        $data = ft_pageSearch($this->options->get('search_query'), $poswords);
149fe9d054bSAndreas Gohr        return array_keys($data);
150fe9d054bSAndreas Gohr    }
151fe9d054bSAndreas Gohr
152fe9d054bSAndreas Gohr    /**
153fe9d054bSAndreas Gohr     * Add recent changed pages to the feed object
154fe9d054bSAndreas Gohr     *
155fe9d054bSAndreas Gohr     * @return array
156fe9d054bSAndreas Gohr     */
157fe9d054bSAndreas Gohr    protected function fetchItemsFromRecentChanges()
158fe9d054bSAndreas Gohr    {
159fe9d054bSAndreas Gohr        global $conf;
160fe9d054bSAndreas Gohr        $flags = 0;
161fe9d054bSAndreas Gohr        if (!$this->options->get('show_deleted')) $flags += RECENTS_SKIP_DELETED;
162fe9d054bSAndreas Gohr        if (!$this->options->get('show_minor')) $flags += RECENTS_SKIP_MINORS;
163fe9d054bSAndreas Gohr        if ($this->options->get('only_new')) $flags += RECENTS_ONLY_CREATION;
164fe9d054bSAndreas Gohr        if ($this->options->get('content_type') == 'media' && $conf['mediarevisions']) {
165fe9d054bSAndreas Gohr            $flags += RECENTS_MEDIA_CHANGES;
166fe9d054bSAndreas Gohr        }
167fe9d054bSAndreas Gohr        if ($this->options->get('content_type') == 'both' && $conf['mediarevisions']) {
168fe9d054bSAndreas Gohr            $flags += RECENTS_MEDIA_PAGES_MIXED;
169fe9d054bSAndreas Gohr        }
170fe9d054bSAndreas Gohr
171fe9d054bSAndreas Gohr        return getRecents(0, $this->options->get('items'), $this->options->get('namespace'), $flags);
172fe9d054bSAndreas Gohr    }
173fe9d054bSAndreas Gohr
174fe9d054bSAndreas Gohr    /**
175fe9d054bSAndreas Gohr     * Add items from a plugin to the feed object
176fe9d054bSAndreas Gohr     *
177fe9d054bSAndreas Gohr     * @triggers FEED_MODE_UNKNOWN
178fe9d054bSAndreas Gohr     * @return array
179fe9d054bSAndreas Gohr     */
180fe9d054bSAndreas Gohr    protected function fetchItemsFromPlugin()
181fe9d054bSAndreas Gohr    {
182fe9d054bSAndreas Gohr        $eventData = [
183fe9d054bSAndreas Gohr            'opt' => $this->options->options,
184fe9d054bSAndreas Gohr            'data' => [],
185fe9d054bSAndreas Gohr        ];
186fe9d054bSAndreas Gohr        $event = new Event('FEED_MODE_UNKNOWN', $eventData);
187fe9d054bSAndreas Gohr        if ($event->advise_before(true)) {
188fe9d054bSAndreas Gohr            throw new \RuntimeException('unknown feed mode');
189fe9d054bSAndreas Gohr        }
190fe9d054bSAndreas Gohr        $event->advise_after();
191fe9d054bSAndreas Gohr
192fe9d054bSAndreas Gohr        return $eventData['data'];
193fe9d054bSAndreas Gohr    }
194fe9d054bSAndreas Gohr
195fe9d054bSAndreas Gohr    /**
196fe9d054bSAndreas Gohr     * Add a logo to the feed
197fe9d054bSAndreas Gohr     *
198fe9d054bSAndreas Gohr     * Looks at different possible candidates for a logo and adds the first one
199fe9d054bSAndreas Gohr     *
200fe9d054bSAndreas Gohr     * @return void
201fe9d054bSAndreas Gohr     */
202fe9d054bSAndreas Gohr    protected function initLogo()
203fe9d054bSAndreas Gohr    {
204fe9d054bSAndreas Gohr        global $conf;
205fe9d054bSAndreas Gohr
206fe9d054bSAndreas Gohr        $this->feed->image = new \FeedImage();
207fe9d054bSAndreas Gohr        $this->feed->image->title = $conf['title'];
208fe9d054bSAndreas Gohr        $this->feed->image->link = DOKU_URL;
209fe9d054bSAndreas Gohr        $this->feed->image->url = tpl_getMediaFile([
210fe9d054bSAndreas Gohr            ':wiki:logo.svg',
211fe9d054bSAndreas Gohr            ':logo.svg',
212fe9d054bSAndreas Gohr            ':wiki:logo.png',
213fe9d054bSAndreas Gohr            ':logo.png',
214fe9d054bSAndreas Gohr            ':wiki:logo.jpg',
215fe9d054bSAndreas Gohr            ':logo.jpg',
216fe9d054bSAndreas Gohr            ':wiki:favicon.ico',
217fe9d054bSAndreas Gohr            ':favicon.ico',
218cf9a4884SAndreas Gohr            ':wiki:dokuwiki.svg',
219cf9a4884SAndreas Gohr            ':wiki:dokuwiki-128.png',
220fe9d054bSAndreas Gohr            'images/favicon.ico'
221fe9d054bSAndreas Gohr        ], true);
222fe9d054bSAndreas Gohr    }
223fe9d054bSAndreas Gohr}
224