diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index ae32f3a101dd6534ca05146a5c90e229a696f25f..cbe13600ab04f92097eb38e9734ea8064a43234e 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -2,7 +2,7 @@ class PostsController < ApplicationController before_action :authenticate_user!, except: [:document, :share_q, :share_a, :help_center, :show] before_action :set_post, only: [:toggle_comments, :feature, :lock, :unlock] - before_action :set_scoped_post, only: [:change_category, :show, :edit, :update] + before_action :set_scoped_post, only: [:change_category, :show, :edit, :update, :close, :reopen, :delete, :restore] before_action :verify_moderator, only: [:toggle_comments] before_action :edit_checks, only: [:edit, :update] @@ -173,6 +173,46 @@ class PostsController < ApplicationController end end + def close + unless check_your_privilege('flag_close', nil, false) + render json: { status: 'failed', message: helpers.ability_err_msg(:flag_close, 'close this post') }, + status: :forbidden + return + end + + if @post.closed + render json: { status: 'failed', message: 'Cannot close a closed post.' }, status: :bad_request + return + end + + reason = CloseReason.find_by id: params[:reason_id] + if reason.nil? + render json: { status: 'failed', message: 'Close reason not found.' }, status: :not_found + return + end + + if reason.requires_other_post + other = Post.find_by(id: params[:other_post]) + if other.nil? || !top_level_post_types.include?(other.post_type_id) + render json: { status: 'failed', message: 'Invalid input for other post.' }, status: :bad_request + return + end + + duplicate_of = Question.find(params[:other_post]) + else + duplicate_of = nil + end + + if @post.update(closed: true, closed_by: current_user, closed_at: DateTime.now, last_activity: DateTime.now, + last_activity_by: current_user, close_reason: reason, duplicate_post: duplicate_of) + PostHistory.question_closed(@post, current_user) + render json: { status: 'success' } + else + render json: { status: 'failed', message: "Can't close this question right now. Try again later.", + errors: @post.errors.full_messages } + end + end + # TODO: delete, undelete, close, reopen def document diff --git a/app/controllers/questions_controller.rb b/app/controllers/questions_controller.rb index 12d40b523784445f90ec23c36d24f9f8ef990880..3d1296e7b740f6ef033e9ded7bd97903f1e5ab4d 100644 --- a/app/controllers/questions_controller.rb +++ b/app/controllers/questions_controller.rb @@ -82,45 +82,6 @@ class QuestionsController < ApplicationController end end - def close - unless check_your_privilege('flag_close', nil, false) - render(json: { status: 'failed', message: helpers.ability_err_msg(:flag_close, 'close this question') }, - status: :forbidden) - return - end - - if @question.closed - render(json: { status: 'failed', message: 'Cannot close a closed question.' }, status: :bad_request) - return - end - - reason = CloseReason.find_by id: params[:reason_id] - if reason.nil? - render(json: { status: 'failed', message: 'Close reason not found.' }, status: :not_found) - return - end - - if reason.requires_other_post - unless Question.exists? params[:other_post] - render(json: { status: 'failed', message: 'Invalid input for other post.' }, status: :bad_request) - return - end - - duplicate_of = Question.find(params[:other_post]) - else - duplicate_of = nil - end - - if @question.update(closed: true, closed_by: current_user, closed_at: DateTime.now, last_activity: DateTime.now, - last_activity_by: current_user, close_reason: reason, duplicate_post: duplicate_of) - PostHistory.question_closed(@question, current_user) - render json: { status: 'success' } - else - render json: { status: 'failed', message: "Can't close this question right now. Try again later.", - errors: @question.errors.full_messages } - end - end - def reopen unless check_your_privilege('flag_close', nil, false) flash[:danger] = helpers.ability_err_msg(:flag_close, 'reopen this question') diff --git a/app/models/post.rb b/app/models/post.rb index 0b9b6e1e86bee448e50f4cd53f74501777433e85..98e9edbbcc9d84c676b775f0f93b8407d1cc6df7 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -11,6 +11,8 @@ class Post < ApplicationRecord belongs_to :last_edited_by, class_name: 'User', optional: true belongs_to :category, optional: true belongs_to :license, optional: true + belongs_to :close_reason, optional: true + belongs_to :duplicate_post, class_name: 'Question', optional: true has_and_belongs_to_many :tags, dependent: :destroy has_many :votes, dependent: :destroy has_many :comments, dependent: :destroy diff --git a/app/models/question.rb b/app/models/question.rb index b0a347e7e0a0e11e29982e6d6f56a1ecb1cec626..85d63310c74ff41449674029f02bad3f8df25607 100644 --- a/app/models/question.rb +++ b/app/models/question.rb @@ -4,9 +4,6 @@ class Question < Post scope :meta, -> { joins(:category).where(categories: { name: 'Meta' }) } scope :main, -> { joins(:category).where(categories: { name: 'Main' }) } - belongs_to :close_reason, optional: true - belongs_to :duplicate_post, class_name: 'Question', optional: true - def self.post_type_id PostType.mapping['Question'] end diff --git a/test/controllers/posts_controller_test.rb b/test/controllers/posts_controller_test.rb index fcc735141fb6b06deb889cc4e2208294a72e1494..4f468979b38510b319130977cc29ca1cf31074a3 100644 --- a/test/controllers/posts_controller_test.rb +++ b/test/controllers/posts_controller_test.rb @@ -302,4 +302,18 @@ class PostsControllerTest < ActionController::TestCase assert_not_nil flash[:danger] assert_equal before_history, after_history, 'PostHistory event incorrectly created on no-change update' end + + test 'can close question' do + sign_in users(:closer) + before_history = PostHistory.where(post: posts(:question_one)).count + post :close, params: { id: posts(:question_one).id, reason_id: close_reasons(:not_good).id } + after_history = PostHistory.where(post: posts(:question_one)).count + assert_response 200 + assert_not_nil assigns(:post) + assert_equal before_history + 1, after_history, 'PostHistory event not created on closure' + assert_nothing_raised do + JSON.parse(response.body) + end + assert_equal 'success', JSON.parse(response.body)['status'] + end end