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)); 118fe9d054bSAndreas Gohr $data = []; 119fe9d054bSAndreas Gohr $search_opts = [ 120fe9d054bSAndreas Gohr 'depth' => 1, 121fe9d054bSAndreas Gohr 'pagesonly' => true, 122fe9d054bSAndreas Gohr 'listfiles' => true 123fe9d054bSAndreas Gohr ]; 124fe9d054bSAndreas Gohr search( 125fe9d054bSAndreas Gohr $data, 126fe9d054bSAndreas Gohr $conf['datadir'], 127fe9d054bSAndreas Gohr 'search_universal', 128fe9d054bSAndreas Gohr $search_opts, 129fe9d054bSAndreas Gohr $ns, 130fe9d054bSAndreas Gohr $lvl = 1, 131fe9d054bSAndreas Gohr $this->options->get('sort') 132fe9d054bSAndreas Gohr ); 133fe9d054bSAndreas Gohr 134fe9d054bSAndreas Gohr return $data; 135fe9d054bSAndreas Gohr } 136fe9d054bSAndreas Gohr 137fe9d054bSAndreas Gohr /** 138fe9d054bSAndreas Gohr * Add the result of a full text search to the feed object 139fe9d054bSAndreas Gohr * 140fe9d054bSAndreas Gohr * @return array 141fe9d054bSAndreas Gohr */ 142fe9d054bSAndreas Gohr protected function fetchItemsFromSearch() 143fe9d054bSAndreas Gohr { 144fe9d054bSAndreas Gohr if (!actionOK('search')) throw new \RuntimeException('search is disabled'); 145fe9d054bSAndreas Gohr if (!$this->options->get('search_query')) return []; 146fe9d054bSAndreas Gohr 147fe9d054bSAndreas Gohr $data = ft_pageSearch($this->options->get('search_query'), $poswords); 148fe9d054bSAndreas Gohr return array_keys($data); 149fe9d054bSAndreas Gohr } 150fe9d054bSAndreas Gohr 151fe9d054bSAndreas Gohr /** 152fe9d054bSAndreas Gohr * Add recent changed pages to the feed object 153fe9d054bSAndreas Gohr * 154fe9d054bSAndreas Gohr * @return array 155fe9d054bSAndreas Gohr */ 156fe9d054bSAndreas Gohr protected function fetchItemsFromRecentChanges() 157fe9d054bSAndreas Gohr { 158fe9d054bSAndreas Gohr global $conf; 159fe9d054bSAndreas Gohr $flags = 0; 160fe9d054bSAndreas Gohr if (!$this->options->get('show_deleted')) $flags += RECENTS_SKIP_DELETED; 161fe9d054bSAndreas Gohr if (!$this->options->get('show_minor')) $flags += RECENTS_SKIP_MINORS; 162fe9d054bSAndreas Gohr if ($this->options->get('only_new')) $flags += RECENTS_ONLY_CREATION; 163fe9d054bSAndreas Gohr if ($this->options->get('content_type') == 'media' && $conf['mediarevisions']) { 164fe9d054bSAndreas Gohr $flags += RECENTS_MEDIA_CHANGES; 165fe9d054bSAndreas Gohr } 166fe9d054bSAndreas Gohr if ($this->options->get('content_type') == 'both' && $conf['mediarevisions']) { 167fe9d054bSAndreas Gohr $flags += RECENTS_MEDIA_PAGES_MIXED; 168fe9d054bSAndreas Gohr } 169fe9d054bSAndreas Gohr 170fe9d054bSAndreas Gohr return getRecents(0, $this->options->get('items'), $this->options->get('namespace'), $flags); 171fe9d054bSAndreas Gohr } 172fe9d054bSAndreas Gohr 173fe9d054bSAndreas Gohr /** 174fe9d054bSAndreas Gohr * Add items from a plugin to the feed object 175fe9d054bSAndreas Gohr * 176fe9d054bSAndreas Gohr * @triggers FEED_MODE_UNKNOWN 177fe9d054bSAndreas Gohr * @return array 178fe9d054bSAndreas Gohr */ 179fe9d054bSAndreas Gohr protected function fetchItemsFromPlugin() 180fe9d054bSAndreas Gohr { 181fe9d054bSAndreas Gohr $eventData = [ 182fe9d054bSAndreas Gohr 'opt' => $this->options->options, 183fe9d054bSAndreas Gohr 'data' => [], 184fe9d054bSAndreas Gohr ]; 185fe9d054bSAndreas Gohr $event = new Event('FEED_MODE_UNKNOWN', $eventData); 186fe9d054bSAndreas Gohr if ($event->advise_before(true)) { 187fe9d054bSAndreas Gohr throw new \RuntimeException('unknown feed mode'); 188fe9d054bSAndreas Gohr } 189fe9d054bSAndreas Gohr $event->advise_after(); 190fe9d054bSAndreas Gohr 191fe9d054bSAndreas Gohr return $eventData['data']; 192fe9d054bSAndreas Gohr } 193fe9d054bSAndreas Gohr 194fe9d054bSAndreas Gohr /** 195fe9d054bSAndreas Gohr * Add a logo to the feed 196fe9d054bSAndreas Gohr * 197fe9d054bSAndreas Gohr * Looks at different possible candidates for a logo and adds the first one 198fe9d054bSAndreas Gohr * 199fe9d054bSAndreas Gohr * @return void 200fe9d054bSAndreas Gohr */ 201fe9d054bSAndreas Gohr protected function initLogo() 202fe9d054bSAndreas Gohr { 203fe9d054bSAndreas Gohr global $conf; 204fe9d054bSAndreas Gohr 205fe9d054bSAndreas Gohr $this->feed->image = new \FeedImage(); 206fe9d054bSAndreas Gohr $this->feed->image->title = $conf['title']; 207fe9d054bSAndreas Gohr $this->feed->image->link = DOKU_URL; 208fe9d054bSAndreas Gohr $this->feed->image->url = tpl_getMediaFile([ 209fe9d054bSAndreas Gohr ':wiki:logo.svg', 210fe9d054bSAndreas Gohr ':logo.svg', 211fe9d054bSAndreas Gohr ':wiki:logo.png', 212fe9d054bSAndreas Gohr ':logo.png', 213fe9d054bSAndreas Gohr ':wiki:logo.jpg', 214fe9d054bSAndreas Gohr ':logo.jpg', 215fe9d054bSAndreas Gohr ':wiki:favicon.ico', 216fe9d054bSAndreas Gohr ':favicon.ico', 217*cf9a4884SAndreas Gohr ':wiki:dokuwiki.svg', 218*cf9a4884SAndreas Gohr ':wiki:dokuwiki-128.png', 219fe9d054bSAndreas Gohr 'images/favicon.ico' 220fe9d054bSAndreas Gohr ], true); 221fe9d054bSAndreas Gohr } 222fe9d054bSAndreas Gohr} 223