mysociety/alaveteli

View on GitHub
app/controllers/comment_controller.rb

Summary

Maintainability
B
4 hrs
Test Coverage
# app/controllers/comment_controller.rb:
# Show annotations upon a request or other object.
#
# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved.
# Email: hello@mysociety.org; WWW: http://www.mysociety.org/

class CommentController < ApplicationController
  before_action :build_comment, only: [:new]
  before_action :check_read_only, only: [ :new ]
  before_action :find_info_request, only: [ :new ]
  before_action :create_track_thing, only: [ :new ]
  before_action :reject_unless_comments_allowed, only: [ :new ]
  before_action :reject_if_user_banned, only: [ :new ]
  before_action :set_in_pro_area, only: [ :new ]

  def new
    if params[:comment]
      # TODO: this check should theoretically be a validation rule in the model
      @existing_comment = Comment.find_existing(@info_request.id, params[:comment][:body])
    else
      # Default to subscribing to request when first viewing form
      params[:subscribe_to_request] = true
    end

    # See if values were valid or not
    if !params[:comment] || !@existing_comment.nil? || !@comment.valid? || params[:reedit]
      @comment ||= @info_request.comments.new
      render action: 'new'
      return
    end

    # Show preview page, if it is a preview
    if params[:preview].to_i == 1
      render action: 'preview'
      return
    end

    if !authenticated?
      ask_to_login(
        web: _('To post your annotation'),
        email: _('Then your annotation to {{info_request_title}} will be ' \
                 'posted.',
                 info_request_title: @info_request.title),
        email_subject: _('Confirm your annotation to {{info_request_title}}',
                         info_request_title: @info_request.title)
      )
    else
      if spam_comment?(params[:comment][:body], @user)
        handle_spam_comment(@user) && return
      end

      # Also subscribe to track for this request, so they get updates
      # (do this first, so definitely don't send alert)
      flash[:notice] = _("Thank you for making an annotation!")

      if params[:subscribe_to_request]
        @track_thing = TrackThing.create_track_for_request(@info_request)
        @existing_track = TrackThing.find_existing(@user, @track_thing)
        if @user && @info_request.user == @user
          # don't subscribe to own request!
        elsif !@existing_track
          @track_thing.track_medium = 'email_daily'
          @track_thing.tracking_user_id = @user.id
          @track_thing.save!
          flash[:notice] += _(" You will also be emailed updates about the request.")
        else
          flash[:notice] += _(" You are already being emailed updates about the request.")
        end
      end

      # This automatically saves dependent objects in the same transaction
      @comment = @info_request.add_comment(params[:comment][:body], authenticated_user)
      @info_request.save!

      # we don't use comment_url here, as then you don't see the flash at top of page
      redirect_to request_url(@info_request)
    end
  end

  private

  def build_comment
    if params[:comment]
      @comment = Comment.new(comment_params.merge(user: @user))
    end
  end

  def comment_params
    params.require(:comment).permit(:body)
  end

  def find_info_request
    if params[:type] == 'request'
      @info_request = InfoRequest.find_by_url_title!(params[:url_title])
      if @info_request.embargo && cannot?(:read, @info_request)
        raise ActiveRecord::RecordNotFound
      end
    else
      raise "Unknown type #{ params[:type] }"
    end
  end

  def create_track_thing
    @track_thing = TrackThing.create_track_for_request(@info_request)
  end

  # Are comments disabled on this request, or globally?
  #
  # There is no “add comment” link when comments are disabled, so users should
  # not usually hit this unless they are explicitly attempting to avoid the
  # comment block.
  def reject_unless_comments_allowed
    unless feature_enabled?(:annotations) && @info_request.comments_allowed?
      redirect_to request_url(@info_request), notice: "Comments are not allowed on this request"
    end
  end

  # Banned from adding comments?
  def reject_if_user_banned
    return if !authenticated? || authenticated_user.can_make_comments?

    if authenticated_user.banned?
      @details = authenticated_user.can_fail_html
      render template: 'user/banned'
    else
      render template: 'comment/rate_limited'
    end
  end

  # An override of ApplicationController#set_in_pro_area to set the flag
  # whenever the info_request has an embargo, because we might not have a :pro
  # parameter to go on.
  def set_in_pro_area
    @in_pro_area = @info_request.embargo.present?
  end

  def spam_comment?(comment_body, user)
    !user.confirmed_not_spam? &&
      AlaveteliSpamTermChecker.new.spam?(comment_body)
  end

  def block_spam_comments?
    AlaveteliConfiguration.block_spam_comments ||
      AlaveteliConfiguration.enable_anti_spam
  end

  # Sends an exception and blocks the comment depending on configuration.
  def handle_spam_comment(user)
    if send_exception_notifications?
      e = Exception.new("Possible spam annotation from user #{ user.id }")
      ExceptionNotifier.notify_exception(e, env: request.env)
    end

    if block_spam_comments?
      flash.now[:error] = _("Sorry, we're currently unable to add your " \
                            "annotation. Please try again later.")
      render action: 'new'
      true
    end
  end
end