armandofox/audience1st

View on GitHub
app/controllers/seatmaps_controller.rb

Summary

Maintainability
A
25 mins
Test Coverage
A
96%
class SeatmapsController < ApplicationController

  before_action :is_boxoffice_manager_filter, :except => %w(seatmap assign_seats)

  def index
    @seatmaps = Seatmap.all.order(:name)
  end

  def show
    seatmap = Seatmap.find params[:id]
    send_data seatmap.csv, :type => 'text/csv', :filename => "#{seatmap.name}.csv"
  end

  def create                    # must be reached by 'post' due to file upload
    @seatmap = Seatmap.new(seatmaps_new_params)
    return redirect_to(seatmaps_path, :alert => "Seatmap was NOT uploaded because of errors: #{@seatmap.errors.as_html}") unless @seatmap.valid?
    # as a courtesy, check if URI is fetchable
    check_image
    @seatmap.save!
    redirect_to seatmaps_path
  end

  def update
    return destroy if params[:commit] =~ /delete/i
    @seatmap = Seatmap.find params[:id]
    if @seatmap.update_attributes(seatmaps_update_params)
      flash[:notice] = 'Seatmap successfully updated.'
    else
      flash[:alert] = "Seatmap was NOT updated because of errors: #{seatmap.errors.as_html}"
    end
    check_image
    redirect_to seatmaps_path
  end

  def destroy
    @seatmap = Seatmap.find params[:id]
    return redirect_to(seatmaps_path, :alert => 'You cannot delete a seatmap if it is associated with any performances.') unless @seatmap.showdates.empty?
    if @seatmap.destroy
      flash[:notice] = "Seatmap '#{@seatmap.name}' deleted."
    else
      flash[:alert] = "Seatmap '#{@seatmap.name}' could not be deleted: #{@seatmap.errors.as_html}"
    end
    redirect_to seatmaps_path
  end

  # AJAX responders for seatmap-related functions

  def seatmap
    # return the seatmap for this production, and array of UNAVAILABLE seats for this performance.
    # if 'selected' is passed, it is a comma-separated list of full seat labels to show as
    # already-selected seats (if the seatmap workflow in question respects it).
    # if this is an 'admin' level user, make sure "holdback" restrictions are ignored.
    showdate = Showdate.find(params[:id]) 
    restrict_to_zone = params[:zone]
    already_selected = params[:selected].to_s.split(/\s*,\s*/)
    if showdate.has_reserved_seating?
      render :json => Seatmap.seatmap_and_unavailable_seats_as_json(
               showdate,
               restrict_to_zone: restrict_to_zone,
               selected: already_selected,
               is_boxoffice: @gAdminDisplay)
    else
      render :json => {'map' => nil}
    end
  end

  def raw_seatmap
    render :json => Seatmap.raw_seatmap_as_json(Seatmap.find params[:id])
  end
  
  def house_seats_seatmap
    # seatmap for a particular show id showing House Seats as 'selected', reserved
    # seats as 'unavailable', and unreserved seats as 'available'
    render :json => Seatmap.house_seats_seatmap_as_json(Showdate.find params[:id])
  end

  def assign_seats
    # XHR call with params['seats'] = JSON array of selected seats, params['vouchers'] =
    #  comma-separated IDs of vouchers
    vouchers = Voucher.find(params[:vouchers].split(/\s*,\s*/))
    seats = params[:seats].split(/\s*,\s*/)
    error_message = nil
    # This is messy: we want a transaction so that if ANY seat assignment fails they ALL rollback,
    # but the ActiveRecord::Rollback exception breaks out of the transaction but cannot be
    # rescued.  (Normally we'd put the error message render into a rescue of the rollback exception)
    Voucher.transaction do
      vouchers.each_with_index do |v,i|
        unless v.assign_seat(seats[i])
          error_message = v.errors.full_messages.join(', ')
          raise ActiveRecord::Rollback # this exception doesn't need to be rescued
        end
      end
    end
    if error_message
      render(:status => :bad_request, :plain => error_message)
    else
      render(:status => :ok, :nothing => true)
    end
  end

  # helpers

  def check_image
    unless @seatmap.image_url.blank?
      u = SimpleURIChecker.new(@seatmap.image_url)
      flash[:alert] = "Warning: #{u.errors.as_html}" unless u.check(:allowed_content_types => ['image/png', 'image/jpeg', 'image/svg'])
    end
  end

  private

  def seatmaps_new_params
    permitted = params.permit(:csv, :name, :image_url)
    if permitted[:csv].blank?
      csv_contents = ''
    else
      # treat all files as UTF-8, which is safe since all legal ASCII files are also UTF-8.
      # If we fail to do this, files with (eg) UTF-8 BOM, or UTF-8 characters, will choke
      # since the tempfile created by upload is apparently ASCII-8bit by default.
      csv_contents = File.new(permitted[:csv].tempfile, 'r:bom|utf-8').read
    end
    { :csv => csv_contents,
      :name => permitted[:name],
      :image_url => permitted[:image_url] }
  end

  def seatmaps_update_params
    params.require(:seatmap).permit(:name, :image_url)
  end
end