Feed.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <?php
  2. /**
  3. * This file is part of the ForkBB <https://github.com/forkbb>.
  4. *
  5. * @copyright (c) Visman <mio.visman@yandex.ru, https://github.com/MioVisman>
  6. * @license The MIT License (MIT)
  7. */
  8. declare(strict_types=1);
  9. namespace ForkBB\Models\Pages;
  10. use ForkBB\Models\Page;
  11. use ForkBB\Models\Forum\Forum;
  12. use ForkBB\Models\Topic\Topic;
  13. use ForkBB\Models\User\User;
  14. use RuntimeException;
  15. use function \ForkBB\__;
  16. class Feed extends Page
  17. {
  18. const MAX_LEN_CONT = 1000;
  19. /**
  20. * Возвращает шаблон с простым текстом
  21. */
  22. protected function exit(string $message, int $status = 404): Page
  23. {
  24. $this->plainText = __($message);
  25. $this->nameTpl = 'layouts/plain';
  26. $this->httpStatus = \max(200, $status);
  27. $this->onlinePos = 'feed';
  28. $this->onlineDetail = null;
  29. $this->header('Content-type', 'text/plain; charset=utf-8');
  30. return $this;
  31. }
  32. /**
  33. * Подготовка данных для шаблона
  34. */
  35. public function view(array $args, string $method): Page
  36. {
  37. $this->c->DEBUG = 0;
  38. if ($this->c->config->i_feed_type < 1) {
  39. return $this->exit('Bad request');
  40. }
  41. $fid = $args['fid'] ?? 0;
  42. $tid = $args['tid'] ?? 0;
  43. if ($fid > 0 && $tid > 0) {
  44. return $this->exit('Bad request');
  45. }
  46. if ($tid) {
  47. $topic = $this->c->topics->load($tid);
  48. if (! $topic instanceof Topic) {
  49. return $this->exit('Bad request');
  50. }
  51. $feed = [
  52. 'id' => $this->c->Router->link('Feed', $args),
  53. 'title' => $this->c->config->o_board_title . __('Title separator') . $topic->subject,
  54. 'link' => $topic->link,
  55. 'description' => __(['The most recent posts in %s topic', $topic->subject]),
  56. 'updated' => $topic->last_post,
  57. 'items' => [],
  58. ];
  59. $items = $this->c->posts->feed($topic);
  60. if (! empty($items)) {
  61. foreach ($items as $cur) {
  62. $item = [
  63. 'id' => $this->c->Router->link('ViewPost', ['id' => $cur['pid']]),
  64. 'title' => $topic->subject,
  65. 'updated' => $cur['edited'] > $cur['posted'] ? $cur['edited'] : $cur['posted'],
  66. 'link' => $this->c->Router->link('ViewPost', ['id' => $cur['pid']]),
  67. 'author' => $cur['username'],
  68. 'content' => $this->c->Parser->parseMessage($this->trimContent($cur['content']), (bool) $cur['hide_smilies']),
  69. 'published' => $cur['posted'],
  70. ];
  71. $feed['items'][] = $item;
  72. }
  73. }
  74. } else {
  75. if ($this->c->config->i_feed_ttl > 0) {
  76. $cacheId = 'feed' . \sha1("{$this->user->group_id}|{$this->user->language}|{$fid}");
  77. } else {
  78. $cacheId = null;
  79. }
  80. if (null !== $cacheId && $this->c->Cache->has($cacheId)) {
  81. $feed = $this->c->Cache->get($cacheId);
  82. } else {
  83. $forum = $this->c->forums->loadTree($fid);
  84. if (! $forum instanceof Forum) {
  85. return $this->exit('Bad request');
  86. }
  87. $feed = [
  88. 'id' => $this->c->Router->link('Feed', $args),
  89. 'title' => $this->c->config->o_board_title,
  90. 'link' => $forum->link,
  91. 'updated' => $forum->tree->last_post,
  92. 'items' => [],
  93. ];
  94. if (0 === $fid) {
  95. $feed['description'] = __(['The most recent posts at %s board', $this->c->config->o_board_title]);
  96. } else {
  97. $feed['description'] = __(['The most recent posts in %s forum', $forum->forum_name]);
  98. $feed['title'] .= __('Title separator') . $forum->forum_name;
  99. }
  100. $items = $this->c->posts->feed($forum);
  101. if (! empty($items)) {
  102. foreach ($items as $cur) {
  103. $fName = $this->c->forums->get($cur['fid'])->forum_name;
  104. $item = [
  105. 'id' => $this->c->Router->link('ViewPost', ['id' => $cur['pid']]),
  106. 'title' => $fName . __('Title separator') . $cur['topic_name'],
  107. 'updated' => $cur['edited'] > $cur['posted'] ? $cur['edited'] : $cur['posted'],
  108. 'link' => $this->c->Router->link('ViewPost', ['id' => $cur['pid']]),
  109. 'author' => $cur['username'],
  110. 'content' => $this->c->Parser->parseMessage($this->trimContent($cur['content']), (bool) $cur['hide_smilies']),
  111. 'published' => $cur['posted'],
  112. ];
  113. $feed['items'][] = $item;
  114. }
  115. }
  116. if (null !== $cacheId) {
  117. if (true !== $this->c->Cache->set($cacheId, $feed, 60 * $this->c->config->i_feed_ttl)) {
  118. throw new RuntimeException('Unable to write value to cache - feed');
  119. }
  120. }
  121. }
  122. }
  123. $this->nameTpl = "feed_{$args['type']}";
  124. $this->onlinePos = 'feed';
  125. $this->onlineDetail = null;
  126. $this->feed = $feed;
  127. $this->header('Content-type', "application/{$args['type']}+xml; charset=utf-8");
  128. return $this;
  129. }
  130. /**
  131. * Сокращает длину сообщения
  132. */
  133. protected function trimContent(string $text): string
  134. {
  135. if (\mb_strlen($text, 'UTF-8') > self::MAX_LEN_CONT) {
  136. $result = \mb_substr($text, 0, self::MAX_LEN_CONT, 'UTF-8');
  137. $length = \strrpos($result, ' ');
  138. if (\is_int($length)) {
  139. $result = \substr($result, 0, $length);
  140. }
  141. $result = \rtrim($result, "!,.-\n\t ");
  142. if (isset($result[0])) {
  143. $text = $result . '…';
  144. }
  145. }
  146. return $text;
  147. }
  148. }