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