QutBioacoustics/baw-server

View on GitHub
app/controllers/public_controller.rb

Summary

Maintainability
C
1 day
Test Coverage
# frozen_string_literal: true

class PublicController < ApplicationController
  skip_authorization_check only: [
    :index,
    :website_status,
    :credits,
    :disclaimers,
    :ethics_statement,
    :data_upload,

    :new_contact_us, :create_contact_us,
    :new_bug_report, :create_bug_report,
    :new_data_request, :create_data_request,

    :cors_preflight
  ]

  # ensure that invalid CORS preflight requests get useful responses
  skip_before_action :verify_authenticity_token, only: :cors_preflight

  # Allows rendering CMS blobs
  # TODO: remove when rails views removed
  include ComfortableMexicanSofa::RenderMethods
  helper Comfy::CmsHelper
  helper CmsHelpers

  def index
    base_path = "#{Rails.root}/public"
    image_base = '/system/home/'
    json_data_file = "#{base_path}#{image_base}animals.json"
    sensor_tree = "#{base_path}#{image_base}sensor_tree.jpg"
    if File.exist?(json_data_file) && File.exist?(sensor_tree)
      species_data = JSON.load(File.read(json_data_file))
      item_count = species_data['species'].size
      item_index = rand(item_count)
      # select a random image with audio and sensor tree
      @selected_images = {
        animal: species_data['species'][item_index],
        sensor_tree: "#{image_base}sensor_tree.jpg",
        image_base: "#{image_base}media/",
        example_spectrogram: "#{image_base}spectrogram_example.jpg",
        ecologist: "#{image_base}eco.jpg"
      }
    end

    @random_projects = Access::ByPermission
                       .projects(current_user)
                       .includes(:creator)
                       .references(:creator)
                       .order('RANDOM()')
                       .take(3)

    respond_to do |format|
      format.html
      format.json { no_content_as_json }
    end
  end

  def website_status
    result = StatsController.fetch_stats(current_user)
    @status_info = result[:summary]
    # use .includes to eager load associations
    @recent_audio_recordings =
      result
      .dig(:recent, :audio_recordings)
      &.includes([:creator, { site: :projects }])
    @recent_audio_events =
      result
      .dig(:recent, :audio_events)
      .includes([:updater, { audio_recording: :site }])

    respond_to do |format|
      format.html
      format.json { render json: @status_info }
    end
  end

  # GET /credits
  def credits
    respond_to do |format|
      format.html
    end
  end

  # GET /disclaimers
  def disclaimers
    respond_to do |format|
      format.html
    end
  end

  # GET /ethics_statement
  def ethics_statement
    respond_to do |format|
      format.html
    end
  end

  def data_upload
    respond_to do |format|
      format.html
    end
  end

  # GET /contact_us
  def new_contact_us
    @contact_us = DataClass::ContactUs.new
    respond_to do |format|
      format.html {}
    end
  end

  # POST /contact_us
  def create_contact_us
    @contact_us = DataClass::ContactUs.new(params[:data_class_contact_us])

    model_valid = @contact_us.valid?
    recaptcha_valid = verify_recaptcha(
      action: 'contact_us',
      model: @contact_us,
      message: 'Captcha response was not correct. Please try again.',
      attribute: :recaptcha
    )

    respond_to do |format|
      if recaptcha_valid && model_valid
        PublicMailer.contact_us_message(current_user, @contact_us, request).deliver_now
        format.html {
          redirect_to contact_us_path,
                      notice: "Thank you for contacting us. If you've asked us to contact you or " \
                              'we need more information, we will be in touch with you shortly.'
        }
      else
        format.html {
          render action: 'new_contact_us'
        }
      end
    end
  end

  # GET /bug_report
  def new_bug_report
    @bug_report = DataClass::BugReport.new
    respond_to do |format|
      format.html {}
    end
  end

  # POST /bug_report
  def create_bug_report
    @bug_report = DataClass::BugReport.new(params[:data_class_bug_report])

    model_valid = @bug_report.valid?
    recaptcha_valid = verify_recaptcha(
      action: 'bug_report',
      model: @bug_report,
      message: 'Captcha response was not correct. Please try again.',
      attribute: :recaptcha
    )

    respond_to do |format|
      if recaptcha_valid && model_valid
        PublicMailer.bug_report_message(current_user, @bug_report, request).deliver_now
        format.html {
          redirect_to bug_report_path,
                      notice: 'Thank you, your report was successfully submitted.
 If you entered an email address, we will let you know if the problems you describe are resolved.'
        }
      else
        format.html {
          render action: 'new_bug_report'
        }
      end
    end
  end

  # GET /data_request
  def new_data_request
    @data_request = DataClass::DataRequest.new

    annotation_download

    respond_to do |format|
      format.html {}
    end
  end

  # POST /data_request
  def create_data_request
    @data_request = DataClass::DataRequest.new(params[:data_class_data_request])

    model_valid = @data_request.valid?
    recaptcha_valid = verify_recaptcha(
      action: 'data_request',
      model: @data_request,
      message: 'Captcha response was not correct. Please try again.',
      attribute: :recaptcha
    )

    respond_to do |format|
      if recaptcha_valid && model_valid
        PublicMailer.data_request_message(current_user, @data_request, request).deliver_now
        format.html {
          redirect_to data_request_path,
                      notice: 'Your request was successfully submitted. We will be in contact shortly.'
        }
      else
        format.html {
          render action: 'new_data_request'
        }
      end
    end
  end

  def cors_preflight
    # Authentication and authorization are not checked
    # this method caters for all MALFORMED OPTIONS requests.
    # it will not be used for valid OPTIONS requests
    # valid OPTIONS requests will be caught by the rails-cors gem (see application.rb)
    raise CustomErrors::BadRequestError,
          "CORS preflight request to '#{params[:requested_route]}' was not valid. Required headers: Origin, Access-Control-Request-Method. Optional headers: Access-Control-Request-Headers."
  end

  def nav_menu
    {
      #anchor_after: 'baw.shared.links.home.title',
      menu_items: [
        {
          title: 'baw.shared.links.disclaimers.title',
          href: disclaimers_path,
          tooltip: 'baw.shared.links.disclaimers.description',
          icon: nil,
          indentation: 0,
          predicate: ->(_user) { action_name == 'disclaimers' }
        },
        {
          title: 'baw.shared.links.credits.title',
          href: credits_path,
          tooltip: 'baw.shared.links.credits.description',
          icon: nil,
          indentation: 0,
          predicate: ->(_user) { action_name == 'credits' }
        },
        {
          title: 'baw.shared.links.ethics_statement.title',
          href: ethics_statement_path,
          tooltip: 'baw.shared.links.ethics_statement.description',
          icon: nil,
          indentation: 0,
          predicate: ->(_user) { action_name == 'ethics_statement' }
        }
      ]
    }
  end

  private

  def annotation_download
    selected_params = annotation_download_params

    selected_project_id = selected_params[:selected_project_id]
    selected_site_id = selected_params[:selected_site_id]
    selected_user_id = selected_params[:selected_user_id]
    selected_timezone_name = selected_params[:selected_timezone_name]

    @annotation_download = nil

    if !selected_project_id.blank? && !selected_site_id.blank?

      # only accessible if current user has show access to project
      # and site is in specified project
      project_id = selected_project_id.to_i
      project = Project.find(project_id)
      site_id = selected_site_id.to_i
      site = Site.find(site_id)
      msg = "You must have access to the site (#{site.id}) and project(s) (#{site.projects.pluck(:id).join(', ')}) to download annotations."
      raise CanCan::AccessDenied.new(msg, :show, site) if project.nil? || site.nil?
      raise CanCan::AccessDenied.new(msg, :show, site) unless Access::Core.can?(current_user, :reader, project)

      Access::Core.check_orphan_site!(site)
      unless Access::Core.can_any?(current_user, :reader, site.projects)
        raise CanCan::AccessDenied.new(msg, :show, site)
      end
      raise CanCan::AccessDenied.new(msg, :show, site) unless project.sites.pluck(:id).include?(site_id)

      @annotation_download = {
        link: download_site_audio_events_path(project_id, site_id, selected_timezone_name: selected_timezone_name),
        name: site.name,
        timezone_name: selected_timezone_name
      }

    elsif !selected_user_id.blank?

      user_id = selected_user_id.to_i
      user = User.find(user_id)
      is_same_user = User.same_user?(current_user, user)
      msg = 'Only admins and annotation creators can download annotations created by a user.'
      raise CanCan::AccessDenied.new(msg, :show, AudioEvent) if user.nil?
      raise CanCan::AccessDenied.new(msg, :show, AudioEvent) if !Access::Core.is_admin?(current_user) && !is_same_user

      @annotation_download = {
        link: download_user_audio_events_path(user_id, selected_timezone_name: selected_timezone_name),
        name: user.user_name,
        timezone_name: selected_timezone_name
      }
    end
  end

  def annotation_download_params
    params.permit(
      :selected_project_id,
      :selected_site_id,
      :selected_user_id,
      :selected_timezone_name
    )
  end
end