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