* feat: Add a pack file that pulls in the MultiSelect Component * feat: move the tags to its own component * save tags * refactor: create a getCSRFToken function in the packs files so that it can be used in the admin * feat: import the new module in request.js * feat: remove unnecessary id * feat: first pass of csrf token test * chore: update the test * fix: csrf token * feat: hide the enw functionality behinda feature flag * fix: loading form twice * refactor: import for csrftoken * chore: update the description of the function * feat: use acts_on_taggable to craete a relationship between display_ad and tag * feat: add a tag field to the display_ad form * feat: add the selected tags from the multiselect autocomplete component to the input text field that references the tag_list * feat: add the tag_list to the controller so that we can save it to the db with the display ad parameters * feat: pull out the tag validation from the article and the display_ads into a concern * feat: write soem tests for validating the tag on the display_ads model * feat: add the tag_list as a hidden field * feat: set the selected tags on edit * feat: add a js class for the placement area * feat: use the change in the dropdown to determine whether we show the tags field dropdown * refactor: rename and brak up fucntions * feat: show the tags field if the value of the placement area is already set * fix: move if statement out of the change event * feat: hide tags field and clear the tag list * refactor: delete the display ads * tests: ensure that we test the toggle * fix:ensure that the tags on the hidden field show up as a string in the input field * feat: update the jsdoc * fix: no need to replace space with comma since we showing as string in form * feat: update the name of the label * fix: add hidden back * feat: add cached_tag_list to displya_ads + the index * feat: udpdate the schema * refactor: move the scopes into the a module that can be re-used between display_ads and articles * feat: account for showing display ads with the targeted tags that match the article * feat: write a spec to show the correct ads based on the article_tags * refactor: change TagListValidateable to Taggable and move out the not_cached_tagged_with_any scope * test teh relationship between display ads and tags * feat: write soem tests for validating the tag on the display_ads model * chore: change empty to blank * feat: will show display ads with no tags set if there are no article tags * feat: add a newline
99 lines
3.4 KiB
Ruby
99 lines
3.4 KiB
Ruby
class DisplayAd < ApplicationRecord
|
|
include Taggable
|
|
acts_as_taggable_on :tags
|
|
resourcify
|
|
|
|
ALLOWED_PLACEMENT_AREAS = %w[sidebar_left sidebar_left_2 sidebar_right post_comments].freeze
|
|
ALLOWED_PLACEMENT_AREAS_HUMAN_READABLE = ["Sidebar Left (First Position)",
|
|
"Sidebar Left (Second Position)",
|
|
"Sidebar Right",
|
|
"Below the comment section"].freeze
|
|
|
|
MAX_TAG_LIST_SIZE = 10
|
|
POST_WIDTH = 775
|
|
SIDEBAR_WIDTH = 350
|
|
|
|
enum display_to: { all: 0, logged_in: 1, logged_out: 2 }, _prefix: true
|
|
|
|
belongs_to :organization, optional: true
|
|
has_many :display_ad_events, dependent: :destroy
|
|
|
|
validates :placement_area, presence: true,
|
|
inclusion: { in: ALLOWED_PLACEMENT_AREAS }
|
|
validates :body_markdown, presence: true
|
|
validate :validate_tag
|
|
before_save :process_markdown
|
|
after_save :generate_display_ad_name
|
|
|
|
scope :approved_and_published, -> { where(approved: true, published: true) }
|
|
|
|
scope :search_ads, lambda { |term|
|
|
where "name ILIKE :search OR processed_html ILIKE :search OR placement_area ILIKE :search",
|
|
search: "%#{term}%"
|
|
}
|
|
|
|
def self.for_display(area, user_signed_in, article_tags = [])
|
|
relation = approved_and_published.where(placement_area: area).order(success_rate: :desc)
|
|
|
|
if article_tags.any?
|
|
display_ads_with_no_tags = relation.where(cached_tag_list: "")
|
|
display_ads_with_targeted_article_tags = relation.cached_tagged_with_any(article_tags)
|
|
|
|
relation = display_ads_with_no_tags.or(display_ads_with_targeted_article_tags)
|
|
end
|
|
|
|
if article_tags.blank?
|
|
relation = relation.where(cached_tag_list: "")
|
|
end
|
|
|
|
relation = if user_signed_in
|
|
relation.where(display_to: %w[all logged_in])
|
|
else
|
|
relation.where(display_to: %w[all logged_out])
|
|
end
|
|
|
|
relation.order(success_rate: :desc)
|
|
|
|
if rand(8) == 1
|
|
relation.sample
|
|
else
|
|
relation.limit(rand(1..15)).sample
|
|
end
|
|
end
|
|
|
|
def human_readable_placement_area
|
|
ALLOWED_PLACEMENT_AREAS_HUMAN_READABLE[ALLOWED_PLACEMENT_AREAS.find_index(placement_area)]
|
|
end
|
|
|
|
def validate_tag
|
|
# check there are not too many tags
|
|
return errors.add(:tag_list, I18n.t("models.article.too_many_tags")) if tag_list.size > MAX_TAG_LIST_SIZE
|
|
|
|
validate_tag_name(tag_list)
|
|
end
|
|
|
|
private
|
|
|
|
def generate_display_ad_name
|
|
return unless name.nil?
|
|
|
|
self.name = "Display Ad #{id}"
|
|
save!
|
|
end
|
|
|
|
def process_markdown
|
|
renderer = Redcarpet::Render::HTMLRouge.new(hard_wrap: true, filter_html: false)
|
|
markdown = Redcarpet::Markdown.new(renderer, Constants::Redcarpet::CONFIG)
|
|
initial_html = markdown.render(body_markdown)
|
|
stripped_html = ActionController::Base.helpers.sanitize initial_html,
|
|
tags: MarkdownProcessor::AllowedTags::DISPLAY_AD,
|
|
attributes: MarkdownProcessor::AllowedAttributes::DISPLAY_AD
|
|
html = stripped_html.delete("\n")
|
|
self.processed_html = Html::Parser.new(html)
|
|
.prefix_all_images(prefix_width, synchronous_detail_detection: true).html
|
|
end
|
|
|
|
def prefix_width
|
|
placement_area.to_s == "post_comments" ? POST_WIDTH : SIDEBAR_WIDTH
|
|
end
|
|
end
|