diff --git a/app/Http/Admin/Services/Article.php b/app/Http/Admin/Services/Article.php
index 3ba1443e..6a9931f2 100644
--- a/app/Http/Admin/Services/Article.php
+++ b/app/Http/Admin/Services/Article.php
@@ -169,6 +169,10 @@ class Article extends Service
$data['title'] = $validator->checkTitle($post['title']);
}
+ if (isset($post['keywords'])) {
+ $data['keywords'] = $validator->checkKeywords($post['keywords']);
+ }
+
if (isset($post['content'])) {
$data['content'] = $validator->checkContent($post['content']);
$data['word_count'] = WordUtil::getWordCount($data['content']);
diff --git a/app/Http/Admin/Services/Help.php b/app/Http/Admin/Services/Help.php
index 8e1a1144..1cdbddf1 100644
--- a/app/Http/Admin/Services/Help.php
+++ b/app/Http/Admin/Services/Help.php
@@ -103,6 +103,10 @@ class Help extends Service
$data['content'] = $validator->checkContent($post['content']);
}
+ if (isset($post['keywords'])) {
+ $data['keywords'] = $validator->checkKeywords($post['keywords']);
+ }
+
if (isset($post['priority'])) {
$data['priority'] = $validator->checkPriority($post['priority']);
}
diff --git a/app/Http/Admin/Services/Package.php b/app/Http/Admin/Services/Package.php
index ec92f2ae..c49ee55f 100644
--- a/app/Http/Admin/Services/Package.php
+++ b/app/Http/Admin/Services/Package.php
@@ -122,14 +122,14 @@ class Package extends Service
$data = [];
- if (isset($post['cover'])) {
- $data['cover'] = $validator->checkCover($post['cover']);
- }
-
if (isset($post['title'])) {
$data['title'] = $validator->checkTitle($post['title']);
}
+ if (isset($post['cover'])) {
+ $data['cover'] = $validator->checkCover($post['cover']);
+ }
+
if (isset($post['summary'])) {
$data['summary'] = $validator->checkSummary($post['summary']);
}
diff --git a/app/Http/Admin/Services/Page.php b/app/Http/Admin/Services/Page.php
index e48767a1..022afa64 100644
--- a/app/Http/Admin/Services/Page.php
+++ b/app/Http/Admin/Services/Page.php
@@ -86,6 +86,10 @@ class Page extends Service
$data['content'] = $validator->checkContent($post['content']);
}
+ if (isset($post['keywords'])) {
+ $data['keywords'] = $validator->checkKeywords($post['keywords']);
+ }
+
if (isset($post['published'])) {
$data['published'] = $validator->checkPublishStatus($post['published']);
}
diff --git a/app/Http/Admin/Services/Question.php b/app/Http/Admin/Services/Question.php
index d882b35d..52b548b7 100644
--- a/app/Http/Admin/Services/Question.php
+++ b/app/Http/Admin/Services/Question.php
@@ -167,6 +167,10 @@ class Question extends Service
$data['content'] = $validator->checkContent($post['content']);
}
+ if (isset($post['keywords'])) {
+ $data['keywords'] = $validator->checkKeywords($post['keywords']);
+ }
+
if (isset($post['anonymous'])) {
$data['anonymous'] = $validator->checkAnonymousStatus($post['anonymous']);
}
diff --git a/app/Http/Admin/Services/Topic.php b/app/Http/Admin/Services/Topic.php
index b463a716..55c2b581 100644
--- a/app/Http/Admin/Services/Topic.php
+++ b/app/Http/Admin/Services/Topic.php
@@ -112,6 +112,10 @@ class Topic extends Service
$data['title'] = $validator->checkTitle($post['title']);
}
+ if (isset($post['cover'])) {
+ $data['cover'] = $validator->checkCover($post['cover']);
+ }
+
if (isset($post['summary'])) {
$data['summary'] = $validator->checkSummary($post['summary']);
}
diff --git a/app/Http/Admin/Views/article/edit_basic.volt b/app/Http/Admin/Views/article/edit_basic.volt
index 05659162..d76f9e88 100644
--- a/app/Http/Admin/Views/article/edit_basic.volt
+++ b/app/Http/Admin/Views/article/edit_basic.volt
@@ -13,6 +13,12 @@
@@ -38,6 +48,7 @@
{% block include_js %}
{{ js_include('lib/xm-select.js') }}
+ {{ js_include('admin/js/cover.upload.js') }}
{% endblock %}
diff --git a/app/Library/Helper.php b/app/Library/Helper.php
index 77c43302..03693d09 100644
--- a/app/Library/Helper.php
+++ b/app/Library/Helper.php
@@ -451,6 +451,32 @@ function kg_parse_summary($content, $length = 150)
return kg_substr($content, 0, $length);
}
+/**
+ * 解析关键字
+ *
+ * @param string $content
+ * @return string
+ */
+function kg_parse_keywords($content)
+{
+ $search = ['|', ';', ';', '、', ','];
+
+ $keywords = str_replace($search, '@', $content);
+
+ $keywords = explode('@', $keywords);
+
+ $list = [];
+
+ foreach ($keywords as $keyword) {
+ $keyword = trim($keyword);
+ if (kg_strlen($keyword) > 1) {
+ $list[] = $keyword;
+ }
+ }
+
+ return implode(',', $list);
+}
+
/**
* 解析内容中上传首图
*
diff --git a/app/Models/Article.php b/app/Models/Article.php
index 9e1072df..82629ed8 100644
--- a/app/Models/Article.php
+++ b/app/Models/Article.php
@@ -64,6 +64,13 @@ class Article extends Model
*/
public $content = '';
+ /**
+ * 关键字
+ *
+ * @var string
+ */
+ public $keywords = '';
+
/**
* 标签
*
diff --git a/app/Models/Category.php b/app/Models/Category.php
index 29397dd1..73dae0b2 100644
--- a/app/Models/Category.php
+++ b/app/Models/Category.php
@@ -150,7 +150,7 @@ class Category extends Model
public function beforeSave()
{
if (empty($this->icon)) {
- $this->icon = kg_default_icon_path();
+ $this->icon = kg_default_category_icon_path();
} elseif (Text::startsWith($this->icon, 'http')) {
$this->icon = self::getIconPath($this->icon);
}
@@ -166,7 +166,7 @@ class Category extends Model
public function afterFetch()
{
if (!Text::startsWith($this->icon, 'http')) {
- $this->icon = kg_cos_icon_url($this->icon);
+ $this->icon = kg_cos_category_icon_url($this->icon);
}
}
diff --git a/app/Models/Help.php b/app/Models/Help.php
index 7886e2dd..9767b5de 100644
--- a/app/Models/Help.php
+++ b/app/Models/Help.php
@@ -34,6 +34,13 @@ class Help extends Model
*/
public $title = '';
+ /**
+ * 关键字
+ *
+ * @var string
+ */
+ public $keywords = '';
+
/**
* 内容
*
diff --git a/app/Models/Page.php b/app/Models/Page.php
index 4318e8f8..d7dad7c3 100644
--- a/app/Models/Page.php
+++ b/app/Models/Page.php
@@ -34,6 +34,13 @@ class Page extends Model
*/
public $alias = '';
+ /**
+ * 关键字
+ *
+ * @var string
+ */
+ public $keywords = '';
+
/**
* 内容
*
diff --git a/app/Models/Question.php b/app/Models/Question.php
index 128e1bc3..66b8e05c 100644
--- a/app/Models/Question.php
+++ b/app/Models/Question.php
@@ -85,6 +85,13 @@ class Question extends Model
*/
public $tags = [];
+ /**
+ * 关键字
+ *
+ * @var string
+ */
+ public $keywords = '';
+
/**
* 概要
*
diff --git a/app/Models/Tag.php b/app/Models/Tag.php
index c33d8b56..a294e00c 100644
--- a/app/Models/Tag.php
+++ b/app/Models/Tag.php
@@ -149,7 +149,7 @@ class Tag extends Model
public function beforeSave()
{
if (empty($this->icon)) {
- $this->icon = kg_default_icon_path();
+ $this->icon = kg_default_category_icon_path();
} elseif (Text::startsWith($this->icon, 'http')) {
$this->icon = self::getIconPath($this->icon);
}
@@ -169,7 +169,7 @@ class Tag extends Model
public function afterFetch()
{
if (!Text::startsWith($this->icon, 'http')) {
- $this->icon = kg_cos_icon_url($this->icon);
+ $this->icon = kg_cos_category_icon_url($this->icon);
}
if (is_string($this->scopes) && $this->scopes != 'all') {
diff --git a/app/Models/Topic.php b/app/Models/Topic.php
index 8b672d1f..861f4667 100644
--- a/app/Models/Topic.php
+++ b/app/Models/Topic.php
@@ -9,6 +9,7 @@ namespace App\Models;
use App\Caches\MaxTopicId as MaxTopicIdCache;
use Phalcon\Mvc\Model\Behavior\SoftDelete;
+use Phalcon\Text;
class Topic extends Model
{
@@ -27,6 +28,13 @@ class Topic extends Model
*/
public $title = '';
+ /**
+ * 封面
+ *
+ * @var string
+ */
+ public $cover = '';
+
/**
* 简介
*
@@ -96,6 +104,15 @@ class Topic extends Model
$this->update_time = time();
}
+ public function beforeSave()
+ {
+ if (empty($this->cover)) {
+ $this->cover = kg_default_topic_cover_path();
+ } elseif (Text::startsWith($this->cover, 'http')) {
+ $this->cover = self::getCoverPath($this->cover);
+ }
+ }
+
public function afterCreate()
{
$cache = new MaxTopicIdCache();
@@ -103,4 +120,20 @@ class Topic extends Model
$cache->rebuild();
}
+ public function afterFetch()
+ {
+ if (!Text::startsWith($this->cover, 'http')) {
+ $this->cover = kg_cos_topic_cover_url($this->cover);
+ }
+ }
+
+ public static function getCoverPath($url)
+ {
+ if (Text::startsWith($url, 'http')) {
+ return parse_url($url, PHP_URL_PATH);
+ }
+
+ return $url;
+ }
+
}
diff --git a/app/Services/Logic/Article/ArticleInfo.php b/app/Services/Logic/Article/ArticleInfo.php
index fd2101aa..9f930e3f 100644
--- a/app/Services/Logic/Article/ArticleInfo.php
+++ b/app/Services/Logic/Article/ArticleInfo.php
@@ -49,6 +49,7 @@ class ArticleInfo extends LogicService
'title' => $article->title,
'cover' => $article->cover,
'summary' => $article->summary,
+ 'keywords' => $article->keywords,
'tags' => $article->tags,
'content' => $article->content,
'private' => $article->private,
diff --git a/app/Services/Logic/Help/HelpInfo.php b/app/Services/Logic/Help/HelpInfo.php
index 08c06143..a018fc10 100644
--- a/app/Services/Logic/Help/HelpInfo.php
+++ b/app/Services/Logic/Help/HelpInfo.php
@@ -28,6 +28,7 @@ class HelpInfo extends LogicService
return [
'id' => $help->id,
'title' => $help->title,
+ 'keywords' => $help->keywords,
'content' => $help->content,
'published' => $help->published,
'deleted' => $help->deleted,
diff --git a/app/Services/Logic/Page/PageInfo.php b/app/Services/Logic/Page/PageInfo.php
index ef7939da..08278bde 100644
--- a/app/Services/Logic/Page/PageInfo.php
+++ b/app/Services/Logic/Page/PageInfo.php
@@ -28,6 +28,7 @@ class PageInfo extends LogicService
return [
'id' => $page->id,
'title' => $page->title,
+ 'keywords' => $page->keywords,
'content' => $page->content,
'published' => $page->published,
'deleted' => $page->deleted,
diff --git a/app/Services/Logic/Question/QuestionInfo.php b/app/Services/Logic/Question/QuestionInfo.php
index 8f908a9b..7f2907a8 100644
--- a/app/Services/Logic/Question/QuestionInfo.php
+++ b/app/Services/Logic/Question/QuestionInfo.php
@@ -51,6 +51,7 @@ class QuestionInfo extends LogicService
'title' => $question->title,
'summary' => $question->summary,
'content' => $question->content,
+ 'keywords' => $question->keywords,
'tags' => $question->tags,
'bounty' => $question->bounty,
'anonymous' => $question->anonymous,
diff --git a/app/Services/Logic/Topic/TopicInfo.php b/app/Services/Logic/Topic/TopicInfo.php
index bbf365cd..85b87c30 100644
--- a/app/Services/Logic/Topic/TopicInfo.php
+++ b/app/Services/Logic/Topic/TopicInfo.php
@@ -28,6 +28,7 @@ class TopicInfo extends LogicService
return [
'id' => $topic->id,
'title' => $topic->title,
+ 'topic' => $topic->cover,
'summary' => $topic->summary,
'published' => $topic->published,
'deleted' => $topic->deleted,
diff --git a/app/Validators/Article.php b/app/Validators/Article.php
index 1e30364b..9656b652 100644
--- a/app/Validators/Article.php
+++ b/app/Validators/Article.php
@@ -107,6 +107,19 @@ class Article extends Validator
return kg_clean_html($value);
}
+ public function checkKeywords($keywords)
+ {
+ $keywords = $this->filter->sanitize($keywords, ['trim', 'string']);
+
+ $length = kg_strlen($keywords);
+
+ if ($length > 100) {
+ throw new BadRequestException('article.keyword_too_long');
+ }
+
+ return kg_parse_keywords($keywords);
+ }
+
public function checkSourceType($type)
{
if (!array_key_exists($type, ArticleModel::sourceTypes())) {
diff --git a/app/Validators/Course.php b/app/Validators/Course.php
index 002b36f6..eda4d6ff 100644
--- a/app/Validators/Course.php
+++ b/app/Validators/Course.php
@@ -148,7 +148,7 @@ class Course extends Validator
$length = kg_strlen($keywords);
if ($length > 100) {
- throw new BadRequestException('course.keywords_too_long');
+ throw new BadRequestException('course.keyword_too_long');
}
$keywords = str_replace(['|', ';', ';', '、', ','], '@', $keywords);
diff --git a/app/Validators/Help.php b/app/Validators/Help.php
index 1f86d966..95d567ab 100644
--- a/app/Validators/Help.php
+++ b/app/Validators/Help.php
@@ -103,6 +103,19 @@ class Help extends Validator
return kg_clean_html($value);
}
+ public function checkKeywords($keywords)
+ {
+ $keywords = $this->filter->sanitize($keywords, ['trim', 'string']);
+
+ $length = kg_strlen($keywords);
+
+ if ($length > 100) {
+ throw new BadRequestException('help.keyword_too_long');
+ }
+
+ return kg_parse_keywords($keywords);
+ }
+
public function checkPriority($priority)
{
$value = $this->filter->sanitize($priority, ['trim', 'int']);
diff --git a/app/Validators/Page.php b/app/Validators/Page.php
index d893aff6..fa222509 100644
--- a/app/Validators/Page.php
+++ b/app/Validators/Page.php
@@ -107,6 +107,19 @@ class Page extends Validator
return $value;
}
+ public function checkKeywords($keywords)
+ {
+ $keywords = $this->filter->sanitize($keywords, ['trim', 'string']);
+
+ $length = kg_strlen($keywords);
+
+ if ($length > 100) {
+ throw new BadRequestException('page.keyword_too_long');
+ }
+
+ return kg_parse_keywords($keywords);
+ }
+
public function checkContent($content)
{
$value = $this->filter->sanitize($content, ['trim']);
diff --git a/app/Validators/Question.php b/app/Validators/Question.php
index a7625ea1..1481868c 100644
--- a/app/Validators/Question.php
+++ b/app/Validators/Question.php
@@ -89,6 +89,19 @@ class Question extends Validator
return $value;
}
+ public function checkKeywords($keywords)
+ {
+ $keywords = $this->filter->sanitize($keywords, ['trim', 'string']);
+
+ $length = kg_strlen($keywords);
+
+ if ($length > 100) {
+ throw new BadRequestException('question.keyword_too_long');
+ }
+
+ return kg_parse_keywords($keywords);
+ }
+
public function checkContent($content)
{
$value = $this->filter->sanitize($content, ['trim']);
diff --git a/app/Validators/Topic.php b/app/Validators/Topic.php
index 24bf5e89..5a146b02 100644
--- a/app/Validators/Topic.php
+++ b/app/Validators/Topic.php
@@ -10,6 +10,7 @@ namespace App\Validators;
use App\Caches\MaxTopicId as MaxTopicIdCache;
use App\Caches\Topic as TopicCache;
use App\Exceptions\BadRequest as BadRequestException;
+use App\Library\Validators\Common as CommonValidator;
use App\Models\Topic as TopicModel;
use App\Repos\Topic as TopicRepo;
@@ -81,6 +82,17 @@ class Topic extends Validator
return $value;
}
+ public function checkCover($cover)
+ {
+ $value = $this->filter->sanitize($cover, ['trim', 'string']);
+
+ if (!CommonValidator::url($value)) {
+ throw new BadRequestException('topic.invalid_cover');
+ }
+
+ return kg_cos_img_style_trim($value);
+ }
+
public function checkSummary($summary)
{
$value = $this->filter->sanitize($summary, ['trim', 'string']);
diff --git a/config/errors.php b/config/errors.php
index 23ea859d..d809b316 100644
--- a/config/errors.php
+++ b/config/errors.php
@@ -120,6 +120,7 @@ $error['nav.has_child_node'] = '不允许相关操作(存在子节点)';
$error['article.not_found'] = '文章不存在';
$error['article.title_too_short'] = '标题太短(少于2个字符)';
$error['article.title_too_long'] = '标题太长(多于50个字符)';
+$error['article.keyword_too_long'] = '关键字太长(多于100个字符)';
$error['article.content_too_short'] = '内容太短(少于10个字符)';
$error['article.content_too_long'] = '内容太长(多于30000个字符)';
$error['article.invalid_source_type'] = '无效的来源类型';
@@ -138,6 +139,7 @@ $error['article.delete_not_allowed'] = '当前不允许删除文章';
$error['question.not_found'] = '问题不存在';
$error['question.title_too_short'] = '标题太短(少于5个字符)';
$error['question.title_too_long'] = '标题太长(多于50个字符)';
+$error['question.keyword_too_long'] = '关键字太长(多于100个字符)';
$error['question.content_too_short'] = '内容太短(少于10个字符)';
$error['question.content_too_long'] = '内容太长(多于30000个字符)';
$error['question.invalid_publish_status'] = '无效的发布状态';
@@ -172,7 +174,7 @@ $error['course.not_found'] = '课程不存在';
$error['course.title_too_short'] = '标题太短(少于5个字符)';
$error['course.title_too_long'] = '标题太长(多于50个字符)';
$error['course.summary_too_long'] = '标题太长(多于255个字符)';
-$error['course.keywords_too_long'] = '关键字太长(多于100个字符)';
+$error['course.keyword_too_long'] = '关键字太长(多于100个字符)';
$error['course.details_too_long'] = '详情太长(多于5000个字符)';
$error['course.invalid_model'] = '无效的模型类别';
$error['course.invalid_level'] = '无效的难度级别';
@@ -203,6 +205,7 @@ $error['topic.not_found'] = '话题不存在';
$error['topic.title_too_short'] = '标题太短(少于2个字符)';
$error['topic.title_too_long'] = '标题太长(多于50个字符)';
$error['topic.summary_too_long'] = '简介太长(多于255个字符)';
+$error['topic.invalid_cover'] = '无效的封面';
$error['topic.invalid_publish_status'] = '无效的发布状态';
/**
@@ -328,6 +331,7 @@ $error['page.title_too_short'] = '标题太短(少于2个字符)';
$error['page.title_too_long'] = '标题太长(多于50个字符)';
$error['page.alias_too_short'] = '别名太短(少于2个字符)';
$error['page.alias_too_long'] = '别名太长(多于50个字符)';
+$error['page.keyword_too_long'] = '关键字太长(多于100个字符)';
$error['page.content_too_short'] = '内容太短(少于10个字符)';
$error['page.content_too_long'] = '内容太长(多于30000个字符)';
$error['page.invalid_alias'] = '无效的别名(推荐使用英文作为别名)';
@@ -339,6 +343,7 @@ $error['page.invalid_publish_status'] = '无效的发布状态';
$error['help.not_found'] = '帮助不存在';
$error['help.title_too_short'] = '标题太短(少于2个字符)';
$error['help.title_too_long'] = '标题太长(多于50个字符)';
+$error['help.keyword_too_long'] = '关键字太长(多于100个字符)';
$error['help.content_too_short'] = '内容太短(少于10个字符)';
$error['help.content_too_long'] = '内容太长(多于30000个字符)';
$error['help.invalid_priority'] = '无效的排序数值(范围:1-255)';
diff --git a/db/migrations/20220801025747.php b/db/migrations/20220801025747.php
index 6ceec26c..a86f33b1 100644
--- a/db/migrations/20220801025747.php
+++ b/db/migrations/20220801025747.php
@@ -6,8 +6,9 @@
*/
use Phinx\Db\Adapter\MysqlAdapter;
+use Phinx\Migration\AbstractMigration;
-class V20220801025747 extends Phinx\Migration\AbstractMigration
+final class V20220801025747 extends AbstractMigration
{
public function up()
diff --git a/db/migrations/20220915084746.php b/db/migrations/20220915084746.php
new file mode 100644
index 00000000..b65b34c8
--- /dev/null
+++ b/db/migrations/20220915084746.php
@@ -0,0 +1,127 @@
+alterArticleTable();
+ $this->alterQuestionTable();
+ $this->alterTopicTable();
+ $this->alterPageTable();
+ $this->alterHelpTable();
+ $this->handleTopics();
+ }
+
+ protected function alterArticleTable()
+ {
+ $table = $this->table('kg_article');
+
+ if ($table->hasColumn('keywords') == false) {
+ $table->addColumn('keywords', 'string', [
+ 'null' => false,
+ 'default' => '',
+ 'limit' => 100,
+ 'collation' => 'utf8mb4_general_ci',
+ 'encoding' => 'utf8mb4',
+ 'comment' => '关键字',
+ 'after' => 'summary',
+ ]);
+ }
+
+ $table->save();
+ }
+
+ protected function alterQuestionTable()
+ {
+ $table = $this->table('kg_question');
+
+ if ($table->hasColumn('keywords') == false) {
+ $table->addColumn('keywords', 'string', [
+ 'null' => false,
+ 'default' => '',
+ 'limit' => 100,
+ 'collation' => 'utf8mb4_general_ci',
+ 'encoding' => 'utf8mb4',
+ 'comment' => '关键字',
+ 'after' => 'tags',
+ ]);
+ }
+
+ $table->save();
+ }
+
+ protected function alterPageTable()
+ {
+ $table = $this->table('kg_page');
+
+ if ($table->hasColumn('keywords') == false) {
+ $table->addColumn('keywords', 'string', [
+ 'null' => false,
+ 'default' => '',
+ 'limit' => 100,
+ 'collation' => 'utf8mb4_general_ci',
+ 'encoding' => 'utf8mb4',
+ 'comment' => '关键字',
+ 'after' => 'alias',
+ ]);
+ }
+
+ $table->save();
+ }
+
+ protected function alterHelpTable()
+ {
+ $table = $this->table('kg_help');
+
+ if ($table->hasColumn('keywords') == false) {
+ $table->addColumn('keywords', 'string', [
+ 'null' => false,
+ 'default' => '',
+ 'limit' => 100,
+ 'collation' => 'utf8mb4_general_ci',
+ 'encoding' => 'utf8mb4',
+ 'comment' => '关键字',
+ 'after' => 'title',
+ ]);
+ }
+
+ $table->save();
+ }
+
+ protected function alterTopicTable()
+ {
+ $table = $this->table('kg_topic');
+
+ if ($table->hasColumn('cover') == false) {
+ $table->addColumn('cover', 'string', [
+ 'null' => false,
+ 'default' => '',
+ 'limit' => 100,
+ 'collation' => 'utf8mb4_general_ci',
+ 'encoding' => 'utf8mb4',
+ 'comment' => '封面',
+ 'after' => 'title',
+ ]);
+ }
+
+ $table->save();
+ }
+
+ protected function handleTopics()
+ {
+ $this->getQueryBuilder()
+ ->update('kg_topic')
+ ->set('cover', '/img/default/topic_cover.png')
+ ->where(['cover' => ''])
+ ->execute();
+ }
+
+}