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