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