mysociety/alaveteli

View on GitHub
app/controllers/request_controller.rb

Summary

Maintainability
F
6 days
Test Coverage
# app/controllers/request_controller.rb:
# Show information about one particular request.
#
# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
# Email: hello@mysociety.org; WWW: http://www.mysociety.org/

require 'zip'

class RequestController < ApplicationController
  skip_before_action :html_response, only: [:show, :select_authorities]

  before_action :check_read_only, only: [:new, :upload_response]
  before_action :set_render_recaptcha, only: [ :new ]
  before_action :set_info_request, only: [:show]
  before_action :redirect_embargoed_requests_for_pro_users, only: [:show]
  before_action :redirect_public_requests_from_pro_context, only: [:show]
  before_action :redirect_new_form_to_pro_version, only: [:select_authority, :new]
  before_action :set_in_pro_area, only: [:select_authority, :show]

  helper_method :state_transitions_empty?

  include ProminenceHeaders

  MAX_RESULTS = 500
  PER_PAGE = 25

  def select_authority
    # Check whether we force the user to sign in right at the start, or we allow her
    # to start filling the request anonymously
    if AlaveteliConfiguration.force_registration_on_new_request &&
       !authenticated?
      ask_to_login(
        web: _('To send and publish your FOI request'),
        email: _("Then you'll be allowed to send FOI requests."),
        email_subject: _('Confirm your email address')
      )
      return
    end
    unless params[:query].nil?
      query = params[:query]
      flash[:search_params] = params.slice(:query, :bodies, :page)
      @xapian_requests = typeahead_search(query, model: PublicBody)
    end
    medium_cache
  end

  def show
    medium_cache
    AlaveteliLocalization.with_locale(locale) do
      # Test for whole request being hidden
      return render_hidden if cannot?(:read, @info_request)

      # Always show the pro livery if a request is embargoed. This makes it
      # clear to admins and ex-pro users that the `InfoRequest` is still
      # private. Users who are not permitted to view the request are redirected
      # so we don't need to consider the `current_user` here.
      @in_pro_area = true if @info_request.embargo

      set_last_request(@info_request)

      # assign variables from request parameters
      @collapse_quotes = !params[:unfold]

      @update_status = can_update_status(@info_request)

      assign_variables_for_show_template(@info_request)

      # Only owners (and people who own everything) can update status
      if @update_status && !@is_owning_user && !authenticated?(
        as: @info_request.user
      )
        ask_to_login(
          as: @info_request.user,
          web: _('To update the status of this FOI request'),
          email: _('Then you can update the status of your request to ' \
                   '{{authority_name}}.',
                   authority_name: @info_request.public_body.name),
          email_subject: _('Update the status of your request to ' \
                           '{{authority_name}}',
                           authority_name: @info_request.public_body.name)
        )
        return
      end

      # What state transitions can the request go into
      assign_state_transition_variables

      # Sidebar stuff
      @sidebar = true
      @sidebar_template = @in_pro_area ? "alaveteli_pro/info_requests/sidebar" : "sidebar"

      # Track corresponding to this page
      @track_thing = TrackThing.create_track_for_request(@info_request)
      @feed_autodetect = [ { url: do_track_url(@track_thing, 'feed'), title: @track_thing.params[:title_in_rss], has_json: true } ]

      respond_to do |format|
        format.html do
          @has_json = true
          render template: 'request/show'
        end
        format.json { render json: @info_request.json_for_api(true) }
      end
    end
  end

  # Extra info about a request, such as event history
  def details
    long_cache
    @info_request = InfoRequest.find_by_url_title!(params[:url_title])
    return render_hidden if cannot?(:read, @info_request)
  end

  # Requests similar to this one
  def similar
    short_cache
    @per_page = 25
    @page = (params[:page] || "1").to_i

    # Later pages are very expensive to load
    if @page > MAX_RESULTS / PER_PAGE
      raise ActiveRecord::RecordNotFound, "Sorry. No pages after #{MAX_RESULTS / PER_PAGE}."
    end

    @info_request = InfoRequest.find_by_url_title!(params[:url_title])

    return render_hidden if cannot?(:read, @info_request)

    @xapian_object = ActsAsXapian::Similar.new([InfoRequestEvent],
                                               @info_request.info_request_events,
                                               offset: (@page - 1) * @per_page,
                                               limit: @per_page,
                                               collapse_by_prefix: 'request_collapse')
    @matches_estimated = @xapian_object.matches_estimated
    @show_no_more_than = (@matches_estimated > MAX_RESULTS) ? MAX_RESULTS : @matches_estimated
  end

  def list
    medium_cache
    @view = params[:view]
    unless @page # used in cache case, as perform_search sets @page as side effect
      @page = get_search_page_from_params
    end
    @per_page = PER_PAGE
    @max_results = MAX_RESULTS
    if @view == "recent"
      return redirect_to request_list_all_url(action: "list", view: "all", page: @page), status: :moved_permanently
    end

    # Later pages are very expensive to load
    if @page > MAX_RESULTS / PER_PAGE
      raise ActiveRecord::RecordNotFound, "Sorry. No pages after #{MAX_RESULTS / PER_PAGE}."
    end

    @filters = params.merge(latest_status: @view)

    if @page > 1
      @title = _("Browse and search requests (page {{count}})", count: @page)
    else
      @title = _('Browse and search requests')
    end

    @track_thing = TrackThing.create_track_for_search_query(InfoRequestEvent.make_query_from_params(@filters))
    @feed_autodetect = [ { url: do_track_url(@track_thing, 'feed'), title: @track_thing.params[:title_in_rss], has_json: true } ]

    # Don't let robots go more than 20 pages in
    @no_crawl = true if @page > 20
  end

  # Page new form posts to
  def new
    # All new requests are of normal_sort
    unless params[:outgoing_message].nil?
      params[:outgoing_message][:what_doing] = 'normal_sort'
    end

    # If we've just got here (so no writing to lose), and we're already
    # logged in, force the user to describe any undescribed requests. Allow
    # margin of 1 undescribed so it isn't too annoying - the function
    # get_undescribed_requests also allows one day since the response
    # arrived.
    if !@user.nil? && params[:submitted_new_request].nil?
      @undescribed_requests = @user.get_undescribed_requests
      if @undescribed_requests.size > 1
        render action: 'new_please_describe'
        return
      end
    end

    # Banned from making new requests?
    user_exceeded_limit = false
    if authenticated? && !authenticated_user.can_file_requests?
      # If the reason the user cannot make new requests is that they are
      # rate-limited, it’s possible they composed a request before they
      # logged in and we want to include the text of the request so they
      # can squirrel it away for tomorrow, so we detect this later after
      # we have constructed the InfoRequest.
      user_exceeded_limit = authenticated_user.exceeded_limit?(:info_requests)
      unless user_exceeded_limit
        @details = authenticated_user.can_fail_html
        render template: 'user/banned'
        return
      end
      # User did exceed limit
      @next_request_permitted_at = authenticated_user.next_request_permitted_at
    end

    # First time we get to the page, just display it
    if params[:submitted_new_request].nil? || params[:reedit]
      if user_exceeded_limit
        render template: 'user/rate_limited'
        return
      end
      return render_new_compose
    end

    # CREATE ACTION

    # Check we have :public_body_id - spammers seem to be using :public_body
    # erroneously instead
    if params[:info_request][:public_body_id].blank?
      redirect_to frontpage_path and return
    end

    # See if the exact same request has already been submitted
    # TODO: this check should theoretically be a validation rule in the
    # model, except we really want to pass @existing_request to the view so
    # it can link to it.
    @existing_request = InfoRequest.find_existing(params[:info_request][:title], params[:info_request][:public_body_id], params[:outgoing_message][:body])

    # Create both FOI request and the first request message
    @info_request = InfoRequest.build_from_attributes(info_request_params,
                                                      outgoing_message_params)
    @outgoing_message = @info_request.outgoing_messages.first

    # Maybe we lost the address while they're writing it
    unless @info_request.public_body.is_requestable?
      render action: "new_#{ @info_request.public_body.not_requestable_reason }"
      return
    end

    # See if values were valid or not
    if @existing_request || !@info_request.valid?
      # We don't want the error "Outgoing messages is invalid", as in this
      # case the list of errors will also contain a more specific error
      # describing the reason it is invalid.
      @info_request.errors.delete(:outgoing_messages)

      render action: 'new'
      return
    end

    # Show preview page, if it is a preview
    return render_new_preview if params[:preview].to_i == 1

    if user_exceeded_limit
      render template: 'user/rate_limited'
      return
    end

    unless authenticated?
      ask_to_login(
        web: _('To send and publish your FOI request').to_str,
        email: _('Then your FOI request to {{public_body_name}} will be sent ' \
                 'and published.',
                 public_body_name: @info_request.public_body.name),
        email_subject: _('Confirm your FOI request to {{public_body_name}}',
                         public_body_name: @info_request.public_body.name)
      )
      return
    end

    @info_request.user = request_user

    if spam_subject?(@outgoing_message.subject, @user)
      handle_spam_subject(@info_request.user) && return
    end

    if blocked_ip?(country_from_ip, @user)
      handle_blocked_ip(@info_request) && return
    end

    if AlaveteliConfiguration.new_request_recaptcha && !@user.confirmed_not_spam?
      if @render_recaptcha && !verify_recaptcha
        flash.now[:error] = _('There was an error with the reCAPTCHA. ' \
                              'Please try again.')

        if send_exception_notifications?
          e = Exception.new("Possible blocked non-spam (recaptcha) from #{@info_request.user_id}: #{@info_request.title}")
          ExceptionNotifier.notify_exception(e, env: request.env)
        end

        render action: 'new'
        return
      end
    end

    # This automatically saves dependent objects, such as @outgoing_message, in the same transaction
    @info_request.save!

    if @outgoing_message.sendable?
      begin
        mail_message = OutgoingMailer.initial_request(
          @outgoing_message.info_request,
          @outgoing_message
        ).deliver_now

      rescue *OutgoingMessage.expected_send_errors => e
        # Catch a wide variety of potential ActionMailer failures and
        # record the exception reason so administrators don't have to
        # dig into logs.
        @outgoing_message.record_email_failure(
          e.message
        )

        flash[:error] = _("An error occurred while sending your request to " \
                          "{{authority_name}} but has been saved and flagged " \
                          "for administrator attention.",
                          authority_name: @info_request.public_body.name)
      else
        @outgoing_message.record_email_delivery(
          mail_message.to_addrs.join(', '),
          mail_message.message_id
        )

        flash[:request_sent] = true
      ensure
        # Ensure the InfoRequest is fully updated before templating to
        # isolate templating issues recording delivery status.
        @info_request.save!
      end
    end

    redirect_to show_request_path(@info_request.url_title)
  end

  # Used for links from polymorphic URLs e.g. in Atom feeds - just redirect to
  # proper URL for the message the event refers to
  def show_request_event
    @info_request_event = InfoRequestEvent.find(params[:info_request_event_id])
    if @info_request_event.info_request.embargo
      raise ActiveRecord::RecordNotFound
    end

    if @info_request_event.is_incoming_message?
      redirect_to incoming_message_url(@info_request_event.incoming_message), status: :moved_permanently
    elsif @info_request_event.is_outgoing_message?
      redirect_to outgoing_message_url(@info_request_event.outgoing_message), status: :moved_permanently
    else
      # TODO: maybe there are better URLs for some events than this
      redirect_to request_url(@info_request_event.info_request), status: :moved_permanently
    end
  end

  # FOI officers can upload a response
  def upload_response
    AlaveteliLocalization.with_locale(locale) do
      @info_request = InfoRequest.not_embargoed.find_by_url_title!(params[:url_title])

      @reason_params = {
        web: _('To upload a response, you must be logged in using an ' \
               'email address from {{authority_name}}',
               authority_name: CGI.escapeHTML(@info_request.public_body.name)),
        email: _('Then you can upload an FOI response. '),
        email_subject: _('Confirm your account on {{site_name}}',
                         site_name: site_name)
      }

      unless authenticated?
        ask_to_login(**@reason_params)
        return false
      end

      if @info_request.allow_new_responses_from == 'nobody'
        render template:
          'request/request_subtitle/allow_new_responses_from/_nobody'
        return
      end

      unless @info_request.public_body.is_foi_officer?(@user)
        domain_required = @info_request.public_body.foi_officer_domain_required
        if domain_required.nil?
          render template: 'user/wrong_user_unknown_email'
          return
        end
        @reason_params[:user_name] = "an email @" + domain_required
        render template: 'user/wrong_user'
        return
      end
    end
    if params[:submitted_upload_response]
      file_name = nil
      file_content = nil
      unless params[:file_1].nil?
        file_name = params[:file_1].original_filename
        file_content = params[:file_1].read
      end
      body = params[:body] || ""

      if file_name.nil? && body.empty?
        flash[:error] = _("Please type a message and/or choose a file " \
                            "containing your response.")
        return
      end

      mail = RequestMailer.fake_response(@info_request, @user, body, file_name, file_content)

      @info_request.
        receive(mail,
                mail.encoded,
                override_stop_new_responses: true)
      flash[:notice] = _("Thank you for responding to this FOI request! " \
                           "Your response has been published below, and a " \
                           "link to your response has been emailed to {{user_name}}.",
                         user_name: @info_request.user.name.html_safe)
      redirect_to request_url(@info_request)
      nil
    end
  end

  # Type ahead search
  def search_typeahead
    # Since acts_as_xapian doesn't support the Partial match flag, we work
    # around it by making the last word a wildcard, which is quite the same
    @query = ''

    if params.key?(:requested_from)
      @query << "requested_from:#{ params[:requested_from] } "
    end

    @per_page = (params.fetch(:per_page) { 25 }).to_i

    @query << params[:q].to_s
    @xapian_requests = typeahead_search(@query,
                                        { model: InfoRequestEvent,
                                          per_page: @per_page })
    render partial: "request/search_ahead"
  end

  def download_entire_request
    AlaveteliLocalization.with_locale(locale) do
      @info_request = InfoRequest.find_by_url_title!(params[:url_title])
      # Check for access and hide emargoed requests immediately, so that we
      # don't leak any info to people who can't access them
      render_hidden if @info_request.embargo && cannot?(:read, @info_request)
      if !authenticated?
        ask_to_login(
          web: _('To download the zip file'),
          email: _('Then you can download a zip file of ' \
                   '{{info_request_title}}.',
                   info_request_title: @info_request.title),
          email_subject: _('Log in to download a zip file of ' \
                           '{{info_request_title}}',
                           info_request_title: @info_request.title)
        )
      else
        # Test for whole request being hidden or requester-only
        return render_hidden if cannot?(:read, @info_request)

        cache_file_path = @info_request.make_zip_cache_path(@user)
        unless File.exist?(cache_file_path)
          FileUtils.mkdir_p(File.dirname(cache_file_path))
          make_request_zip(@info_request, cache_file_path)
          File.chmod(0644, cache_file_path)
        end
        send_file(cache_file_path, filename: "#{@info_request.url_title}.zip")
      end
    end
  end

  private

  def info_request_params
    params.require(:info_request).permit(:title, :public_body_id, :tag_string)
  end

  def outgoing_message_params
    params.require(:outgoing_message).permit(:body, :what_doing)
  end

  def can_update_status(info_request)
    # Don't allow status update on external requests, otherwise accept param
    info_request.is_external? ? false : params[:update_status] == "1"
  end

  def assign_variables_for_show_template(info_request)
    @info_request = info_request
    @status = info_request.calculate_status
    @old_unclassified =
      info_request.is_old_unclassified? && authenticated?
    @is_owning_user = info_request.is_owning_user?(authenticated_user)
    @last_info_request_event_id = info_request.last_event_id_needing_description
    @new_responses_count =
      info_request.
      events_needing_description.
      select { |event| event.event_type == 'response' }.
      size
    @follower_count = @info_request.track_things.count + 1

    # For send followup link at bottom
    @last_response = info_request.get_last_public_response

    @show_profile_photo = !!(
      !@info_request.is_external? &&
      @info_request.user.show_profile_photo? &&
      !@render_to_file
    )

    @show_top_describe_state_form = !!(
      !@in_pro_area &&
      (@update_status || @info_request.awaiting_description) &&
      !@render_to_file
    )

    @show_bottom_describe_state_form = !!(
      !@in_pro_area &&
      @info_request.awaiting_description &&
      !@render_to_file
    )

    @show_owner_update_status_action = !!(
      !@old_unclassified && !@render_to_file
    )

    @show_other_user_update_status_action = !!(
      @old_unclassified && !@render_to_file
    )

    @show_action_menu = !@render_to_file

    @similar_requests, @similar_more = @info_request.similar_requests

    @citations = @info_request.citations.newest(3)
  end

  def assign_state_transition_variables
    @state_transitions = @info_request.state.transitions(
      is_pro_user: @in_pro_area,
      is_owning_user: @is_owning_user,
      user_asked_to_update_status: @update_status || @in_pro_area)

    # If there are no available transitions, we shouldn't show any options
    # to update the status
    if state_transitions_empty?(@state_transitions)
      @show_top_describe_state_form = false
      @show_bottom_describe_state_form = false
      @show_owner_update_status_action = false
      @show_other_user_update_status_action = false
    end
  end

  def state_transitions_empty?(transitions)
    return true if transitions.nil?

    transitions[:pending].empty? && \
      transitions[:complete].empty? && \
      transitions[:other].empty?
  end

  def make_request_zip(info_request, file_path)
    Zip::File.open(file_path, create: true) do |zipfile|
      file_info = make_request_summary_file(info_request)
      zipfile.get_output_stream(file_info[:filename]) { |f| f.write(file_info[:data]) }
      message_index = 0
      info_request.incoming_messages.each do |message|
        next unless can?(:read, message)

        message_index += 1
        message.get_attachments_for_display.each do |attachment|
          next unless can?(:read, attachment)

          filename = "#{message_index}_#{attachment.url_part_number}_#{attachment.display_filename}"
          zipfile.get_output_stream(filename) do |f|
            body = message.apply_masks(attachment.default_body, attachment.content_type)
            f.write(body)
          end
        end
      end
    end
  end

  def make_request_summary_file(info_request)
    done = false
    @render_to_file = true
    assign_variables_for_show_template(info_request)
    if HTMLtoPDFConverter.exist?
      html_output = render_to_string(template: 'request/show')
      tmp_input = Tempfile.new(['foihtml2pdf-input', '.html'])
      tmp_input.write(html_output)
      tmp_input.close
      tmp_output = Tempfile.new('foihtml2pdf-output')
      command = HTMLtoPDFConverter.new(tmp_input, tmp_output)
      output = command.run
      if !output.nil?
        file_info = { filename: 'correspondence.pdf',
                      data: File.open(tmp_output.path).read }
        done = true
      else
        logger.error("Could not convert info request #{info_request.id} to PDF with command '#{command}'")
      end
      tmp_output.close
      tmp_input.delete
      tmp_output.delete
    else
      logger.warn("No HTML -> PDF converter found")
    end
    unless done
      file_info = { filename: 'correspondence.txt',
                    data: render_to_string(template: 'request/show',
                                              layout: false,
                                              formats: [:text]) }
    end
    file_info
  end

  def render_new_compose
    params[:info_request] = {} unless params[:info_request]

    # Reconstruct the params
    # first the public body (by URL name or id)
    params[:info_request][:public_body_id] ||=
      if params[:url_name]
        if params[:url_name].match(/^[0-9]+$/)
          PublicBody.find(params[:url_name]).id
        else
          public_body = PublicBody.find_by_url_name_with_historic(params[:url_name])
          if public_body.nil?  # TODO: proper 404
            raise ActiveRecord::RecordNotFound, "None found"
          end

          public_body.id
        end
      elsif params[:public_body_id]
        params[:public_body_id]
      end

    unless params[:info_request][:public_body_id]
      # compulsory to have a body by here, or go to front page which is start
      # of process
      redirect_to frontpage_url
      return
    end

    # ... next any tags or other things
    params[:info_request][:title] = params[:title] if params[:title]
    params[:info_request][:tag_string] = params[:tags] if params[:tags]

    @info_request = InfoRequest.new(info_request_params)

    params[:info_request_id] = @info_request.id

    # Manually permit params because strong params was too difficult given the
    # non-standard arrangement.
    message_params =
      if params[:outgoing_message]
        { outgoing_message: params[:outgoing_message] }
      else
        { outgoing_message: {} }
      end

    message_params[:outgoing_message][:body] ||= params[:body] if params[:body]
    if params[:default_letter]
      message_params[:outgoing_message][:default_letter] ||= params[:default_letter]
    end

    message_params = ActionController::Parameters.new(message_params)
    permitted = message_params.
      permit(outgoing_message: [:body, :default_letter, :what_doing])

    @outgoing_message = OutgoingMessage.new(info_request: @info_request)

    if permitted[:outgoing_message][:body]
      @outgoing_message.body = permitted[:outgoing_message][:body]
    end
    if permitted[:outgoing_message][:default_letter]
      @outgoing_message.default_letter = permitted[:outgoing_message][:default_letter]
    end
    if permitted[:outgoing_message][:what_doing]
      @outgoing_message.what_doing = permitted[:outgoing_message][:what_doing]
    end
    @outgoing_message.set_signature_name(@user.name) unless @user.nil?

    if @info_request.public_body.is_requestable?
      render action: 'new'
    elsif @info_request.public_body.not_requestable_reason == 'bad_contact'
      render action: 'new_bad_contact'
    else
      # if not requestable because defunct or not_apply, redirect to main page
      # (which doesn't link to the /new/ URL)
      redirect_to public_body_url(@info_request.public_body)
    end
    nil
  end

  def render_new_preview
    if @outgoing_message.contains_email? || @outgoing_message.contains_postcode?
      flash.now[:error] = {
        partial: "preview_errors",
        locals: {
          contains_email: @outgoing_message.contains_email?,
          contains_postcode: @outgoing_message.contains_postcode?,
          help_link: help_privacy_path(anchor: "email_address"),
          user: @user
        }
      }
    end
    render action: 'preview'
  end

  def set_render_recaptcha
    @render_recaptcha = AlaveteliConfiguration.new_request_recaptcha &&
                        (!@user || !@user.confirmed_not_spam?)
  end

  def redirect_embargoed_requests_for_pro_users
    # Pro users should see their embargoed requests in the pro page, so that
    # if other site functions send them to a request page, they end up back in
    # the pro area
    if feature_enabled?(:alaveteli_pro) && params[:pro] != "1" && current_user
      @info_request = InfoRequest.find_by_url_title!(params[:url_title])
      if @info_request.is_actual_owning_user?(current_user) && @info_request.embargo
        redirect_to show_alaveteli_pro_request_url(
          url_title: @info_request.url_title)
      end
    end
  end

  def redirect_public_requests_from_pro_context
    # Requests which aren't embargoed should always go to the normal request
    # page, so that pro's seem them in that context after they publish them
    if feature_enabled?(:alaveteli_pro) && params[:pro] == "1"
      @info_request = InfoRequest.find_by_url_title!(params[:url_title])
      redirect_to request_url(@info_request) unless @info_request.embargo
    end
  end

  def redirect_new_form_to_pro_version
    # Pros should use the pro version of the form
    if feature_enabled?(:alaveteli_pro) &&
       request_user &&
       request_user.is_pro? &&
       params[:pro] != "1"
      if params[:url_name]
        redirect_to(
          new_alaveteli_pro_info_request_url(public_body: params[:url_name]))
      else
        redirect_to new_alaveteli_pro_info_request_url
      end
    end
  end

  # If an admin has clicked the confirmation link on a users behalf,
  # we don’t want to reassign the request to the administrator.
  def request_user
    if params[:post_redirect_user]
      params[:post_redirect_user]
    else
      current_user
    end
  end

  def spam_subject?(message_subject, user)
    !user.confirmed_not_spam? &&
      AlaveteliSpamTermChecker.new.spam?(message_subject.to_ascii)
  end

  def block_spam_subject?
    AlaveteliConfiguration.block_spam_requests ||
      AlaveteliConfiguration.enable_anti_spam
  end

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

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

  def blocked_ip?(ip, user)
    !user.confirmed_not_spam? &&
      AlaveteliConfiguration.restricted_countries.include?(ip) &&
      country_from_ip != AlaveteliConfiguration.iso_country_code
  end

  def block_restricted_country_ips?
    AlaveteliConfiguration.block_restricted_country_ips ||
      AlaveteliConfiguration.enable_anti_spam
  end

  def handle_blocked_ip(info_request)
    if send_exception_notifications?
      msg = "Possible spam request (ip_in_blocklist) from " \
            "User##{info_request.user_id}: #{user_ip} (#{country_from_ip})"
      ExceptionNotifier.notify_exception(Exception.new(msg), env: request.env)
    end

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

  def set_info_request
    AlaveteliLocalization.with_locale(locale) do
      @info_request ||= InfoRequest.find_by_url_title!(params[:url_title])
    end
  end

  def locale
    @locale ||= AlaveteliLocalization.locale
  end

  def with_prominence
    @info_request
  end
end