diff --git a/app/Builders/ArticleFavoriteList.php b/app/Builders/ArticleFavoriteList.php index 20e856a6..b81a55d7 100644 --- a/app/Builders/ArticleFavoriteList.php +++ b/app/Builders/ArticleFavoriteList.php @@ -4,6 +4,7 @@ namespace App\Builders; use App\Repos\Article as ArticleRepo; use App\Repos\User as UserRepo; +use Phalcon\Text; class ArticleFavoriteList extends Builder { @@ -38,7 +39,8 @@ class ArticleFavoriteList extends Builder $columns = [ 'id', 'title', 'cover', - 'view_count', 'like_count', 'comment_count', 'favorite_count', + 'view_count', 'like_count', + 'comment_count', 'favorite_count', ]; $articles = $articleRepo->findByIds($ids, $columns); @@ -48,7 +50,11 @@ class ArticleFavoriteList extends Builder $result = []; foreach ($articles->toArray() as $article) { - $article['cover'] = $baseUrl . $article['cover']; + + if (!empty($article['cover']) && !Text::startsWith($article['cover'], 'http')) { + $article['cover'] = $baseUrl . $article['cover']; + } + $result[$article['id']] = $article; } diff --git a/app/Builders/QuestionFavoriteList.php b/app/Builders/QuestionFavoriteList.php new file mode 100644 index 00000000..0f2cba55 --- /dev/null +++ b/app/Builders/QuestionFavoriteList.php @@ -0,0 +1,84 @@ +getQuestions($relations); + + foreach ($relations as $key => $value) { + $relations[$key]['question'] = $questions[$value['question_id']] ?? new \stdClass(); + } + + return $relations; + } + + public function handleUsers(array $relations) + { + $users = $this->getUsers($relations); + + foreach ($relations as $key => $value) { + $relations[$key]['user'] = $users[$value['user_id']] ?? new \stdClass(); + } + + return $relations; + } + + public function getQuestions(array $relations) + { + $ids = kg_array_column($relations, 'question_id'); + + $questionRepo = new QuestionRepo(); + + $columns = [ + 'id', 'title', 'cover', + 'view_count', 'like_count', + 'answer_count', 'favorite_count', + ]; + + $questions = $questionRepo->findByIds($ids, $columns); + + $baseUrl = kg_cos_url(); + + $result = []; + + foreach ($questions->toArray() as $question) { + + if (!empty($question['cover']) && !Text::startsWith($question['cover'], 'http')) { + $question['cover'] = $baseUrl . $question['cover']; + } + + $result[$question['id']] = $question; + } + + return $result; + } + + public function getUsers(array $relations) + { + $ids = kg_array_column($relations, 'user_id'); + + $userRepo = new UserRepo(); + + $users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']); + + $baseUrl = kg_cos_url(); + + $result = []; + + foreach ($users->toArray() as $user) { + $user['avatar'] = $baseUrl . $user['avatar']; + $result[$user['id']] = $user; + } + + return $result; + } + +} diff --git a/app/Builders/QuestionList.php b/app/Builders/QuestionList.php index 95299dfb..3987fac9 100644 --- a/app/Builders/QuestionList.php +++ b/app/Builders/QuestionList.php @@ -2,6 +2,8 @@ namespace App\Builders; +use App\Caches\CategoryList as CategoryListCache; +use App\Models\Category as CategoryModel; use App\Repos\User as UserRepo; class QuestionList extends Builder @@ -16,6 +18,17 @@ class QuestionList extends Builder return $questions; } + public function handleCategories(array $articles) + { + $categories = $this->getCategories(); + + foreach ($articles as $key => $article) { + $articles[$key]['category'] = $categories[$article['category_id']] ?? new \stdClass(); + } + + return $articles; + } + public function handleUsers(array $questions) { $users = $this->getUsers($questions); @@ -28,6 +41,26 @@ class QuestionList extends Builder return $questions; } + public function getCategories() + { + $cache = new CategoryListCache(); + + $items = $cache->get(CategoryModel::TYPE_QUESTION); + + if (empty($items)) return []; + + $result = []; + + foreach ($items as $item) { + $result[$item['id']] = [ + 'id' => $item['id'], + 'name' => $item['name'], + ]; + } + + return $result; + } + public function getUsers($questions) { $ownerIds = kg_array_column($questions, 'owner_id'); diff --git a/app/Console/Tasks/SitemapTask.php b/app/Console/Tasks/SitemapTask.php index ad686103..a86751c8 100644 --- a/app/Console/Tasks/SitemapTask.php +++ b/app/Console/Tasks/SitemapTask.php @@ -8,6 +8,7 @@ use App\Models\Course as CourseModel; use App\Models\Help as HelpModel; use App\Models\ImGroup as ImGroupModel; use App\Models\Page as PageModel; +use App\Models\Question as QuestionModel; use App\Models\Topic as TopicModel; use App\Models\User as UserModel; use App\Services\Service as AppService; @@ -37,6 +38,7 @@ class SitemapTask extends Task $this->addIndex(); $this->addCourses(); $this->addArticles(); + $this->addQuestions(); $this->addTeachers(); $this->addTopics(); $this->addImGroups(); @@ -66,7 +68,11 @@ class SitemapTask extends Task /** * @var Resultset|CourseModel[] $courses */ - $courses = CourseModel::query()->where('published = 1')->execute(); + $courses = CourseModel::query() + ->where('published = 1') + ->orderBy('id DESC') + ->limit(500) + ->execute(); if ($courses->count() == 0) return; @@ -81,7 +87,11 @@ class SitemapTask extends Task /** * @var Resultset|ArticleModel[] $articles */ - $articles = ArticleModel::query()->where('published = 1')->execute(); + $articles = ArticleModel::query() + ->where('published = :published:', ['published' => ArticleModel::PUBLISH_APPROVED]) + ->orderBy('id DESC') + ->limit(500) + ->execute(); if ($articles->count() == 0) return; @@ -91,6 +101,25 @@ class SitemapTask extends Task } } + protected function addQuestions() + { + /** + * @var Resultset|QuestionModel[] $questions + */ + $questions = QuestionModel::query() + ->where('published = :published:', ['published' => QuestionModel::PUBLISH_APPROVED]) + ->orderBy('id DESC') + ->limit(500) + ->execute(); + + if ($questions->count() == 0) return; + + foreach ($questions as $question) { + $loc = sprintf('%s/question/%s', $this->siteUrl, $question->id); + $this->sitemap->addItem($loc, 0.8); + } + } + protected function addTeachers() { /** diff --git a/app/Http/Admin/Controllers/AnswerController.php b/app/Http/Admin/Controllers/AnswerController.php new file mode 100644 index 00000000..f2f3e38e --- /dev/null +++ b/app/Http/Admin/Controllers/AnswerController.php @@ -0,0 +1,81 @@ +getAnswers(); + + $this->view->setVar('pager', $pager); + } + + /** + * @Post("/{id:[0-9]+}/update", name="admin.answer.update") + */ + public function updateAction($id) + { + $answerService = new AnswerService(); + + $answerService->updateAnswer($id); + + $content = ['msg' => '更新回答成功']; + + return $this->jsonSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/delete", name="admin.answer.delete") + */ + public function deleteAction($id) + { + $answerService = new AnswerService(); + + $answerService->deleteAnswer($id); + + $content = [ + 'location' => $this->request->getHTTPReferer(), + 'msg' => '删除回答成功', + ]; + + return $this->jsonSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/restore", name="admin.answer.restore") + */ + public function restoreAction($id) + { + $answerService = new AnswerService(); + + $answerService->restoreAnswer($id); + + $content = [ + 'location' => $this->request->getHTTPReferer(), + 'msg' => '还原回答成功', + ]; + + return $this->jsonSuccess($content); + } + +} diff --git a/app/Http/Admin/Controllers/ArticleController.php b/app/Http/Admin/Controllers/ArticleController.php index 7fd4a3b4..26efde1d 100644 --- a/app/Http/Admin/Controllers/ArticleController.php +++ b/app/Http/Admin/Controllers/ArticleController.php @@ -54,19 +54,6 @@ class ArticleController extends Controller $this->view->setVar('pager', $pager); } - /** - * @Get("/list/pending", name="admin.article.pending_list") - */ - public function pendingListAction() - { - $articleService = new ArticleService(); - - $pager = $articleService->getPendingArticles(); - - $this->view->pick('article/pending_list'); - $this->view->setVar('pager', $pager); - } - /** * @Get("/add", name="admin.article.add") */ @@ -106,10 +93,8 @@ class ArticleController extends Controller { $articleService = new ArticleService(); - $rejectOptions = $articleService->getRejectOptions(); $article = $articleService->getArticle($id); - $this->view->setVar('reject_options', $rejectOptions); $this->view->setVar('article', $article); } @@ -184,22 +169,31 @@ class ArticleController extends Controller } /** - * @Post("/{id:[0-9]+}/review", name="admin.article.review") + * @Route("/{id:[0-9]+}/review", name="admin.article.review") */ public function reviewAction($id) { $articleService = new ArticleService(); - $articleService->reviewArticle($id); + if ($this->request->isPost()) { - $location = $this->url->get(['for' => 'admin.article.pending_list']); + $articleService->reviewArticle($id); - $content = [ - 'location' => $location, - 'msg' => '审核文章成功', - ]; + $location = $this->url->get(['for' => 'admin.mod.articles']); - return $this->jsonSuccess($content); + $content = [ + 'location' => $location, + 'msg' => '审核文章成功', + ]; + + return $this->jsonSuccess($content); + } + + $rejectOptions = $articleService->getRejectOptions(); + $article = $articleService->getArticle($id); + + $this->view->setVar('reject_options', $rejectOptions); + $this->view->setVar('article', $article); } } diff --git a/app/Http/Admin/Controllers/ModerationController.php b/app/Http/Admin/Controllers/ModerationController.php index 75c81808..f5fb5dbb 100644 --- a/app/Http/Admin/Controllers/ModerationController.php +++ b/app/Http/Admin/Controllers/ModerationController.php @@ -22,4 +22,16 @@ class ModerationController extends Controller $this->view->setVar('pager', $pager); } + /** + * @Get("/questions", name="admin.mod.questions") + */ + public function questionsAction() + { + $modService = new ModerationService(); + + $pager = $modService->getQuestions(); + + $this->view->setVar('pager', $pager); + } + } diff --git a/app/Http/Admin/Controllers/QuestionController.php b/app/Http/Admin/Controllers/QuestionController.php new file mode 100644 index 00000000..ac959bfe --- /dev/null +++ b/app/Http/Admin/Controllers/QuestionController.php @@ -0,0 +1,195 @@ +url->get( + ['for' => 'admin.category.list'], + ['type' => CategoryModel::TYPE_ARTICLE] + ); + + $this->response->redirect($location); + } + + /** + * @Get("/search", name="admin.question.search") + */ + public function searchAction() + { + $questionService = new QuestionService(); + + $publishTypes = $questionService->getPublishTypes(); + $categories = $questionService->getCategories(); + $xmTags = $questionService->getXmTags(0); + + $this->view->setVar('publish_types', $publishTypes); + $this->view->setVar('categories', $categories); + $this->view->setVar('xm_tags', $xmTags); + } + + /** + * @Get("/list", name="admin.question.list") + */ + public function listAction() + { + $questionService = new QuestionService(); + + $pager = $questionService->getQuestions(); + + $this->view->setVar('pager', $pager); + } + + /** + * @Get("/add", name="admin.question.add") + */ + public function addAction() + { + $questionService = new QuestionService(); + + $categories = $questionService->getCategories(); + + $this->view->setVar('categories', $categories); + } + + /** + * @Get("/{id:[0-9]+}/edit", name="admin.question.edit") + */ + public function editAction($id) + { + $questionService = new QuestionService(); + + $publishTypes = $questionService->getPublishTypes(); + $categories = $questionService->getCategories(); + $question = $questionService->getQuestion($id); + $xmTags = $questionService->getXmTags($id); + + $this->view->setVar('publish_types', $publishTypes); + $this->view->setVar('categories', $categories); + $this->view->setVar('question', $question); + $this->view->setVar('xm_tags', $xmTags); + } + + /** + * @Get("/{id:[0-9]+}/show", name="admin.question.show") + */ + public function showAction($id) + { + $questionService = new QuestionService(); + + $question = $questionService->getQuestion($id); + + $this->view->setVar('question', $question); + } + + /** + * @Post("/create", name="admin.question.create") + */ + public function createAction() + { + $questionService = new QuestionService(); + + $question = $questionService->createQuestion(); + + $location = $this->url->get([ + 'for' => 'admin.question.edit', + 'id' => $question->id, + ]); + + $content = [ + 'location' => $location, + 'msg' => '创建问题成功', + ]; + + return $this->jsonSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/update", name="admin.question.update") + */ + public function updateAction($id) + { + $questionService = new QuestionService(); + + $questionService->updateQuestion($id); + + $content = ['msg' => '更新问题成功']; + + return $this->jsonSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/delete", name="admin.question.delete") + */ + public function deleteAction($id) + { + $questionService = new QuestionService(); + + $questionService->deleteQuestion($id); + + $content = [ + 'location' => $this->request->getHTTPReferer(), + 'msg' => '删除问题成功', + ]; + + return $this->jsonSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/restore", name="admin.question.restore") + */ + public function restoreAction($id) + { + $questionService = new QuestionService(); + + $questionService->restoreQuestion($id); + + $content = [ + 'location' => $this->request->getHTTPReferer(), + 'msg' => '还原问题成功', + ]; + + return $this->jsonSuccess($content); + } + + /** + * @Route("/{id:[0-9]+}/review", name="admin.question.review") + */ + public function reviewAction($id) + { + $questionService = new QuestionService(); + + if ($this->request->isPost()) { + + $questionService->reviewQuestion($id); + + $location = $this->url->get(['for' => 'admin.mod.questions']); + + $content = [ + 'location' => $location, + 'msg' => '审核问题成功', + ]; + + return $this->jsonSuccess($content); + } + + $rejectOptions = $questionService->getRejectOptions(); + $question = $questionService->getQuestion($id); + + $this->view->setVar('reject_options', $rejectOptions); + $this->view->setVar('question', $question); + } + +} diff --git a/app/Http/Admin/Controllers/UploadController.php b/app/Http/Admin/Controllers/UploadController.php index 22f2e82f..b34c5f3a 100644 --- a/app/Http/Admin/Controllers/UploadController.php +++ b/app/Http/Admin/Controllers/UploadController.php @@ -148,7 +148,6 @@ class UploadController extends Controller $items['user_avatar'] = $service->uploadDefaultUserAvatar(); $items['group_avatar'] = $service->uploadDefaultGroupAvatar(); - $items['article_cover'] = $service->uploadDefaultArticleCover(); $items['course_cover'] = $service->uploadDefaultCourseCover(); $items['package_cover'] = $service->uploadDefaultPackageCover(); $items['gift_cover'] = $service->uploadDefaultGiftCover(); diff --git a/app/Http/Admin/Services/Answer.php b/app/Http/Admin/Services/Answer.php new file mode 100644 index 00000000..093304e6 --- /dev/null +++ b/app/Http/Admin/Services/Answer.php @@ -0,0 +1,105 @@ +getParams(); + + $params['deleted'] = $params['deleted'] ?? 0; + + $sort = $pagerQuery->getSort(); + $page = $pagerQuery->getPage(); + $limit = $pagerQuery->getLimit(); + + $answerRepo = new AnswerRepo(); + + $pager = $answerRepo->paginate($params, $sort, $page, $limit); + + return $this->handleAnswers($pager); + } + + public function getAnswer($id) + { + return $this->findOrFail($id); + } + + public function updateAnswer($id) + { + $answer = $this->findOrFail($id); + + $post = $this->request->getPost(); + + $validator = new AnswerValidator(); + + $data = []; + + if (isset($post['content'])) { + $data['content'] = $validator->checkContent($post['content']); + } + + if (isset($post['published'])) { + $data['published'] = $validator->checkPublishStatus($post['published']); + } + + $answer->update($data); + + return $answer; + } + + public function deleteAnswer($id) + { + $page = $this->findOrFail($id); + + $page->deleted = 1; + + $page->update(); + + return $page; + } + + public function restoreAnswer($id) + { + $page = $this->findOrFail($id); + + $page->deleted = 0; + + $page->update(); + + return $page; + } + + protected function findOrFail($id) + { + $validator = new AnswerValidator(); + + return $validator->checkAnswer($id); + } + + protected function handleAnswers($pager) + { + if ($pager->total_items > 0) { + + $builder = new AnswerListBuilder(); + + $pipeA = $pager->items->toArray(); + $pipeB = $builder->handleUsers($pipeA); + $pipeC = $builder->objects($pipeB); + + $pager->items = $pipeC; + } + + return $pager; + } + +} diff --git a/app/Http/Admin/Services/Article.php b/app/Http/Admin/Services/Article.php index 98674a74..84c2d84a 100644 --- a/app/Http/Admin/Services/Article.php +++ b/app/Http/Admin/Services/Article.php @@ -7,15 +7,14 @@ use App\Caches\Article as ArticleCache; use App\Library\Paginator\Query as PagerQuery; use App\Library\Utils\Word as WordUtil; use App\Models\Article as ArticleModel; -use App\Models\ArticleTag as ArticleTagModel; use App\Models\Category as CategoryModel; use App\Models\Reason as ReasonModel; use App\Models\User as UserModel; use App\Repos\Article as ArticleRepo; -use App\Repos\ArticleTag as ArticleTagRepo; use App\Repos\Category as CategoryRepo; use App\Repos\Tag as TagRepo; use App\Repos\User as UserRepo; +use App\Services\Logic\Article\ArticleDataTrait; use App\Services\Logic\Notice\System\ArticleApproved as ArticleApprovedNotice; use App\Services\Logic\Notice\System\ArticleRejected as ArticleRejectedNotice; use App\Services\Logic\Point\History\ArticlePost as ArticlePostPointHistory; @@ -25,14 +24,7 @@ use App\Validators\Article as ArticleValidator; class Article extends Service { - public function getArticleModel() - { - $article = new ArticleModel(); - - $article->afterFetch(); - - return $article; - } + use ArticleDataTrait; public function getXmTags($id) { @@ -164,14 +156,6 @@ class Article 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']); - } - if (isset($post['content'])) { $data['content'] = $validator->checkContent($post['content']); $data['word_count'] = WordUtil::getWordCount($data['content']); @@ -262,9 +246,12 @@ class Article extends Service $article = $this->findOrFail($id); + $validator = new ArticleValidator(); + if ($type == 'approve') { $article->published = ArticleModel::PUBLISH_APPROVED; } elseif ($type == 'reject') { + $validator->checkRejectReason($reason); $article->published = ArticleModel::PUBLISH_REJECTED; } @@ -309,61 +296,6 @@ class Article extends Service return $validator->checkArticle($id); } - protected function saveTags(ArticleModel $article, $tagIds) - { - $originTagIds = []; - - /** - * 修改数据后,afterFetch设置的属性会失效,重新执行 - */ - $article->afterFetch(); - - if ($article->tags) { - $originTagIds = kg_array_column($article->tags, 'id'); - } - - $newTagIds = $tagIds ? explode(',', $tagIds) : []; - $addedTagIds = array_diff($newTagIds, $originTagIds); - - if ($addedTagIds) { - foreach ($addedTagIds as $tagId) { - $articleTag = new ArticleTagModel(); - $articleTag->article_id = $article->id; - $articleTag->tag_id = $tagId; - $articleTag->create(); - } - } - - $deletedTagIds = array_diff($originTagIds, $newTagIds); - - if ($deletedTagIds) { - $articleTagRepo = new ArticleTagRepo(); - foreach ($deletedTagIds as $tagId) { - $articleTag = $articleTagRepo->findArticleTag($article->id, $tagId); - if ($articleTag) { - $articleTag->delete(); - } - } - } - - $articleTags = []; - - if ($newTagIds) { - $tagRepo = new TagRepo(); - $tags = $tagRepo->findByIds($newTagIds); - if ($tags->count() > 0) { - $articleTags = []; - foreach ($tags as $tag) { - $articleTags[] = ['id' => $tag->id, 'name' => $tag->name]; - } - } - } - - $article->tags = $articleTags; - - $article->update(); - } - protected function handleArticles($pager) { if ($pager->total_items > 0) { diff --git a/app/Http/Admin/Services/AuthNode.php b/app/Http/Admin/Services/AuthNode.php index 04e00268..656a2bd4 100644 --- a/app/Http/Admin/Services/AuthNode.php +++ b/app/Http/Admin/Services/AuthNode.php @@ -272,12 +272,6 @@ class AuthNode extends Service 'type' => 'button', 'route' => 'admin.article.edit', ], - [ - 'id' => '1-7-6', - 'title' => '文章分类', - 'type' => 'menu', - 'route' => 'admin.article.category', - ], [ 'id' => '1-7-5', 'title' => '删除文章', @@ -288,7 +282,7 @@ class AuthNode extends Service 'id' => '1-7-9', 'title' => '文章详情', 'type' => 'button', - 'route' => 'admin.article.review', + 'route' => 'admin.article.show', ], [ 'id' => '1-7-10', @@ -298,6 +292,55 @@ class AuthNode extends Service ], ], ], + [ + 'id' => '1-10', + 'title' => '问答管理', + 'type' => 'menu', + 'children' => [ + [ + 'id' => '1-10-1', + 'title' => '问题列表', + 'type' => 'menu', + 'route' => 'admin.question.list', + ], + [ + 'id' => '1-10-2', + 'title' => '搜索问题', + 'type' => 'menu', + 'route' => 'admin.question.search', + ], + [ + 'id' => '1-10-3', + 'title' => '添加问题', + 'type' => 'menu', + 'route' => 'admin.question.add', + ], + [ + 'id' => '1-10-4', + 'title' => '编辑问题', + 'type' => 'button', + 'route' => 'admin.question.edit', + ], + [ + 'id' => '1-10-5', + 'title' => '删除问题', + 'type' => 'button', + 'route' => 'admin.question.delete', + ], + [ + 'id' => '1-10-9', + 'title' => '问题详情', + 'type' => 'button', + 'route' => 'admin.question.show', + ], + [ + 'id' => '1-10-10', + 'title' => '审核问题', + 'type' => 'button', + 'route' => 'admin.question.review', + ], + ], + ], [ 'id' => '1-8', 'title' => '标签管理', @@ -387,6 +430,12 @@ class AuthNode extends Service 'type' => 'menu', 'route' => 'admin.mod.articles', ], + [ + 'id' => '2-10-2', + 'title' => '问题列表', + 'type' => 'menu', + 'route' => 'admin.mod.questions', + ], ], ], [ diff --git a/app/Http/Admin/Services/Moderation.php b/app/Http/Admin/Services/Moderation.php index 68b69cc0..bef5a221 100644 --- a/app/Http/Admin/Services/Moderation.php +++ b/app/Http/Admin/Services/Moderation.php @@ -3,9 +3,12 @@ namespace App\Http\Admin\Services; use App\Builders\ArticleList as ArticleListBuilder; +use App\Builders\QuestionList as QuestionListBuilder; use App\Library\Paginator\Query as PagerQuery; use App\Models\Article as ArticleModel; +use App\Models\Question as QuestionModel; use App\Repos\Article as ArticleRepo; +use App\Repos\Question as QuestionRepo; class Moderation extends Service { @@ -30,6 +33,26 @@ class Moderation extends Service return $this->handleArticles($pager); } + public function getQuestions() + { + $pagerQuery = new PagerQuery(); + + $params = $pagerQuery->getParams(); + + $params['published'] = QuestionModel::PUBLISH_PENDING; + $params['deleted'] = 0; + + $sort = $pagerQuery->getSort(); + $page = $pagerQuery->getPage(); + $limit = $pagerQuery->getLimit(); + + $questionRepo = new QuestionRepo(); + + $pager = $questionRepo->paginate($params, $sort, $page, $limit); + + return $this->handleQuestions($pager); + } + protected function handleArticles($pager) { if ($pager->total_items > 0) { @@ -49,4 +72,23 @@ class Moderation extends Service return $pager; } + protected function handleQuestions($pager) + { + if ($pager->total_items > 0) { + + $builder = new QuestionListBuilder(); + + $items = $pager->items->toArray(); + + $pipeA = $builder->handleQuestions($items); + $pipeB = $builder->handleCategories($pipeA); + $pipeC = $builder->handleUsers($pipeB); + $pipeD = $builder->objects($pipeC); + + $pager->items = $pipeD; + } + + return $pager; + } + } diff --git a/app/Http/Admin/Services/Question.php b/app/Http/Admin/Services/Question.php new file mode 100644 index 00000000..1fe04d72 --- /dev/null +++ b/app/Http/Admin/Services/Question.php @@ -0,0 +1,336 @@ +findAll(['published' => 1], 'priority'); + + if ($allTags->count() == 0) return []; + + $questionTagIds = []; + + if ($id > 0) { + $question = $this->findOrFail($id); + if (!empty($question->tags)) { + $questionTagIds = kg_array_column($question->tags, 'id'); + } + } + + $list = []; + + foreach ($allTags as $tag) { + $selected = in_array($tag->id, $questionTagIds); + $list[] = [ + 'name' => $tag->name, + 'value' => $tag->id, + 'selected' => $selected, + ]; + } + + return $list; + } + + public function getCategories() + { + $categoryRepo = new CategoryRepo(); + + return $categoryRepo->findAll([ + 'type' => CategoryModel::TYPE_ARTICLE, + 'level' => 1, + 'published' => 1, + ]); + } + + public function getPublishTypes() + { + return QuestionModel::publishTypes(); + } + + public function getRejectOptions() + { + return ReasonModel::questionRejectOptions(); + } + + public function getQuestions() + { + $pagerQuery = new PagerQuery(); + + $params = $pagerQuery->getParams(); + + if (!empty($params['xm_tag_ids'])) { + $params['tag_id'] = explode(',', $params['xm_tag_ids']); + } + + $params['deleted'] = $params['deleted'] ?? 0; + + $sort = $pagerQuery->getSort(); + $page = $pagerQuery->getPage(); + $limit = $pagerQuery->getLimit(); + + $questionRepo = new QuestionRepo(); + + $pager = $questionRepo->paginate($params, $sort, $page, $limit); + + return $this->handleQuestions($pager); + } + + public function getQuestion($id) + { + return $this->findOrFail($id); + } + + public function createQuestion() + { + $post = $this->request->getPost(); + + $user = $this->getLoginUser(); + + $validator = new QuestionValidator(); + + $title = $validator->checkTitle($post['title']); + + $question = new QuestionModel(); + + $question->owner_id = $user->id; + $question->title = $title; + + $question->create(); + + $this->incrUserQuestionCount($user); + + $this->eventsManager->fire('Question:afterCreate', $this, $question); + + return $question; + } + + public function updateQuestion($id) + { + $post = $this->request->getPost(); + + $question = $this->findOrFail($id); + + $validator = new QuestionValidator(); + + $data = []; + + if (isset($post['category_id'])) { + $category = $validator->checkCategory($post['category_id']); + $data['category_id'] = $category->id; + } + + if (isset($post['title'])) { + $data['title'] = $validator->checkTitle($post['title']); + } + + if (isset($post['content'])) { + $data['content'] = $validator->checkContent($post['content']); + } + + if (isset($post['anonymous'])) { + $data['anonymous'] = $validator->checkAnonymousStatus($post['anonymous']); + } + + if (isset($post['closed'])) { + $data['closed'] = $validator->checkCloseStatus($post['closed']); + } + + if (isset($post['published'])) { + $data['published'] = $validator->checkPublishStatus($post['published']); + } + + if (isset($post['xm_tag_ids'])) { + $this->saveTags($question, $post['xm_tag_ids']); + } + + $question->update($data); + + $this->rebuildQuestionIndex($question); + + $this->eventsManager->fire('Question:afterUpdate', $this, $question); + + return $question; + } + + public function deleteQuestion($id) + { + $question = $this->findOrFail($id); + + $question->deleted = 1; + + $question->update(); + + $userRepo = new UserRepo(); + + $owner = $userRepo->findById($question->owner_id); + + $this->decrUserQuestionCount($owner); + + $this->rebuildQuestionIndex($question); + + $this->eventsManager->fire('Question:afterDelete', $this, $question); + + return $question; + } + + public function restoreQuestion($id) + { + $question = $this->findOrFail($id); + + $question->deleted = 0; + + $question->update(); + + $userRepo = new UserRepo(); + + $owner = $userRepo->findById($question->owner_id); + + $this->incrUserQuestionCount($owner); + + $this->rebuildQuestionIndex($question); + + $this->eventsManager->fire('Question:afterRestore', $this, $question); + + return $question; + } + + public function reviewQuestion($id) + { + $type = $this->request->getPost('type', ['trim', 'string']); + $reason = $this->request->getPost('reason', ['trim', 'string']); + + $question = $this->findOrFail($id); + + $validator = new QuestionValidator(); + + if ($type == 'approve') { + $question->published = QuestionModel::PUBLISH_APPROVED; + } elseif ($type == 'reject') { + $validator->checkRejectReason($reason); + $question->published = QuestionModel::PUBLISH_REJECTED; + } + + $question->update(); + + $sender = $this->getLoginUser(); + + if ($type == 'approve') { + + $this->rebuildQuestionIndex($question); + + $this->handlePostPoint($question); + + $notice = new QuestionApprovedNotice(); + + $notice->handle($question, $sender); + + $this->eventsManager->fire('Question:afterApprove', $this, $question); + + } elseif ($type == 'reject') { + + $options = ReasonModel::questionRejectOptions(); + + if (array_key_exists($reason, $options)) { + $reason = $options[$reason]; + } + + $notice = new QuestionRejectedNotice(); + + $notice->handle($question, $sender, $reason); + + $this->eventsManager->fire('Question:afterReject', $this, $question); + } + + return $question; + } + + protected function findOrFail($id) + { + $validator = new QuestionValidator(); + + return $validator->checkQuestion($id); + } + + protected function handleQuestions($pager) + { + if ($pager->total_items > 0) { + + $builder = new QuestionListBuilder(); + + $items = $pager->items->toArray(); + + $pipeA = $builder->handleQuestions($items); + $pipeB = $builder->handleCategories($pipeA); + $pipeC = $builder->handleUsers($pipeB); + $pipeD = $builder->objects($pipeC); + + $pager->items = $pipeD; + } + + return $pager; + } + + protected function incrUserQuestionCount(UserModel $user) + { + $user->question_count += 1; + + $user->update(); + } + + protected function decrUserQuestionCount(UserModel $user) + { + if ($user->question_count > 0) { + $user->question_count -= 1; + $user->update(); + } + } + + protected function rebuildQuestionCache(QuestionModel $question) + { + $cache = new QuestionCache(); + + $cache->rebuild($question->id); + } + + protected function rebuildQuestionIndex(QuestionModel $question) + { + $sync = new QuestionIndexSync(); + + $sync->addItem($question->id); + } + + protected function handlePostPoint(QuestionModel $question) + { + if ($question->published != QuestionModel::PUBLISH_APPROVED) return; + + $service = new QuestionPostPointHistory(); + + $service->handle($question); + } + +} diff --git a/app/Http/Admin/Views/article/add.volt b/app/Http/Admin/Views/article/add.volt index 81e6d1e4..369bd8c8 100644 --- a/app/Http/Admin/Views/article/add.volt +++ b/app/Http/Admin/Views/article/add.volt @@ -6,17 +6,6 @@
-