YaleSTC/shifts

View on GitHub
app/controllers/calendar_feeds_controller.rb

Summary

Maintainability
A
3 hrs
Test Coverage
class CalendarFeedsController < ApplicationController
    skip_before_filter RubyCAS::Filter, only: [ :grab ]
    skip_before_filter :login_check, only: [ :grab ]

  def index
    @source_types = %w[User Department LocGroup Location]
    shift_source = Struct.new( :type, :name, :token )
    @shift_sources=[]
    @sub_sources=[]

    @source_types.each do |source_type|
      current_user.send(source_type.underscore.concat('s')).each do |source|
         @shift_sources << shift_source.new(source.class.name, source.name, generate_token(source, "Shift"))
         @sub_sources << shift_source.new(source.class.name, source.name, generate_token(source, "SubRequest"))
      end
    end
  end

  def reset
    current_user.calendar_feed_hash = nil
    current_user.save!
    flash[:notice] = 'All calendars reset.  NOTE: Your old feeds are now inactive'
    redirect_to action: "index"
  end

  def grab
    begin
      @token_array = resolve_token(params[:token], params[:user_id])
      @user = User.find(params[:user_id])
    rescue Exception => e
      redirect_to action: "index"
      flash[:error] = 'Could not load calendar feed.  This feed may be invalid, inactive, or disabled.'
      return
    end
    @shifts = []
    @source = @token_array[0]
    @type = @token_array[1]
    if @type == "SubRequest"
       @shifts = @user.available_sub_requests(@source)
    elsif @type == "Shift"
       case
        when @source.class.name == "Department" && @user.departments.include?(@source) && @user.is_active?(@source)
            @shifts = Shift.active.in_locations(@source.loc_groups.select {|lg| @user.can_view?(lg)}.collect(&:locations).flatten).after_date(Time.now.utc - 3.weeks).not_for_user(@user).flatten
        when @source.class.name == "LocGroup" && @user.can_view?(@source)
            @shifts = Shift.active.in_locations(@source.locations).after_date(Time.now.utc - 3.weeks).not_for_user(@user).flatten
        when @source.class.name == "Location" && @user.can_view?(@source.loc_group)
            @shifts = Shift.active.not_for_user(@user).where("location_id = ? AND end >= ?", @source.id, Time.now.utc - 3.weeks)
        when @source.class.name == "User"
            @shifts = Shift.active.in_departments(@source.active_departments).for_user(@source).after_date(Time.now.utc - 3.weeks).flatten
      end
    end
    render text: generate_ical
  end

  private

  def generate_token(source, type)
    if !current_user.calendar_feed_hash
      current_user.calendar_feed_hash = SecureRandom.hex(32) # generates 64-character string
      current_user.save!
    end
      require 'openssl'
      require 'digest/sha2'
      cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
      cipher.encrypt
      cipher.key = AppConfig.first.calendar_feed_hash
      cipher.iv = current_user.calendar_feed_hash
      @token = cipher.update(source.class.name.to_s + ',' +  source.id.to_s + ',' + type)    #creates cipher from string
      @token << cipher.final
      @token.unpack("H*").to_s          #makes cipher alpa-numeric pretty
  end

  def resolve_token(token, user_id)
    require 'openssl'
    require 'digest/sha2'
    cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
    cipher.decrypt
    cipher.key = AppConfig.first.calendar_feed_hash
      cipher.iv = User.find(user_id).calendar_feed_hash
      @source_string = cipher.update(JSON.parse(token).pack("H*"))    #unpacks cipher and decrpts to original string
      @source_string << cipher.final
      @source = @source_string.split(',')[0].constantize.find(@source_string.split(',')[1])     #gets shift_source from string
      @feed_type=@source_string.split(',')[2]   #is it for Shifts or for Sub_Requests
      return @source, @feed_type
      #This block will fail with bad data -- but it is handled in the grab function.
  end

  def generate_ical
    cal = Icalendar::Calendar.new
    cal.append_custom_property("METHOD","PUBLISH")
    cal.append_custom_property("x-wr-calname", @type + "s: " + @source.name)
    cal.append_custom_property("X-WR-CALDESC", @type + "s Calendar Feed for user: " + @user.name + ".  The feed is " + @source.class.name.downcase + ": " + @source.name)
    cal.append_custom_property("X-PUBLISHED-TTL", "PT1H")  #default refresh rate
      @shifts.each do |shift|
        @event = Icalendar::Event.new
        @event.dtstart = shift.start.utc.to_s(:us_icaldate_utc)
        @event.dtend = shift.end.utc.to_s(:us_icaldate_utc)
        if @type == "Shift"
          @event.summary = "Shift" + (@source.class.name != "User" ? " for #{shift.user.name}" : "") + " in #{shift.location.short_name}"
          @event.summary << " (sub requested!)" if shift.has_sub?
          @event.description = shift.user.name + " has requested a sub for this shift!" if shift.has_sub?
        elsif @type == "SubRequest"
          @event.description = "\nMandatory: " + shift.mandatory_start.to_s(:twelve_hour) + " - " + shift.mandatory_end.to_s(:twelve_hour)
          @event.description << "\n\n" + shift.reason
           @event.summary = "Sub for #{shift.user.name} in #{shift.location.short_name}"
        end
        @event.location = shift.location.name
        cal.add_event @event
      end
      headers['Content-Type'] = "text/calendar; charset=UTF-8"
      headers['Content-Disposition'] = "inline; filename=" + User.find(params[:user_id]).name + "_calendar_feed.ics"
     # cal.publish  #not necessary...
      cal.to_ical
  end


end