mconf/mconf-web

View on GitHub
app/models/event.rb

Summary

Maintainability
A
3 hrs
Test Coverage
class Event < ActiveRecord::Base
  extend FriendlyId

  include PublicActivity::Common

  SOCIAL_NETWORKS = ['Facebook', 'Google Plus', 'Twitter', 'Linkedin']

  def self.host
    Site.current.domain
  end

  def new_activity key, user
    create_activity key, owner: owner, recipient: user, parameters: { username: user.try(:name), trackable_name: name }
  end

  # Temporary while we have no private events
  def public
    if owner_type == 'User'
      true # User owned spaces are always public
    elsif owner_type == 'Space'
      owner && owner.public?
    end
  end

  attr_accessor :date_display_format

  geocoded_by :address
  after_validation :geocode

  belongs_to :owner, :polymorphic => true
  has_many :participants, :dependent => :destroy

  validates :name, presence: true
  validates :start_on, presence: true
  validates :time_zone, presence: true
  validates :summary, length: {:maximum => 140}
  validates :owner, presence: true

  friendly_id :name, use: :slugged, :slug_column => :slug
  validates :slug, :presence => true

  # If the event has no ending date, use a day from start date
  before_save :check_end_on
  before_validation :check_summary

  # Test if we need to clear the coordinates because address was cleared
  before_save :check_coordinates

  # Ensure events will never be found if disabled
  default_scope -> {
    if !Mconf::Modules.mod_enabled?('events')
      Event.none
    elsif !Mconf::Modules.mod_enabled?('spaces')
      Event.where.not(owner_type: Space.name)
    end
  }

  # Search events based on a list of words
  scope :search_by_terms, -> (words, include_private=false) {
    words = words.join(' ') if words.is_a?(Array)
    where('name LIKE ?', "%#{words}%")
  }

  # The default ordering for search methods
  scope :search_order, -> {
    order("start_on DESC")
  }

  # Events that are happening currently
  scope :happening_now, lambda {
    where("start_on <= ? AND end_on > ?", Time.zone.now, Time.zone.now)
  }

  # Events that have already happened
  scope :past, lambda {
    where("end_on < ?", Time.zone.now)
  }

  # Events that are either in the future or are running now.
  scope :upcoming, lambda {
    where("end_on > ?", Time.zone.now)
  }

  # Events that happen between `from` and `to`
  scope :within, lambda { |from, to|
    where("(start_on >= ? AND start_on <= ?) OR (end_on >= ? AND end_on <= ?)", from, to, from, to)
  }

  # For form pretty display only
  attr_accessor :owner_name
  def owner_name
    @owner_name || owner.try(:name) || owner.try(:email)
  end

  def full_url
    Rails.application.routes.url_helpers.event_path(self, :host => Event.host, :only_path => false)
  end

  def should_generate_new_friendly_id?
    new_record?
  end

  def description_html
    if not description.blank?
      require 'redcarpet'
      markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new(escape_html: true))
      html = markdown.render description
    else
      html = I18n.t('events.no_description')
    end

    html
  end

  def social_networks=(networks)
    write_attribute(:social_networks , networks.select{|n| !n.empty?}.join(','))
  end

  def social_networks
    networks = read_attribute(:social_networks)
    networks ? networks.split(',') : []
  end

  def start_on_with_time_zone
    start_on.try(:in_time_zone, time_zone)
  end

  def end_on_with_time_zone
    end_on.try(:in_time_zone, time_zone)
  end

  # To format results on forms
  # Defaults to 'en' date format but can be set using event.date_display_format=
  def start_on_date
    if @start_on_date
      # showing date sent on last request
      @start_on_date
    else
      # fetching date from database and converting for display
      start_on_with_time_zone.strftime(date_display_format || "%m/%d/%Y") if start_on
    end
  end

  def start_on_time
    start_on_with_time_zone
  end

  def end_on_date
    if @end_on_date
      @end_on_date
    else
      end_on_with_time_zone.strftime(date_display_format || "%m/%d/%Y") if end_on
    end
  end

  def end_on_time
    end_on_with_time_zone
  end

  def to_ical
    calendar = Icalendar::Calendar.new
    calendar.add_event(to_ics_internal)
    calendar.publish
    calendar.to_ical
  end
  alias_method :to_ics, :to_ical

  # Returns wheter the event has already happaned and is finished
  def past?
    end_on.past?
  end

  # Returns whether the event is happening now or not.
  def is_happening_now?
    if start_on.past? && end_on.future?
      true
    else
      false
    end
  end

  # Returns whether the event will happen in the future or not.
  def future?
    start_on.future?
  end

  # Returns a string with the starting hour of an event in the correct format
  def get_formatted_hour
    start_on.strftime("%H:%M")
  end

  # Returns a string with the starting date of an event in the correct format
  def get_formatted_date(date=nil, with_tz=true)
    if date.nil?
      if with_tz
        I18n::localize(start_on, :format => "%A, %d %b %Y, %H:%M (#{time_zone})")
      else
        I18n::localize(start_on, :format => "%A, %d %b %Y, %H:%M")
      end
    else
      if with_tz
        I18n::localize(date, :format => "%A, %d %b %Y, %H:%M (#{time_zone})")
      else
        I18n::localize(date, :format => "%A, %d %b %Y, %H:%M")
      end
    end
  end

  # Currently unused
  def get_formatted_timezone(date=nil)
    if date.nil?
      "GMT#{start_on_with_time_zone.formatted_offset}"
    else
      "GMT#{date.in_time_zone(time_zone).formatted_offset}"
    end
  end

  # Returns whether a user (any model) or email (a string) is already registered in this event.
  def is_registered?(user_or_email)
    if user_or_email.is_a?(String)
      Participant.where(:email => user_or_email, :event_id => id).any?
    else
      Participant.where(:owner_type => user_or_email.class.name, :owner_id => user_or_email.id,
                        :event_id => id).any?
    end
  end

  def initials
    self.name.split(' ').collect{ |w| w[0] }.join('')
  end

  private

  def to_ics_internal
    event = Icalendar::Event.new
    event.dtstart = start_on.strftime("%Y%m%dT%H%M%SZ")
    event.dtend = end_on.strftime("%Y%m%dT%H%M%SZ") if !end_on.blank?
    event.summary = name
    event.organizer = owner_name
    event.description = summary
    event.location = "#{location}"
    event.location += " - #{address}" if !address.blank?
    event.ip_class = "PUBLIC"
    event.created = created_at.strftime("%Y%m%dT%H%M%S")
    event.last_modified = updated_at.strftime("%Y%m%dT%H%M%S")
    event.uid = full_url
    event.url = full_url
    event
  end

  def check_end_on
    write_attribute(:end_on, start_on + 1.day) if end_on.blank?

    # Swap dates if it ends before it starts
    if end_on < start_on
      tmp = start_on
      write_attribute(:start_on, end_on)
      write_attribute(:end_on, tmp)
    end
  end

  def check_summary
    if summary.blank?
      s = HTML::FullSanitizer.new.sanitize(description_html).truncate(136, :omission => '...')
      write_attribute(:summary, s)
    end
  end

  def check_coordinates
    if persisted? && address.blank?
      write_attribute(:longitude, nil)
      write_attribute(:latitude, nil)
    end
  end
end