Validate that artist entries belong to an artist tag. Don't allow creating artist entries for character/copyright/meta tags, but do allow entries for general tags (the gentag will be automatically changed to an artist tag).
598 lines
20 KiB
Ruby
598 lines
20 KiB
Ruby
class Artist < ApplicationRecord
|
|
extend Memoist
|
|
class RevertError < Exception ; end
|
|
|
|
attr_accessor :url_string_changed
|
|
array_attribute :other_names
|
|
|
|
before_validation :normalize_name
|
|
before_validation :normalize_other_names
|
|
after_save :create_version
|
|
after_save :update_wiki
|
|
after_save :clear_url_string_changed
|
|
validate :validate_tag_category
|
|
validates :name, tag_name: true, uniqueness: true
|
|
belongs_to_creator
|
|
has_many :members, :class_name => "Artist", :foreign_key => "group_name", :primary_key => "name"
|
|
has_many :urls, :dependent => :destroy, :class_name => "ArtistUrl", :autosave => true
|
|
has_many :versions, -> {order("artist_versions.id ASC")}, :class_name => "ArtistVersion"
|
|
has_one :wiki_page, :foreign_key => "title", :primary_key => "name"
|
|
has_one :tag_alias, :foreign_key => "antecedent_name", :primary_key => "name"
|
|
belongs_to :tag, foreign_key: "name", primary_key: "name", default: -> { Tag.new(name: name, category: Tag.categories.artist) }
|
|
attribute :notes, :string
|
|
|
|
scope :active, -> { where(is_active: true) }
|
|
scope :deleted, -> { where(is_active: false) }
|
|
scope :banned, -> { where(is_banned: true) }
|
|
scope :unbanned, -> { where(is_banned: false) }
|
|
|
|
module UrlMethods
|
|
extend ActiveSupport::Concern
|
|
|
|
module ClassMethods
|
|
# Subdomains are automatically included. e.g., "twitter.com" matches "www.twitter.com",
|
|
# "mobile.twitter.com" and any other subdomain of "twitter.com".
|
|
SITE_BLACKLIST = [
|
|
"artstation.com/artist", # http://www.artstation.com/artist/serafleur/
|
|
"www.artstation.com", # http://www.artstation.com/serafleur/
|
|
%r!cdn[ab]?\.artstation\.com/p/assets/images/images!i, # https://cdna.artstation.com/p/assets/images/images/001/658/068/large/yang-waterkuma-b402.jpg?1450269769
|
|
"ask.fm", # http://ask.fm/mikuroko_396
|
|
"bcyimg.com",
|
|
"bcyimg.com/drawer", # https://img9.bcyimg.com/drawer/32360/post/178vu/46229ec06e8111e79558c1b725ebc9e6.jpg
|
|
"bcy.net",
|
|
"bcy.net/illust/detail", # https://bcy.net/illust/detail/32360/1374683
|
|
"bcy.net/u", # http://bcy.net/u/1390261
|
|
"behance.net", # "https://www.behance.net/webang111
|
|
"booru.org",
|
|
"booru.org/drawfriends", # http://img.booru.org/drawfriends//images/36/de65da5f588b76bc1d9de8af976b540e2dff17e2.jpg
|
|
"donmai.us",
|
|
"donmai.us/users", # http://danbooru.donmai.us/users/507162/
|
|
"derpibooru.org",
|
|
"derpibooru.org/tags", # https://derpibooru.org/tags/artist-colon-checkerboardazn
|
|
"deviantart.com",
|
|
"deviantart.net",
|
|
"dlsite.com",
|
|
"doujinshi.org",
|
|
"doujinshi.org/browse/circle", # http://www.doujinshi.org/browse/circle/65368/
|
|
"doujinshi.org/browse/author", # http://www.doujinshi.org/browse/author/979/23/
|
|
"doujinshi.mugimugi.org",
|
|
"doujinshi.mugimugi.org/browse/author", # http://doujinshi.mugimugi.org/browse/author/3029/
|
|
"doujinshi.mugimugi.org/browse/circle", # http://doujinshi.mugimugi.org/browse/circle/7210/
|
|
"drawcrowd.net", # https://drawcrowd.com/agussw
|
|
"drawr.net", # http://drawr.net/matsu310
|
|
"dropbox.com",
|
|
"dropbox.com/sh", # https://www.dropbox.com/sh/gz9okupqycr2vj2/GHt_oHDKsR
|
|
"dropbox.com/u", # http://dl.dropbox.com/u/76682289/daitoHP-WP/pict/
|
|
"e-hentai.org", # https://e-hentai.org/tag/artist:spirale
|
|
"e621.net",
|
|
"e621.net/post/index/1", # https://e621.net/post/index/1/spirale
|
|
"enty.jp", # https://enty.jp/aizawachihiro888
|
|
"enty.jp/users", # https://enty.jp/users/3766
|
|
"facebook.com", # https://www.facebook.com/LuutenantsLoot
|
|
"fantia.jp", # http://fantia.jp/no100
|
|
"fantia.jp/fanclubs", # https://fantia.jp/fanclubs/1711
|
|
"fav.me", # http://fav.me/d9y1njg
|
|
/blog-imgs-\d+(?:-origin)?\.fc2\.com/i,
|
|
"furaffinity.net",
|
|
"furaffinity.net/user", # http://www.furaffinity.net/user/achthenuts
|
|
"gelbooru.com", # http://gelbooru.com/index.php?page=account&s=profile&uname=junou
|
|
"inkbunny.net", # https://inkbunny.net/achthenuts
|
|
"plus.google.com", # https://plus.google.com/111509637967078773143/posts
|
|
"hentai-foundry.com",
|
|
"hentai-foundry.com/pictures/user", # http://www.hentai-foundry.com/pictures/user/aaaninja/
|
|
"hentai-foundry.com/user", # http://www.hentai-foundry.com/user/aaaninja/profile
|
|
%r!pictures\.hentai-foundry\.com(?:/\w)?!i, # http://pictures.hentai-foundry.com/a/aaaninja/
|
|
"i.imgur.com", # http://i.imgur.com/Ic9q3.jpg
|
|
"instagram.com", # http://www.instagram.com/serafleur.art/
|
|
"iwara.tv",
|
|
"iwara.tv/users", # http://ecchi.iwara.tv/users/marumega
|
|
"kym-cdn.com",
|
|
"livedoor.blogimg.jp",
|
|
"monappy.jp",
|
|
"monappy.jp/u", # https://monappy.jp/u/abara_bone
|
|
"mstdn.jp", # https://mstdn.jp/@oneb
|
|
"nicoseiga.jp",
|
|
"nicoseiga.jp/priv", # http://lohas.nicoseiga.jp/priv/2017365fb6cfbdf47ad26c7b6039feb218c5e2d4/1498430264/6820259
|
|
"nicovideo.jp",
|
|
"nicovideo.jp/user", # http://www.nicovideo.jp/user/317609
|
|
"nicovideo.jp/user/illust", # http://seiga.nicovideo.jp/user/illust/29075429
|
|
"nijie.info", # http://nijie.info/members.php?id=15235
|
|
%r!nijie\.info/nijie_picture!i, # http://pic03.nijie.info/nijie_picture/32243_20150609224803_0.png
|
|
"patreon.com", # http://patreon.com/serafleur
|
|
"pawoo.net", # https://pawoo.net/@148nasuka
|
|
"pawoo.net/web/accounts", # https://pawoo.net/web/accounts/228341
|
|
"picarto.tv", # https://picarto.tv/CheckerBoardAZN
|
|
"picarto.tv/live", # https://www.picarto.tv/live/channel.php?watch=aaaninja
|
|
"pictaram.com", # http://www.pictaram.com/user/5ish/3048385011/1350040096769940245_3048385011
|
|
"pinterest.com", # http://www.pinterest.com/alexandernanitc/
|
|
"pixiv.cc", # http://pixiv.cc/0123456789/
|
|
"pixiv.net", # https://www.pixiv.net/member.php?id=10442390
|
|
"pixiv.net/stacc", # https://www.pixiv.net/stacc/aaaninja2013
|
|
"i.pximg.net",
|
|
"plurk.com", # http://www.plurk.com/a1amorea1a1
|
|
"privatter.net",
|
|
"privatter.net/u", # http://privatter.net/u/saaaatonaaaa
|
|
"rule34.paheal.net",
|
|
"rule34.paheal.net/post/list", # http://rule34.paheal.net/post/list/Reach025/
|
|
"sankakucomplex.com", # https://chan.sankakucomplex.com/?tags=user%3ASubridet
|
|
"society6.com", # http://society6.com/serafleur/
|
|
"tinami.com",
|
|
"tinami.com/creator/profile", # http://www.tinami.com/creator/profile/29024
|
|
"data.tumblr.com",
|
|
/\d+\.media\.tumblr\.com/i,
|
|
"twipple.jp",
|
|
"twipple.jp/user", # http://p.twipple.jp/user/Type10TK
|
|
"twitch.tv", # https://www.twitch.tv/5ish
|
|
"twitpic.com",
|
|
"twitpic.com/photos", # http://twitpic.com/photos/Type10TK
|
|
"twitter.com", # https://twitter.com/akkij0358
|
|
"twitter.com/i/web/status", # https://twitter.com/i/web/status/943446161586733056
|
|
"twimg.com/media", # https://pbs.twimg.com/media/DUUUdD5VMAEuURz.jpg:orig
|
|
"ustream.tv",
|
|
"ustream.tv/channel", # http://www.ustream.tv/channel/633b
|
|
"ustream.tv/user", # http://www.ustream.tv/user/kazaputi
|
|
"vk.com", # https://vk.com/id425850679
|
|
"weibo.com", # http://www.weibo.com/5536681649
|
|
"wp.com",
|
|
"yande.re",
|
|
"youtube.com",
|
|
"youtube.com/c", # https://www.youtube.com/c/serafleurArt
|
|
"youtube.com/channel", # https://www.youtube.com/channel/UCfrCa2Y6VulwHD3eNd3HBRA
|
|
"youtube.com/user", # https://www.youtube.com/user/148nasuka
|
|
"youtu.be", # http://youtu.be/gibeLKKRT-0
|
|
]
|
|
|
|
SITE_BLACKLIST_REGEXP = Regexp.union(SITE_BLACKLIST.map do |domain|
|
|
domain = Regexp.escape(domain) if domain.is_a?(String)
|
|
%r!\Ahttps?://(?:[a-zA-Z0-9_-]+\.)*#{domain}/\z!i
|
|
end)
|
|
|
|
def find_artists(url)
|
|
url = ArtistUrl.normalize(url)
|
|
artists = []
|
|
|
|
# return [] unless Sources::Strategies.find(url).normalized_for_artist_finder?
|
|
|
|
while artists.empty? && url.size > 10
|
|
u = url.sub(/\/+$/, "") + "/"
|
|
u = u.to_escaped_for_sql_like.gsub(/\*/, '%') + '%'
|
|
artists += Artist.joins(:urls).where(["artists.is_active = TRUE AND artist_urls.normalized_url LIKE ? ESCAPE E'\\\\'", u]).limit(10).order("artists.name").all
|
|
url = File.dirname(url) + "/"
|
|
|
|
break if url =~ SITE_BLACKLIST_REGEXP
|
|
end
|
|
|
|
where(id: artists.uniq(&:name).take(20))
|
|
end
|
|
end
|
|
|
|
included do
|
|
memoize :domains
|
|
end
|
|
|
|
def sorted_urls
|
|
urls.sort {|a, b| a.priority <=> b.priority}
|
|
end
|
|
|
|
def url_array
|
|
urls.map(&:to_s).sort
|
|
end
|
|
|
|
def url_string
|
|
url_array.join("\n")
|
|
end
|
|
|
|
def url_string=(string)
|
|
url_string_was = url_string
|
|
|
|
self.urls = string.to_s.scan(/[^[:space:]]+/).map do |url|
|
|
is_active, url = ArtistUrl.parse_prefix(url)
|
|
self.urls.find_or_initialize_by(url: url, is_active: is_active)
|
|
end.uniq(&:url)
|
|
|
|
self.url_string_changed = (url_string_was != url_string)
|
|
end
|
|
|
|
def clear_url_string_changed
|
|
self.url_string_changed = false
|
|
end
|
|
|
|
def map_domain(x)
|
|
case x
|
|
when "pximg.net"
|
|
"pixiv.net"
|
|
|
|
when "deviantart.net"
|
|
"deviantart.com"
|
|
|
|
else
|
|
x
|
|
end
|
|
end
|
|
|
|
def domains
|
|
Cache.get("artist-domains-#{id}", 1.day) do
|
|
Post.raw_tag_match(name).pluck(:source).map do |x|
|
|
begin
|
|
map_domain(Addressable::URI.parse(x).domain)
|
|
rescue Addressable::URI::InvalidURIError
|
|
nil
|
|
end
|
|
end.compact.inject(Hash.new(0)) {|h, x| h[x] += 1; h}.sort {|a, b| b[1] <=> a[1]}
|
|
end
|
|
end
|
|
end
|
|
|
|
module NameMethods
|
|
extend ActiveSupport::Concern
|
|
|
|
module ClassMethods
|
|
def normalize_name(name)
|
|
name.to_s.mb_chars.downcase.strip.gsub(/ /, '_').to_s
|
|
end
|
|
end
|
|
|
|
def normalize_name
|
|
self.name = Artist.normalize_name(name)
|
|
end
|
|
|
|
def pretty_name
|
|
name.tr("_", " ")
|
|
end
|
|
|
|
def normalize_other_names
|
|
self.other_names = other_names.map { |x| Artist.normalize_name(x) }.uniq
|
|
self.other_names -= [name]
|
|
end
|
|
end
|
|
|
|
module GroupMethods
|
|
def member_names
|
|
members.map(&:name).join(", ")
|
|
end
|
|
end
|
|
|
|
module VersionMethods
|
|
def create_version(force=false)
|
|
if saved_change_to_name? || url_string_changed || saved_change_to_is_active? || saved_change_to_is_banned? || saved_change_to_other_names? || saved_change_to_group_name? || saved_change_to_notes? || force
|
|
if merge_version?
|
|
merge_version
|
|
else
|
|
create_new_version
|
|
end
|
|
end
|
|
end
|
|
|
|
def create_new_version
|
|
ArtistVersion.create(
|
|
:artist_id => id,
|
|
:name => name,
|
|
:updater_id => CurrentUser.id,
|
|
:updater_ip_addr => CurrentUser.ip_addr,
|
|
:urls => url_array,
|
|
:is_active => is_active,
|
|
:is_banned => is_banned,
|
|
:other_names => other_names,
|
|
:group_name => group_name
|
|
)
|
|
end
|
|
|
|
def merge_version
|
|
prev = versions.last
|
|
prev.update_attributes(
|
|
:name => name,
|
|
:urls => url_array,
|
|
:is_active => is_active,
|
|
:is_banned => is_banned,
|
|
:other_names => other_names,
|
|
:group_name => group_name
|
|
)
|
|
end
|
|
|
|
def merge_version?
|
|
prev = versions.last
|
|
prev && prev.updater_id == CurrentUser.user.id && prev.updated_at > 1.hour.ago
|
|
end
|
|
|
|
def revert_to!(version)
|
|
if id != version.artist_id
|
|
raise RevertError.new("You cannot revert to a previous version of another artist.")
|
|
end
|
|
|
|
self.name = version.name
|
|
self.url_string = version.urls.join("\n")
|
|
self.is_active = version.is_active
|
|
self.other_names = version.other_names
|
|
self.group_name = version.group_name
|
|
save
|
|
end
|
|
end
|
|
|
|
module FactoryMethods
|
|
# Make a new artist, fetching the defaults either from the given source, or
|
|
# from the source of the artist's last upload.
|
|
def new_with_defaults(params)
|
|
source = params.delete(:source)
|
|
|
|
if source.blank? && params[:name].present?
|
|
CurrentUser.without_safe_mode do
|
|
post = Post.tag_match("source:http* #{params[:name]}").first
|
|
source = post.try(:source)
|
|
end
|
|
end
|
|
|
|
if source.present?
|
|
artist = Sources::Strategies.find(source).new_artist
|
|
artist.attributes = params
|
|
else
|
|
artist = Artist.new(params)
|
|
end
|
|
|
|
artist.normalize_name
|
|
artist.normalize_other_names
|
|
artist
|
|
end
|
|
end
|
|
|
|
module NoteMethods
|
|
extend ActiveSupport::Concern
|
|
|
|
def notes
|
|
@notes || wiki_page.try(:body)
|
|
end
|
|
|
|
def notes=(text)
|
|
if notes != text
|
|
notes_will_change!
|
|
@notes = text
|
|
end
|
|
end
|
|
|
|
def reload(options = nil)
|
|
flush_cache
|
|
|
|
if instance_variable_defined?(:@notes)
|
|
remove_instance_variable(:@notes)
|
|
end
|
|
|
|
super
|
|
end
|
|
|
|
def notes_changed?
|
|
attribute_changed?("notes")
|
|
end
|
|
|
|
def notes_will_change!
|
|
attribute_will_change!("notes")
|
|
end
|
|
|
|
def update_wiki
|
|
if persisted? && saved_change_to_name? && attribute_before_last_save("name").present? && WikiPage.titled(attribute_before_last_save("name")).exists?
|
|
# we're renaming the artist, so rename the corresponding wiki page
|
|
old_page = WikiPage.titled(name_before_last_save).first
|
|
|
|
if wiki_page.present?
|
|
# a wiki page with the new name already exists, so update the content
|
|
wiki_page.update(body: "#{wiki_page.body}\n\n#{@notes}")
|
|
else
|
|
# a wiki page doesn't already exist for the new name, so rename the old one
|
|
old_page.update(title: name, body: @notes)
|
|
end
|
|
elsif wiki_page.nil?
|
|
# if there are any notes, we need to create a new wiki page
|
|
if @notes.present?
|
|
wp = create_wiki_page(body: @notes, title: name)
|
|
end
|
|
elsif (!@notes.nil? && (wiki_page.body != @notes)) || wiki_page.title != name
|
|
# if anything changed, we need to update the wiki page
|
|
wiki_page.body = @notes unless @notes.nil?
|
|
wiki_page.title = name
|
|
wiki_page.save
|
|
end
|
|
end
|
|
end
|
|
|
|
module TagMethods
|
|
def has_tag_alias?
|
|
TagAlias.active.exists?(["antecedent_name = ?", name])
|
|
end
|
|
|
|
def tag_alias_name
|
|
TagAlias.active.find_by_antecedent_name(name).consequent_name
|
|
end
|
|
|
|
def category_name
|
|
Tag.category_for(name)
|
|
end
|
|
|
|
def validate_tag_category
|
|
return unless is_active? && name_changed?
|
|
|
|
if tag.category_name == "General"
|
|
tag.update(category: Tag.categories.artist)
|
|
elsif tag.category_name != "Artist"
|
|
errors[:base] << "'#{name}' is a #{tag.category_name.downcase} tag; artist entries can only be created for artist tags"
|
|
end
|
|
end
|
|
end
|
|
|
|
module BanMethods
|
|
def unban!
|
|
Post.transaction do
|
|
CurrentUser.without_safe_mode do
|
|
ti = TagImplication.where(:antecedent_name => name, :consequent_name => "banned_artist").first
|
|
ti.destroy if ti
|
|
|
|
begin
|
|
Post.tag_match(name).where("true /* Artist.unban */").each do |post|
|
|
post.unban!
|
|
fixed_tags = post.tag_string.sub(/(?:\A| )banned_artist(?:\Z| )/, " ").strip
|
|
post.update_attributes(:tag_string => fixed_tags)
|
|
end
|
|
rescue Post::SearchError
|
|
# swallow
|
|
end
|
|
|
|
update_column(:is_banned, false)
|
|
ModAction.log("unbanned artist ##{id}",:artist_unban)
|
|
end
|
|
end
|
|
end
|
|
|
|
def ban!
|
|
Post.transaction do
|
|
CurrentUser.without_safe_mode do
|
|
begin
|
|
Post.tag_match(name).where("true /* Artist.ban */").each do |post|
|
|
post.ban!
|
|
end
|
|
rescue Post::SearchError
|
|
# swallow
|
|
end
|
|
|
|
# potential race condition but unlikely
|
|
unless TagImplication.where(:antecedent_name => name, :consequent_name => "banned_artist").exists?
|
|
tag_implication = TagImplication.create!(:antecedent_name => name, :consequent_name => "banned_artist", :skip_secondary_validations => true)
|
|
tag_implication.approve!(approver: CurrentUser.user)
|
|
end
|
|
|
|
update_column(:is_banned, true)
|
|
ModAction.log("banned artist ##{id}",:artist_ban)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
module SearchMethods
|
|
def named(name)
|
|
where(name: normalize_name(name))
|
|
end
|
|
|
|
def any_other_name_matches(regex)
|
|
where(id: Artist.from("unnest(other_names) AS other_name").where("other_name ~ ?", regex))
|
|
end
|
|
|
|
def any_other_name_like(name)
|
|
where(id: Artist.from("unnest(other_names) AS other_name").where("other_name LIKE ?", name.to_escaped_for_sql_like))
|
|
end
|
|
|
|
def any_name_matches(query)
|
|
if query =~ %r!\A/(.*)/\z!
|
|
where_regex(:name, $1).or(any_other_name_matches($1)).or(where_regex(:group_name, $1))
|
|
else
|
|
normalized_name = normalize_name(query)
|
|
normalized_name = "*#{normalized_name}*" unless normalized_name.include?("*")
|
|
where_like(:name, normalized_name).or(any_other_name_like(normalized_name)).or(where_like(:group_name, normalized_name))
|
|
end
|
|
end
|
|
|
|
def url_matches(query)
|
|
if query =~ %r!\A/(.*)/\z!
|
|
where(id: ArtistUrl.where_regex(:url, $1).select(:artist_id))
|
|
elsif query.include?("*")
|
|
where(id: ArtistUrl.where_like(:url, query).select(:artist_id))
|
|
elsif query =~ %r!\Ahttps?://!i
|
|
find_artists(query)
|
|
else
|
|
where(id: ArtistUrl.where_like(:url, "*#{query}*").select(:artist_id))
|
|
end
|
|
end
|
|
|
|
def any_name_or_url_matches(query)
|
|
if query =~ %r!\Ahttps?://!i
|
|
url_matches(query)
|
|
else
|
|
any_name_matches(query)
|
|
end
|
|
end
|
|
|
|
def search(params)
|
|
q = super
|
|
|
|
q = q.search_text_attribute(:name, params)
|
|
q = q.search_text_attribute(:group_name, params)
|
|
|
|
if params[:any_other_name_like]
|
|
q = q.any_other_name_like(params[:any_other_name_like])
|
|
end
|
|
|
|
if params[:any_name_matches].present?
|
|
q = q.any_name_matches(params[:any_name_matches])
|
|
end
|
|
|
|
if params[:any_name_or_url_matches].present?
|
|
q = q.any_name_or_url_matches(params[:any_name_or_url_matches])
|
|
end
|
|
|
|
if params[:url_matches].present?
|
|
q = q.url_matches(params[:url_matches])
|
|
end
|
|
|
|
q = q.attribute_matches(:is_active, params[:is_active])
|
|
q = q.attribute_matches(:is_banned, params[:is_banned])
|
|
|
|
if params[:creator_name].present?
|
|
q = q.where("artists.creator_id = (select _.id from users _ where lower(_.name) = ?)", params[:creator_name].tr(" ", "_").mb_chars.downcase)
|
|
end
|
|
|
|
if params[:creator_id].present?
|
|
q = q.where("artists.creator_id = ?", params[:creator_id].to_i)
|
|
end
|
|
|
|
if params[:has_tag].to_s.truthy?
|
|
q = q.joins(:tag).where("tags.post_count > 0")
|
|
elsif params[:has_tag].to_s.falsy?
|
|
q = q.includes(:tag).where("tags.name IS NULL OR tags.post_count <= 0").references(:tags)
|
|
end
|
|
|
|
params[:order] ||= params.delete(:sort)
|
|
case params[:order]
|
|
when "name"
|
|
q = q.order("artists.name")
|
|
when "updated_at"
|
|
q = q.order("artists.updated_at desc")
|
|
when "post_count"
|
|
q = q.includes(:tag).order("tags.post_count desc nulls last").order("artists.name").references(:tags)
|
|
else
|
|
q = q.apply_default_order(params)
|
|
end
|
|
|
|
q
|
|
end
|
|
end
|
|
|
|
include UrlMethods
|
|
include NameMethods
|
|
include GroupMethods
|
|
include VersionMethods
|
|
extend FactoryMethods
|
|
include NoteMethods
|
|
include TagMethods
|
|
include BanMethods
|
|
extend SearchMethods
|
|
|
|
def status
|
|
if is_banned? && is_active?
|
|
"Banned"
|
|
elsif is_banned?
|
|
"Banned Deleted"
|
|
elsif is_active?
|
|
"Active"
|
|
else
|
|
"Deleted"
|
|
end
|
|
end
|
|
|
|
def deletable_by?(user)
|
|
user.is_builder?
|
|
end
|
|
|
|
def editable_by?(user)
|
|
user.is_builder? || (!is_banned? && is_active?)
|
|
end
|
|
|
|
def visible?
|
|
!is_banned? || CurrentUser.is_gold?
|
|
end
|
|
end
|