* feat: remove initializeSponsorshipVisibility and related code * feat: remove sponsorships from sidebar * feat: remove sponsorships from the admin - route, controller, view, spec * feat: remove the admin menu item * feat: remove the i8n for admin sponsors controller * feat: sponsorship decorator was not being used anywhere * feat: sponsorship slack messenger was not being used anywhere * feat: remove the sponsorship_headline that gets configures on the admin * feat: remove the /sponsors page * feat: remove renedring of single_sponsor partial and associated partials * feat: remove the navigation link rake task for sponsors * feat: remove sponsorship from tags * feat: remove i8n constants used * remove sponsor references in text to the privacy page * feat: remove the sponsorship detail from the organization page * feat: remove the sponsors css that was used for app/views/pages/sponsors.html.erb * feat: remove the sponsorship i8n that was used in the slack messengers * feat: swap out the decorators to use Article as an example * feat: remove spec to show sponsors on home page * feat: update the specs to use Article Decorator instead of the Sponsorship Decorator * fix: use direct and not all * fix: remove tests for tag sponsorship * fix: remove organization sponsorship test * feat: remove more i8n * remove unused css from app/views/organizations/_sidebar_additional.html.erb * feat: remove sponsorship relationship from tag and organization * remove seed * feat: remove all Sponsor related specs * feat: remove model, associated i8n and specs * feat: remove sponsor from navbar * feat: add a guard clause if we're unable to constantinize the purchase_type * feat; remove sponsorship related tests * feat: remove sponsorship relationship * feat: remov especs related to sponsorship * feat: remove tag sponsorship validation * feat: remove the sponsorship factory
147 lines
4.8 KiB
Ruby
147 lines
4.8 KiB
Ruby
class Organization < ApplicationRecord
|
|
include CloudinaryHelper
|
|
|
|
include Images::Profile.for(:profile_image_url)
|
|
|
|
COLOR_HEX_REGEXP = /\A#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\z/
|
|
INTEGER_REGEXP = /\A\d+\z/
|
|
SLUG_REGEXP = /\A[a-zA-Z0-9\-_]+\z/
|
|
|
|
acts_as_followable
|
|
|
|
before_validation :downcase_slug
|
|
before_validation :check_for_slug_change
|
|
before_validation :evaluate_markdown
|
|
|
|
before_save :remove_at_from_usernames
|
|
before_save :generate_secret
|
|
|
|
after_save :bust_cache
|
|
|
|
after_update_commit :conditionally_update_articles
|
|
after_destroy_commit :bust_cache
|
|
|
|
has_many :articles, dependent: :nullify
|
|
has_many :collections, dependent: :nullify
|
|
has_many :credits, dependent: :restrict_with_error
|
|
has_many :display_ads, dependent: :destroy
|
|
has_many :listings, dependent: :destroy
|
|
has_many :notifications, dependent: :delete_all
|
|
has_many :organization_memberships, dependent: :delete_all
|
|
has_many :profile_pins, as: :profile, inverse_of: :profile, dependent: :destroy
|
|
has_many :unspent_credits, -> { where spent: false }, class_name: "Credit", inverse_of: :organization
|
|
has_many :users, through: :organization_memberships
|
|
|
|
validates :articles_count, presence: true
|
|
validates :bg_color_hex, format: COLOR_HEX_REGEXP, allow_blank: true
|
|
validates :company_size, format: { with: INTEGER_REGEXP, message: :integer_only, allow_blank: true }
|
|
validates :company_size, length: { maximum: 7 }, allow_nil: true
|
|
validates :credits_count, presence: true
|
|
validates :cta_body_markdown, length: { maximum: 256 }
|
|
validates :cta_button_text, length: { maximum: 20 }
|
|
validates :cta_button_url, length: { maximum: 150 }, url: { allow_blank: true, no_local: true }
|
|
validates :github_username, length: { maximum: 50 }
|
|
validates :location, :email, length: { maximum: 64 }
|
|
validates :name, :summary, :url, :profile_image, presence: true
|
|
validates :name, length: { maximum: 50 }
|
|
validates :proof, length: { maximum: 1500 }
|
|
validates :secret, length: { is: 100 }, allow_nil: true
|
|
validates :secret, uniqueness: true
|
|
validates :slug, exclusion: { in: ReservedWords.all, message: :reserved_word }
|
|
validates :slug, format: { with: SLUG_REGEXP }, length: { in: 2..18 }
|
|
validates :slug, presence: true, uniqueness: { case_sensitive: false }
|
|
validates :spent_credits_count, presence: true
|
|
validates :summary, length: { maximum: 250 }
|
|
validates :tag_line, length: { maximum: 60 }
|
|
validates :tech_stack, :story, length: { maximum: 640 }
|
|
validates :text_color_hex, format: COLOR_HEX_REGEXP, allow_blank: true
|
|
validates :twitter_username, length: { maximum: 15 }
|
|
validates :unspent_credits_count, presence: true
|
|
validates :url, length: { maximum: 200 }, url: { allow_blank: true, no_local: true }
|
|
|
|
validates :slug, unique_cross_model_slug: true, if: :slug_changed?
|
|
|
|
mount_uploader :profile_image, ProfileImageUploader
|
|
mount_uploader :nav_image, ProfileImageUploader
|
|
mount_uploader :dark_nav_image, ProfileImageUploader
|
|
|
|
alias_attribute :username, :slug
|
|
alias_attribute :old_username, :old_slug
|
|
alias_attribute :old_old_username, :old_old_slug
|
|
alias_attribute :website_url, :url
|
|
|
|
def self.integer_only
|
|
I18n.t("models.organization.integer_only")
|
|
end
|
|
|
|
def self.reserved_word
|
|
I18n.t("models.organization.reserved_word")
|
|
end
|
|
|
|
def check_for_slug_change
|
|
return unless slug_changed?
|
|
|
|
self.old_old_slug = old_slug
|
|
self.old_slug = slug_was
|
|
Organizations::UpdateOrganizationArticlesPathsWorker.perform_async(id, slug_was, slug)
|
|
end
|
|
|
|
def path
|
|
"/#{slug}"
|
|
end
|
|
|
|
def generate_secret
|
|
self.secret = generated_random_secret if secret.blank?
|
|
end
|
|
|
|
def generated_random_secret
|
|
SecureRandom.hex(50)
|
|
end
|
|
|
|
def approved_and_filled_out_cta?
|
|
cta_processed_html?
|
|
end
|
|
|
|
def profile_image_90
|
|
profile_image_url_for(length: 90)
|
|
end
|
|
|
|
def enough_credits?(num_credits_needed)
|
|
credits.unspent.size >= num_credits_needed
|
|
end
|
|
|
|
def destroyable?
|
|
organization_memberships.count == 1 && articles.count.zero? && credits.count.zero?
|
|
end
|
|
|
|
# NOTE: We use Organization and User objects interchangeably. Since the former
|
|
# don't have profiles we return self instead.
|
|
def profile
|
|
self
|
|
end
|
|
|
|
private
|
|
|
|
def evaluate_markdown
|
|
self.cta_processed_html = MarkdownProcessor::Parser.new(cta_body_markdown).evaluate_limited_markdown
|
|
end
|
|
|
|
def remove_at_from_usernames
|
|
self.twitter_username = twitter_username.delete("@") if twitter_username
|
|
self.github_username = github_username.delete("@") if github_username
|
|
end
|
|
|
|
def downcase_slug
|
|
self.slug = slug&.downcase
|
|
end
|
|
|
|
def conditionally_update_articles
|
|
return unless Article::ATTRIBUTES_CACHED_FOR_RELATED_ENTITY.detect { |attr| saved_change_to_attribute?(attr) }
|
|
|
|
articles.each(&:save)
|
|
end
|
|
|
|
def bust_cache
|
|
Organizations::BustCacheWorker.perform_async(id, slug)
|
|
end
|
|
end
|