houdini/app/models/campaign.rb

208 lines
5.9 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
2020-06-12 20:03:43 +00:00
# License: AGPL-3.0-or-later WITH WTO-AP-3.0-or-later
# Full license explanation at https://github.com/houdiniproject/houdini/blob/master/LICENSE
class Campaign < ApplicationRecord
include Image::AttachmentExtensions
2019-08-06 12:36:03 +00:00
# :name,
# :tagline,
# :slug, # str: url name
# :total_supporters,
# :goal_amount,
# :nonprofit_id,
# :profile_id,
# :main_image,
# :remove_main_image, # for carrierwave
# :background_image,
# :remove_background_image, #bool carrierwave
# :banner_image,
# :remove_banner_image,
2019-08-06 12:36:03 +00:00
# :published,
# :video_url, #str
# :vimeo_video_id,
# :youtube_video_id,
# :summary,
# :recurring_fund, # bool: whether this is a recurring campaign
# :body,
# :goal_amount_dollars, #accessor: translated into goal_amount (cents)
# :show_total_raised, # bool
# :show_total_count, # bool
# :hide_activity_feed, # bool
# :end_datetime,
2019-08-06 12:36:03 +00:00
# :deleted, #bool (soft delete)
# :hide_goal, # bool
# :hide_thermometer, #bool
# :hide_title, # bool
# :receipt_message, # text
# :hide_custom_amounts, # boolean
# :parent_campaign_id,
# :reason_for_supporting,
# :default_reason_for_supporting
validate :end_datetime_cannot_be_in_past, on: :create
validates :profile, presence: true
validates :nonprofit, presence: true
validates :goal_amount,
presence: true,
numericality: { only_integer: true, greater_than: 99 }
validates :name,
presence: true,
length: { maximum: 60 }
validates :slug, uniqueness: { scope: :nonprofit_id, message: 'You already have a campaign with that URL.' }, presence: true
attr_accessor :goal_amount_dollars
2020-04-29 22:22:13 +00:00
has_one_attached :main_image
has_one_attached :background_image
has_one_attached :banner_image
has_one_attached_with_sizes(:main_image, {normal: [524, 360], thumb: [180,150]})
has_one_attached_with_sizes(:background_image, {normal: [1000, 600]})
2020-06-10 22:31:47 +00:00
has_one_attached_with_default(:main_image, Houdini.defaults.image.profile,
filename: "main_image_#{SecureRandom.uuid}#{Pathname.new(Houdini.defaults.image.profile).extname}")
has_many :donations
has_many :charges, through: :donations
has_many :payments, through: :donations, source: 'payment'
has_many :campaign_gift_options
has_many :campaign_gifts, through: :campaign_gift_options
has_many :supporters, through: :donations
has_many :recurring_donations
has_many :campaign_gift_purchases
has_many :roles, as: :host, dependent: :destroy
has_many :comments, as: :host, dependent: :destroy
has_many :activities, as: :host, dependent: :destroy
belongs_to :profile
belongs_to :nonprofit
belongs_to :parent_campaign, class_name: 'Campaign'
has_many :children_campaigns, class_name: 'Campaign', foreign_key: 'parent_campaign_id'
scope :published, -> { where(published: true) }
scope :active, -> { where(published: true).where('end_datetime IS NULL OR end_datetime >= ?', Date.today) }
scope :past, -> { where(published: true).where('end_datetime < ?', Date.today) }
scope :unpublished, -> { where(published: [nil, false]) }
scope :not_deleted, -> { where(deleted: [nil, false]) }
scope :deleted, -> { where(deleted: true) }
scope :not_a_child, -> { where(parent_campaign_id: nil) }
before_validation do
if goal_amount_dollars.present?
self.goal_amount = (goal_amount_dollars.delete(',').to_f * 100).to_i
end
self
end
before_validation(on: :create) do
self.slug = Format::Url.convert_to_slug(name) unless slug
set_defaults
self
end
before_save do
parse_video_id if video_url && video_url_changed?
self
end
after_create do
user = profile.user
Role.create(name: :campaign_editor, user_id: user.id, host: self)
2020-06-12 18:03:59 +00:00
Houdini.event_publisher.announce(:campaign_create, self)
self
end
def set_defaults
self.total_supporters = 1
self.published = false if published.nil?
end
def parse_video_id
if video_url.include? 'vimeo'
self.vimeo_video_id = video_url.split('/').last
self.youtube_video_id = nil
elsif video_url.include? 'youtube'
match = video_url.match(/\?v=(.+)/)
return if match.nil?
self.youtube_video_id = match[1].split('&').first
self.vimeo_video_id = nil
elsif video_url.include? 'youtu.be'
self.youtube_video_id = video_url.split('/').last
self.vimeo_video_id = nil
elsif video_url.blank?
self.vimeo_video_id = nil
self.youtube_video_id = nil
end
self
end
def total_raised
payments.sum(:gross_amount)
end
def percentage_funded
goal_amount.nil? ? 0 : total_raised * 100 / goal_amount
end
def average_donation
donations.any? ? total_raised / donations.count : 0
end
# Validations
def end_datetime_cannot_be_in_past
if end_datetime.present? && end_datetime < Time.now
errors.add(:end_datetime, "can't be in the past")
end
end
def ready_to_publish?
[(body && body.length >= 500), (campaign_gift_options.count >= 1)].all?
end
def url
"#{nonprofit.url}/campaigns/#{slug}"
end
def days_left
return 0 if end_datetime.nil?
(end_datetime.to_date - Date.today).to_i
end
def finished?
end_datetime && end_datetime < Time.now
end
def child_params
excluded_for_peer_to_peer = %w[
id created_at updated_at slug profile_id url
total_raised show_recurring_amount external_identifier parent_campaign_id
reason_for_supporting metadata
]
attributes.except(*excluded_for_peer_to_peer)
end
2018-05-29 15:31:55 +00:00
def child_campaign?
2018-11-08 21:02:35 +00:00
parent_campaign.present?
2018-07-06 17:06:39 +00:00
end
def parent_campaign?
!child_campaign?
end
2020-06-18 20:46:47 +00:00
def self.get_campaign_and_children(campaign)
where('campaigns.id = ? OR campaigns.parent_campaign_id = ? ',campaign, campaign)
end
def to_builder(*expand)
Jbuilder.new do |json|
json.(self, :id, :name)
json.object "campaign"
json.nonprofit nonprofit.id
end
end
end