Bläddra i källkod

Change Page page for http headers

Visman 4 år sedan
förälder
incheckning
16220a9291

+ 7 - 0
app/Controllers/Routing.php

@@ -362,6 +362,13 @@ class Routing
                     'SendEmail'
                     'SendEmail'
                 );
                 );
             }
             }
+            // feed
+            $r->add(
+                $r::GET,
+                '/feed/{type:atom|rss}[/forum/{fid:[1-9]\d*}][/topic/{tid:[1-9]\d*}]',
+                'Feed:view',
+                'Feed'
+            );
 
 
         }
         }
         // админ и модератор
         // админ и модератор

+ 19 - 14
app/Models/Page.php

@@ -15,6 +15,12 @@ abstract class Page extends Model
      */
      */
     protected $pageHeaders = [];
     protected $pageHeaders = [];
 
 
+    /**
+     * Http заголовки
+     * @var array
+     */
+    protected $httpHeaders = [];
+
     /**
     /**
      * Конструктор
      * Конструктор
      *
      *
@@ -38,7 +44,7 @@ abstract class Page extends Model
 
 
         $this->fIndex       = 'index'; # string      Указатель на активный пункт навигации
         $this->fIndex       = 'index'; # string      Указатель на активный пункт навигации
         $this->httpStatus   = 200;     # int         HTTP статус ответа для данной страницы
         $this->httpStatus   = 200;     # int         HTTP статус ответа для данной страницы
-        $this->httpHeaders  = [];      # array       HTTP заголовки отличные от статуса
+#       $this->httpHeaders  = [];      # array       HTTP заголовки отличные от статуса
 #       $this->nameTpl      = null;    # null|string Имя шаблона
 #       $this->nameTpl      = null;    # null|string Имя шаблона
 #       $this->titles       = [];      # array       Массив титула страницы | setTitles()
 #       $this->titles       = [];      # array       Массив титула страницы | setTitles()
         $this->fIswev       = [];      # array       Массив info, success, warning, error, validation информации
         $this->fIswev       = [];      # array       Массив info, success, warning, error, validation информации
@@ -61,6 +67,15 @@ abstract class Page extends Model
             'type' => 'text/css',
             'type' => 'text/css',
             'href' => $this->c->PUBLIC_URL . '/style/' . $this->user->style . '/style.css',
             'href' => $this->c->PUBLIC_URL . '/style/' . $this->user->style . '/style.css',
         ]);
         ]);
+
+        $now = \gmdate('D, d M Y H:i:s') . ' GMT';
+
+        $this->header('Cache-Control', 'no-cache, no-store, must-revalidate')
+//            ->header('Cache-Control', 'private, no-cache')
+            ->header('Content-type', 'text/html; charset=utf-8')
+            ->header('Date', $now)
+            ->header('Last-Modified', $now)
+            ->header('Expires', $now);
     }
     }
 
 
     /**
     /**
@@ -344,9 +359,7 @@ abstract class Page extends Model
         } else {
         } else {
             $key .= ':';
             $key .= ':';
         }
         }
-        $attr   = $this->getAttr('httpHeaders', []);
-        $attr[] = ["{$key} {$value}", $replace];
-        $this->setAttr('httpHeaders', $attr);
+        $this->httpHeaders[] = ["{$key} {$value}", $replace];
 
 
         return $this;
         return $this;
     }
     }
@@ -359,17 +372,9 @@ abstract class Page extends Model
      */
      */
     protected function gethttpHeaders(): array
     protected function gethttpHeaders(): array
     {
     {
-        $now = \gmdate('D, d M Y H:i:s') . ' GMT';
-
-        $this->httpStatus()
-            ->header('Cache-Control', 'no-cache, no-store, must-revalidate')
-//            ->header('Cache-Control', 'private, no-cache')
-            ->header('Content-type', 'text/html; charset=utf-8')
-            ->header('Date', $now)
-            ->header('Last-Modified', $now)
-            ->header('Expires', $now);
+        $this->httpStatus();
 
 
-        return $this->getAttr('httpHeaders', []);
+        return $this->httpHeaders;
     }
     }
 
 
     /**
     /**

+ 22 - 0
app/Models/Pages/Admin/Update.php

@@ -565,4 +565,26 @@ class Update extends Admin
 
 
         return null;
         return null;
     }
     }
+
+    /**
+     * rev.8 to rev.9
+     */
+    protected function stageNumber8(array $args): ?int
+    {
+        $coreConfig = new CoreConfig($this->c->DIR_CONFIG . '/' . self::CONFIG_FILE);
+
+        $coreConfig->add(
+            'multiple=>Feed',
+            '\\ForkBB\\Models\\Pages\\Feed::class',
+            'Email'
+        );
+        $coreConfig->add(
+            'multiple=>PostManagerFeed',
+            '\\ForkBB\\Models\\Post\\Feed::class',
+            'PostManagerMove'
+        );
+        $coreConfig->save();
+
+        return null;
+    }
 }
 }

+ 156 - 0
app/Models/Pages/Feed.php

@@ -0,0 +1,156 @@
+<?php
+
+namespace ForkBB\Models\Pages;
+
+use ForkBB\Models\Page;
+use ForkBB\Models\Forum\Model as Forum;
+use ForkBB\Models\Topic\Model as Topic;
+use ForkBB\Models\User\Model as User;
+use function \ForkBB\__;
+
+class Feed extends Page
+{
+    protected function exit(string $message): Page
+    {
+        return $this;
+    }
+
+    /**
+     * Подготовка данных для шаблона
+     *
+     * @param array $args
+     * @param string $method
+     *
+     * @return Page
+     */
+    public function view(array $args, string $method): Page
+    {
+        $this->c->DEBUG = 0;
+
+        if ('0' == $this->c->config->o_feed_type) {
+            return $this->exit('Bad request');
+        }
+
+        $fid = (int) ($args['fid'] ?? 0);
+        $tid = (int) ($args['tid'] ?? 0);
+
+        if ($fid > 0 && $tid > 0) {
+            return $this->exit('Bad request');
+        }
+
+        if ($tid) {
+            $topic = $this->c->topics->load($tid);
+
+            if (! $topic instanceof Topic) {
+                return $this->exit('Bad request');
+            }
+
+            $feed = [
+                'id'            => $this->c->Router->link('Feed', $args),
+                'title'         => $this->c->config->o_board_title . __('Title separator') . $topic->subject,
+                'link'          => $topic->link,
+                'description'   => __('The most recent posts in %s topic', $topic->subject),
+                'updated'       => $topic->last_post,
+                'items'         => [],
+            ];
+
+            $items = $this->c->posts->feed($topic);
+            if (! empty($items)) {
+                $uids = [];
+                foreach ($items as $cur) {
+                    $uids[$cur['uid']] = $cur['uid'];
+                }
+                unset($uids[1]);
+
+                $this->c->users->loadByIds($uids);
+
+                foreach ($items as $cur) {
+                    $user  = $this->c->users->get($cur['uid']);
+                    $email = $user instanceof User && 0 === $user->email_setting ? $user->email : null;
+                    $item  = [
+                        'id'        => $this->c->Router->link('ViewPost', ['id' => $cur['pid']]),
+                        'title'     => $topic->subject,
+                        'updated'   => $cur['edited'] > $cur['posted'] ? $cur['edited'] : $cur['posted'],
+                        'link'      => $this->c->Router->link('ViewPost', ['id' => $cur['pid']]),
+                        'author'    => $cur['username'],
+                        'email'     => $email ?? 'dummy@example.com',
+                        'isEmail'   => null !== $email,
+                        'content'   => $this->c->Parser->parseMessage($cur['content'], (bool) $cur['hide_smilies']),
+                        'published' => $cur['posted'],
+                    ];
+
+                    $feed['items'][] = $item;
+                }
+            }
+        } else {
+            $forum = $this->c->forums->loadTree($fid);
+
+            if (! $forum instanceof Forum) {
+                return $this->exit('Bad request');
+            }
+
+            $feed = [
+                'id'            => $this->c->Router->link('Feed', $args),
+                'title'         => $this->c->config->o_board_title,
+                'link'          => $forum->link,
+                'updated'       => $forum->tree->last_post,
+                'items'         => [],
+            ];
+
+            if (0 === $fid) {
+                $feed['description'] = __('The most recent posts at %s board', $this->c->config->o_board_title);
+            } else {
+                $feed['description'] = __('The most recent posts in %s forum', $forum->forum_name);
+                $feed['title']      .=  __('Title separator') . $forum->forum_name;
+            }
+
+            $items = $this->c->posts->feed($forum);
+            if (! empty($items)) {
+                $uids = [];
+                foreach ($items as $cur) {
+                    $uids[$cur['uid']] = $cur['uid'];
+                }
+                unset($uids[1]);
+
+                $this->c->users->loadByIds($uids);
+
+                foreach ($items as $cur) {
+                    $user  = $this->c->users->get($cur['uid']);
+                    $email = $user instanceof User && 0 === $user->email_setting ? $user->email : null;
+                    $fName = $this->c->forums->get($cur['fid'])->forum_name;
+                    $item  = [
+                        'id'        => $this->c->Router->link('ViewPost', ['id' => $cur['pid']]),
+                        'title'     => $fName . __('Title separator') . $cur['topic_name'],
+                        'updated'   => $cur['edited'] > $cur['posted'] ? $cur['edited'] : $cur['posted'],
+                        'link'      => $this->c->Router->link('ViewPost', ['id' => $cur['pid']]),
+                        'author'    => $cur['username'],
+                        'email'     => $email ?? 'dummy@example.com',
+                        'isEmail'   => null !== $email,
+                        'content'   => $this->c->Parser->parseMessage($cur['content'], (bool) $cur['hide_smilies']),
+                        'published' => $cur['posted'],
+                    ];
+
+                    $feed['items'][] = $item;
+                }
+            }
+        }
+
+        $this->nameTpl      = "feed_{$args['type']}";
+        $this->onlinePos    = 'feed';
+        $this->onlineDetail = false;
+        $this->onlineFilter = false;
+        $this->feed         = $feed;
+
+        $this->header('Content-type', "application/{$args['type']}+xml; charset=utf-8");
+
+        return $this;
+    }
+
+    /**
+     * Экранирует в соответствии с XML 1.
+     */
+    public function e(string $text): string
+    {
+        return \htmlspecialchars($text, \ENT_XML1 , 'UTF-8');
+    }
+}

+ 9 - 0
app/Models/Pages/Forum.php

@@ -62,6 +62,15 @@ class Forum extends Page
             $this->formMod   = $this->formMod($forum);
             $this->formMod   = $this->formMod($forum);
         }
         }
 
 
+        if ($this->c->config->o_feed_type > 0) {
+            $feedType = '2' == $this->c->config->o_feed_type ? 'atom' : 'rss';
+            $this->pageHeader('feed', 'link', [
+                'rel'  => 'alternate',
+                'type' => "application/{$feedType}+xml",
+                'href' => $this->c->Router->link('Feed', ['type' => $feedType, 'fid' => $forum->id]),
+            ]);
+        }
+
         return $this;
         return $this;
     }
     }
 
 

+ 9 - 0
app/Models/Pages/Index.php

@@ -66,6 +66,15 @@ class Index extends Page
             );
             );
         }
         }
 
 
+        if ($this->c->config->o_feed_type > 0) {
+            $feedType = '2' == $this->c->config->o_feed_type ? 'atom' : 'rss';
+            $this->pageHeader('feed', 'link', [
+                'rel'  => 'alternate',
+                'type' => "application/{$feedType}+xml",
+                'href' => $this->c->Router->link('Feed', ['type' => $feedType]),
+            ]);
+        }
+
         return $this;
         return $this;
     }
     }
 }
 }

+ 9 - 0
app/Models/Pages/Topic.php

@@ -184,6 +184,15 @@ class Topic extends Page
         }
         }
         $topic->updateVisits();
         $topic->updateVisits();
 
 
+        if ($this->c->config->o_feed_type > 0) {
+            $feedType = '2' == $this->c->config->o_feed_type ? 'atom' : 'rss';
+            $this->pageHeader('feed', 'link', [
+                'rel'  => 'alternate',
+                'type' => "application/{$feedType}+xml",
+                'href' => $this->c->Router->link('Feed', ['type' => $feedType, 'tid' => $topic->id]),
+            ]);
+        }
+
         return $this;
         return $this;
     }
     }
 
 

+ 61 - 0
app/Models/Post/Feed.php

@@ -0,0 +1,61 @@
+<?php
+
+namespace ForkBB\Models\Post;
+
+use ForkBB\Models\Action;
+use ForkBB\Models\DataModel;
+use ForkBB\Models\Topic\Model as Topic;
+use ForkBB\Models\Forum\Model as Forum;
+
+use InvalidArgumentException;
+use RuntimeException;
+
+class Feed extends Action
+{
+    /**
+     * Загружает данные для feed
+     *
+     * @throws InvalidArgumentException
+     */
+    public function Feed(DataModel $model): array
+    {
+        if ($model instanceof Topic) {
+            if (0 !== $model->moved_to) {
+                return [];
+            }
+
+            $vars  = [
+                ':id' => $model->id,
+            ];
+            $query = 'SELECT p.id as pid, p.poster as username, p.poster_id as uid, p.message as content,
+                p.hide_smilies, p.posted, p.edited
+                FROM ::posts AS p
+                WHERE p.topic_id=?i:id
+                ORDER BY p.id DESC
+                LIMIT 50';
+
+        } else if ($model instanceof Forum) {
+            $ids = \array_keys($model->descendants);
+
+            if (empty($ids)) {
+                return [];
+            }
+
+            $vars  = [
+                ':forums' => $ids,
+            ];
+            $query = 'SELECT p.id as pid, p.poster as username, p.poster_id as uid, p.message as content,
+                p.hide_smilies, p.posted, p.edited, t.id as tid, t.subject as topic_name, t.forum_id as fid
+                FROM ::posts AS p
+                INNER JOIN ::topics AS t ON t.id=p.topic_id
+                WHERE t.forum_id IN(?ai:forums)
+                ORDER BY p.id DESC
+                LIMIT 50';
+
+        } else {
+            throw new InvalidArgumentException('Expected Topic or Forum');
+        }
+
+        return $this->c->DB->query($query, $vars)->fetchAll();
+    }
+}

+ 1 - 1
app/bootstrap.php

@@ -42,7 +42,7 @@ if (
 }
 }
 $c->PUBLIC_URL = $c->BASE_URL . $forkPublicPrefix;
 $c->PUBLIC_URL = $c->BASE_URL . $forkPublicPrefix;
 
 
-$c->FORK_REVISION = 8;
+$c->FORK_REVISION = 9;
 $c->START         = $forkStart;
 $c->START         = $forkStart;
 $c->DIR_APP       = __DIR__;
 $c->DIR_APP       = __DIR__;
 $c->DIR_PUBLIC    = $forkPublic;
 $c->DIR_PUBLIC    = $forkPublic;

+ 2 - 0
app/config/main.dist.php

@@ -163,6 +163,7 @@ return [
         'Moderate'        => \ForkBB\Models\Pages\Moderate::class,
         'Moderate'        => \ForkBB\Models\Pages\Moderate::class,
         'Report'          => \ForkBB\Models\Pages\Report::class,
         'Report'          => \ForkBB\Models\Pages\Report::class,
         'Email'           => \ForkBB\Models\Pages\Email::class,
         'Email'           => \ForkBB\Models\Pages\Email::class,
+        'Feed'            => \ForkBB\Models\Pages\Feed::class,
         'ProfileView'     => \ForkBB\Models\Pages\Profile\View::class,
         'ProfileView'     => \ForkBB\Models\Pages\Profile\View::class,
         'ProfileEdit'     => \ForkBB\Models\Pages\Profile\Edit::class,
         'ProfileEdit'     => \ForkBB\Models\Pages\Profile\Edit::class,
         'ProfileConfig'   => \ForkBB\Models\Pages\Profile\Config::class,
         'ProfileConfig'   => \ForkBB\Models\Pages\Profile\Config::class,
@@ -266,6 +267,7 @@ return [
         'PostManagerUserInfoFromIP' => \ForkBB\Models\Post\UserInfoFromIP::class,
         'PostManagerUserInfoFromIP' => \ForkBB\Models\Post\UserInfoFromIP::class,
         'PostManagerUserStat'     => \ForkBB\Models\Post\UserStat::class,
         'PostManagerUserStat'     => \ForkBB\Models\Post\UserStat::class,
         'PostManagerMove'         => \ForkBB\Models\Post\Move::class,
         'PostManagerMove'         => \ForkBB\Models\Post\Move::class,
+        'PostManagerFeed'         => \ForkBB\Models\Post\Feed::class,
 
 
         'ReportModel'             => \ForkBB\Models\Report\Model::class,
         'ReportModel'             => \ForkBB\Models\Report\Model::class,
         'ReportManagerSave'       => \ForkBB\Models\Report\Save::class,
         'ReportManagerSave'       => \ForkBB\Models\Report\Save::class,

+ 7 - 4
app/lang/en/common.po

@@ -425,11 +425,14 @@ msgstr "Query"
 msgid "Total query time"
 msgid "Total query time"
 msgstr "Total query time: %s"
 msgstr "Total query time: %s"
 
 
-msgid "RSS description"
-msgstr "The most recent topics at %s."
+msgid "The most recent posts at %s board"
+msgstr "The most recent posts at \"%s\" board."
 
 
-msgid "RSS description topic"
-msgstr "The most recent posts in %s."
+msgid "The most recent posts in %s forum"
+msgstr "The most recent posts in \"%s\" forum."
+
+msgid "The most recent posts in %s topic"
+msgstr "The most recent posts in \"%s\" topic."
 
 
 msgid "RSS reply"
 msgid "RSS reply"
 msgstr "Re: "
 msgstr "Re: "

+ 7 - 4
app/lang/ru/common.po

@@ -427,11 +427,14 @@ msgstr "Запрос"
 msgid "Total query time"
 msgid "Total query time"
 msgstr "Итого: %s"
 msgstr "Итого: %s"
 
 
-msgid "RSS description"
-msgstr "Самые свежие темы на %s."
+msgid "The most recent posts at %s board"
+msgstr "Самые свежие сообщения на форуме \"%s\"."
 
 
-msgid "RSS description topic"
-msgstr "Самые свежие сообщения в %s."
+msgid "The most recent posts in %s forum"
+msgstr "Самые свежие сообщения в разделе \"%s\"."
+
+msgid "The most recent posts in %s topic"
+msgstr "Самые свежие сообщения в теме \"%s\"."
 
 
 msgid "RSS reply"
 msgid "RSS reply"
 msgstr "Re: "
 msgstr "Re: "

+ 25 - 0
app/templates/feed_atom.forkbb.php

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+  <title type="text">{!! $p->e($p->feed['title']) !!}</title>
+  <link rel="self" type="application/atom+xml" href="{!! $p->feed['id'] !!}" />
+  <link rel="alternate" type="text/html" href="{!! $p->feed['link'] !!}" />
+  <updated>{!! $p->e(\gmdate('c', $p->feed['updated'])) !!}</updated>
+  <generator>ForkBB</generator>
+  <id>{!! $p->e($p->feed['id']) !!}</id>
+@foreach($p->feed['items'] as $item)
+  <entry>
+    <title>{!! $p->e($item['title']) !!}</title>
+    <link rel="alternate" type="text/html" href="{!! $item['link'] !!}" />
+    <id>{!! $p->e($item['id']) !!}</id>
+    <updated>{!! $p->e(\gmdate('c', $item['updated'])) !!}</updated>
+    <published>{!! $p->e(\gmdate('c', $item['published'])) !!}</published>
+		<author>
+			<name>{!! $p->e($item['author']) !!}</name>
+    @if ($item['isEmail'])
+			<email>{!! $p->e($item['email']) !!}</email>
+    @endif
+		</author>
+    <content type="html">{!! $p->e($item['content']) !!}</content>
+  </entry>
+@endforeach
+</feed>

+ 21 - 0
app/templates/feed_rss.forkbb.php

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
+  <channel>
+    <atom:link href="{!! $p->feed['id'] !!}" rel="self" type="application/rss+xml" />
+    <title>{!! $p->e($p->feed['title']) !!}</title>
+    <link>{!! $p->e($p->feed['link']) !!}</link>
+    <description>{!! $p->e($p->feed['description']) !!}</description>
+    <pubDate>{!! $p->e(\gmdate('r', $p->feed['updated'])) !!}</pubDate>
+    <generator>ForkBB</generator>
+@foreach($p->feed['items'] as $item)
+    <item>
+      <title>{!! $p->e($item['title']) !!}</title>
+      <link>{!! $p->e($item['link']) !!}</link>
+      <description>{!! $p->e($item['content']) !!}</description>
+      <author>{!! $p->e($item['email']) !!} ({!! $p->e($item['author']) !!})</author>
+      <guid>{!! $p->e($item['id']) !!}</guid>
+      <pubDate>{!! $p->e(\gmdate('r', $item['published'])) !!}</pubDate>
+    </item>
+@endforeach
+  </channel>
+</rss>

+ 1 - 1
readme.md

@@ -1,4 +1,4 @@
-# ForkBB rev 8 Pre-Alpha Readme
+# ForkBB rev 9 Pre-Alpha Readme
 
 
 ## About
 ## About