You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

233 lines
6.4 KiB

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