forked from e621ng/e621ng
Sets
This commit is contained in:
parent
fa619e3e1e
commit
45394916d8
@ -85,6 +85,10 @@ class ApplicationController < ActionController::Base
|
||||
puts "---"
|
||||
end
|
||||
|
||||
if Rails.env.development?
|
||||
Rails.logger.error("<<< \n #{exception.class} exception thrown: #{exception.message}\n#{exception.backtrace.join("\n")}\n<<<")
|
||||
end
|
||||
|
||||
if exception.is_a?(::ActiveRecord::StatementInvalid) && exception.to_s =~ /statement timeout/
|
||||
if Rails.env.production?
|
||||
NewRelic::Agent.notice_error(exception, :uri => request.original_url, :referer => request.referer, :request_params => params, :custom_params => {:user_id => CurrentUser.user.id, :user_ip_addr => CurrentUser.ip_addr})
|
||||
|
94
app/controllers/post_set_maintainers_controller.rb
Normal file
94
app/controllers/post_set_maintainers_controller.rb
Normal file
@ -0,0 +1,94 @@
|
||||
class PostSetMaintainersController < ApplicationController
|
||||
respond_to :html
|
||||
respond_to :js, except: [:index]
|
||||
before_action :member_only
|
||||
|
||||
|
||||
def index
|
||||
@invites = PostSetMaintainer.where(user_id: CurrentUser.id).includes(:post_set)
|
||||
end
|
||||
|
||||
def create
|
||||
@set = PostSet.find(params[:post_set_id])
|
||||
@user = User.find_by_name(params[:username])
|
||||
check_edit_access(@set)
|
||||
@invite = PostSetMaintainer.new(post_set_id: @set.id, user_id: @user.id, status: 'pending')
|
||||
@invite.validate
|
||||
|
||||
if @invite.invalid?
|
||||
flash[:notice] = @invite.errors.full_messages.join('; ')
|
||||
redirect_to maintainers_post_set_path(@set)
|
||||
return
|
||||
end
|
||||
|
||||
if RateLimiter.check_limit("set.invite.#{CurrentUser.id}", 5, 1.hours)
|
||||
flash[:notice] = "You must wait an hour before inviting more set maintainers."
|
||||
|
||||
end
|
||||
|
||||
PostSetMaintainer.where(user_id: @user.id, post_set_id: @set.id).destroy_all
|
||||
@invite.save
|
||||
|
||||
if @invite.valid?
|
||||
RateLimiter.hit("set.invite.#{CurrentUser.id}", 1.hours)
|
||||
flash[:notice] = "#{@user.pretty_name} invited to be a maintainer"
|
||||
else
|
||||
flash[:notice] = @invite.errors.full_messages.join('; ')
|
||||
end
|
||||
redirect_to maintainers_post_set_path(@set)
|
||||
end
|
||||
|
||||
def destroy
|
||||
@maintainer = PostSetMaintainer.find(params[:id] || params[:post_set_maintainer][:id])
|
||||
@set = @maintainer.post_set
|
||||
check_edit_access(@set)
|
||||
check_cancel_access(@maintainer)
|
||||
|
||||
@maintainer.cancel!
|
||||
end
|
||||
|
||||
def approve
|
||||
@maintainer = PostSetMaintainer.find(params[:id])
|
||||
check_approve_access(@maintainer)
|
||||
|
||||
@maintainer.approve!
|
||||
end
|
||||
|
||||
def deny
|
||||
@maintainer = PostSetMaintainer.find(params[:id])
|
||||
raise User::PrivilegeError unless @maintainer.user_id == CurrentUser.id
|
||||
|
||||
@maintainer.deny!
|
||||
end
|
||||
|
||||
def block
|
||||
@maintainer = PostSetMaintainer.find(params[:id])
|
||||
check_block_access(@maintainer)
|
||||
|
||||
@maintainer.block!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_approve_access(maintainer)
|
||||
raise User::PrivilegeError unless maintainer.user_id == CurrentUser.id
|
||||
raise User::PrivilegeError if ['blocked', 'approved'].include?(maintainer.status)
|
||||
end
|
||||
|
||||
def check_cancel_access(maintainer)
|
||||
raise User::PrivilegeError if maintainer.status == 'blocked'
|
||||
raise User::PrivilegeError if maintainer.status == 'cooldown' && @maintainer.created_at > 24.hours.ago
|
||||
end
|
||||
|
||||
def check_block_access(maintainer)
|
||||
raise User::PrivilegeError unless maintainer.user_id == CurrentUser.id
|
||||
raise User::PrivilegeError if maintainer.status == 'blocked'
|
||||
end
|
||||
|
||||
def check_edit_access(set)
|
||||
unless set.is_owner?(CurrentUser) || CurrentUser.is_admin?
|
||||
raise User::PrivilegeError
|
||||
end
|
||||
end
|
||||
|
||||
end
|
166
app/controllers/post_sets_controller.rb
Normal file
166
app/controllers/post_sets_controller.rb
Normal file
@ -0,0 +1,166 @@
|
||||
class PostSetsController < ApplicationController
|
||||
respond_to :html, :json, :xml
|
||||
before_action :member_only, only: [:new, :create, :update, :destroy,
|
||||
:edit, :maintainers, :update_posts,
|
||||
:add_post, :remove_post]
|
||||
|
||||
def index
|
||||
if !params[:post_id].blank?
|
||||
if CurrentUser.is_admin?
|
||||
@sets = PostSet.paginate(include: :set_entries, conditions: ["set_entries.post_id = ?", params[:post_id]], page: 1, per_page: 50)
|
||||
else
|
||||
@sets = PostSet.paginate(include: :set_entries, conditions: ["(post_sets.public = TRUE OR post_sets.user_id = ?) AND set_entries.post_id = ?", CurrentUser.id || 0, params[:post_id]], page: 1, per_page: 50)
|
||||
end
|
||||
elsif !params[:maintainer_id].blank?
|
||||
if CurrentUser.is_admin?
|
||||
@sets = PostSet.paginate(include: :set_maintainers, conditions: ["(set_maintainers.user_id = ? AND set_maintainers.status = 'approved')", params[:maintainer_id]], page: 1, per_page: 50)
|
||||
else
|
||||
@sets = PostSet.paginate(include: :set_maintainers, conditions: ["(post_sets.public = TRUE OR post_sets.user_id = ?) AND (set_maintainers.user_id = ? AND set_maintainers.status = 'approved')", CurrentUser.id || 0, params[:maintainer_id]], page: 1, per_page: 50)
|
||||
end
|
||||
else
|
||||
@sets = PostSet.search(search_params).paginate(params[:page], limit: params[:limit])
|
||||
end
|
||||
|
||||
respond_with(@sets)
|
||||
end
|
||||
|
||||
def atom
|
||||
begin
|
||||
@sets = PostSet.visible.order(id: :desc).limit(32)
|
||||
headers["Content-Type"] = "application/atom+xml"
|
||||
rescue RuntimeError => e
|
||||
@set = []
|
||||
end
|
||||
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def new
|
||||
@set = PostSet.new
|
||||
end
|
||||
|
||||
def create
|
||||
@set = PostSet.create(set_params)
|
||||
flash[:notice] = @set.valid? ? 'Set created' : @set.errors.full_messages.join('; ')
|
||||
respond_with(@set)
|
||||
end
|
||||
|
||||
def show
|
||||
@set = PostSet.find(params[:id])
|
||||
check_view_access(@set)
|
||||
|
||||
respond_with(@set)
|
||||
end
|
||||
|
||||
def edit
|
||||
@set = PostSet.find(params[:id])
|
||||
check_edit_access(@set)
|
||||
@can_edit = @set.is_owner?(CurrentUser) || CurrentUser.is_admin?
|
||||
respond_with(@set)
|
||||
end
|
||||
|
||||
def update
|
||||
@set = PostSet.find(params[:id])
|
||||
check_edit_access(@set)
|
||||
@set.update(set_params)
|
||||
flash[:notice] = @set.valid? ? 'Set updated.' : @set.errors.full_messages.join('; ')
|
||||
|
||||
if CurrentUser.is_admin? && !@set.is_owner?(CurrentUser.user)
|
||||
if @set.saved_change_to_is_public?
|
||||
ModAction.log("User ##{CurrentUser.id} marked set ##{@set.id} as private", :set_mark_private)
|
||||
end
|
||||
|
||||
if @set.saved_change_to_watched_attribute?
|
||||
ModAction.log("Admin ##{CurrentUser.id} edited set ##{@set.id}", :set_edit)
|
||||
end
|
||||
end
|
||||
|
||||
respond_with(@set)
|
||||
end
|
||||
|
||||
def maintainers
|
||||
@set = PostSet.find(params[:id])
|
||||
end
|
||||
|
||||
def post_list
|
||||
@set = PostSet.find(params[:id])
|
||||
check_edit_access(@set)
|
||||
respond_with(@set)
|
||||
end
|
||||
|
||||
def update_posts
|
||||
@set = PostSet.find(params[:id])
|
||||
check_edit_access(@set)
|
||||
@set.update(update_posts_params)
|
||||
@set.synchronize!
|
||||
flash[:notice] = @set.valid? ? 'Set posts updated.' : @set.errors.full_messages.join('; ')
|
||||
|
||||
redirect_back(fallback_location: post_list_post_set_path(@set))
|
||||
end
|
||||
|
||||
def destroy
|
||||
@set = PostSet.find(params[:id])
|
||||
unless @set.is_owner?(CurrentUser.user) || CurrentUser.is_admin?
|
||||
raise User::PrivilegeError
|
||||
end
|
||||
@set.destroy
|
||||
respond_with(@set)
|
||||
end
|
||||
|
||||
def select
|
||||
@sets_owned = PostSet.all(order: "lower(name) ASC", conditions: ["user_id = ?", CurrentUser.id])
|
||||
@sets_maintained = SetMaintainer.all(joins: :post_set, select: "set_maintainers.*, post_sets.*", conditions: ["set_maintainers.user_id = ? AND post_sets.public = TRUE", CurrentUser.id])
|
||||
|
||||
@grouped_options = {
|
||||
"Owned" => @sets_owned.map {|x| [truncate(x.name.tr("_", " "), length: 35), x.id]},
|
||||
"Maintained" => @sets_maintained.map {|x| [truncate(x.name.tr("_", " "), length: 35), x.id]}
|
||||
}
|
||||
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def add_posts
|
||||
@set = PostSet.find(params[:set_id])
|
||||
check_edit_access(@set)
|
||||
@set.add(params[:post_ids])
|
||||
@set.save
|
||||
respond_with(@set)
|
||||
end
|
||||
|
||||
def remove_post
|
||||
@set = PostSet.find(params[:set_id])
|
||||
check_edit_access(@set)
|
||||
@set.remove(params[:post_ids])
|
||||
@set.save
|
||||
respond_with(@set)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_edit_access(set)
|
||||
unless set.is_owner?(CurrentUser.user) || set.is_maintainer?(CurrentUser.user)
|
||||
raise User::PrivilegeError
|
||||
end
|
||||
if !set.is_public && !set.is_owner?(CurrentUser.user)
|
||||
raise User::PrivilegeError
|
||||
end
|
||||
end
|
||||
|
||||
def check_view_access(set)
|
||||
unless set.is_public || set.is_owner?(CurrentUser.user) || CurrentUser.is_admin?
|
||||
raise User::PrivilegeError
|
||||
end
|
||||
end
|
||||
|
||||
def set_params
|
||||
params.require(:post_set).permit(%i[name shortname description is_public transfer_on_delete])
|
||||
end
|
||||
|
||||
def update_posts_params
|
||||
params.require(:post_set).permit([:post_ids_string])
|
||||
end
|
||||
|
||||
def search_params
|
||||
params.fetch(:search, {}).permit!
|
||||
end
|
||||
end
|
@ -105,7 +105,7 @@ private
|
||||
@error_message = post.errors.full_messages.join("; ")
|
||||
render :template => "static/error", :status => 500
|
||||
else
|
||||
response_params = {:q => params[:tags_query], :pool_id => params[:pool_id], :favgroup_id => params[:favgroup_id]}
|
||||
response_params = {:q => params[:tags_query], :pool_id => params[:pool_id], post_set_id: params[:post_set_id], favgroup_id => params[:favgroup_id]}
|
||||
response_params.reject!{|key, value| value.blank?}
|
||||
redirect_to post_path(post, response_params)
|
||||
end
|
||||
|
@ -23,7 +23,7 @@ module PostsHelper
|
||||
|
||||
def missed_post_search_count_js
|
||||
return nil unless post_search_counts_enabled?
|
||||
|
||||
|
||||
if params[:ms] == "1" && @post_set.post_count == 0 && @post_set.is_single_tag?
|
||||
session_id = session.id
|
||||
verifier = ActiveSupport::MessageVerifier.new(Danbooru.config.reportbooru_key, serializer: JSON, digest: "SHA256")
|
||||
@ -34,7 +34,7 @@ module PostsHelper
|
||||
|
||||
def post_search_count_js
|
||||
return nil unless post_search_counts_enabled?
|
||||
|
||||
|
||||
if action_name == "index" && params[:page].nil?
|
||||
tags = Tag.scan_query(params[:tags]).sort.join(" ")
|
||||
|
||||
@ -143,10 +143,17 @@ module PostsHelper
|
||||
def is_pool_selected?(pool)
|
||||
return false if params.has_key?(:q)
|
||||
return false if params.has_key?(:favgroup_id)
|
||||
return false if !params.has_key?(:pool_id)
|
||||
return false unless params.has_key?(:pool_id)
|
||||
return params[:pool_id].to_i == pool.id
|
||||
end
|
||||
|
||||
def is_post_set_selected?(post_set)
|
||||
return false if params.has_key?(:q)
|
||||
return false if params.has_key?(:pool_id)
|
||||
return false unless params.has_key?(:post_set_id)
|
||||
return params[:post_set_id].to_i == post_set.id
|
||||
end
|
||||
|
||||
def show_tag_change_notice?
|
||||
Tag.scan_query(params[:tags]).size == 1 && TagChangeNoticeService.get_forum_topic_id(params[:tags])
|
||||
end
|
||||
|
@ -70,6 +70,18 @@ module PostSets
|
||||
::Pool.find_by_name(pool_name)
|
||||
end
|
||||
|
||||
def post_set_name
|
||||
@post_set_name ||= Tag.has_metatag?(tag_array, :set)
|
||||
end
|
||||
|
||||
def has_post_set?
|
||||
is_single_tag? && post_set_name && post_set
|
||||
end
|
||||
|
||||
def post_set
|
||||
::PostSet.find_by_shortname(post_set_name)
|
||||
end
|
||||
|
||||
def favgroup_name
|
||||
@favgroup_name ||= Tag.has_metatag?(tag_array, :favgroup)
|
||||
end
|
||||
|
22
app/logical/rate_limiter.rb
Normal file
22
app/logical/rate_limiter.rb
Normal file
@ -0,0 +1,22 @@
|
||||
class RateLimiter
|
||||
def self.check_limit(key, max_attempts, lockout_time = 1.minute)
|
||||
return true if Cache.get("#{key}:lockout")
|
||||
|
||||
attempts = Cache.get(key) || 0
|
||||
if attempts >= max_attempts
|
||||
Cache.put("#{key}:lockout", true, lockout_time)
|
||||
reset_limit(key)
|
||||
end
|
||||
end
|
||||
|
||||
def self.hit(key, time_period = 1.minute)
|
||||
value = Cache.get(key) || 0
|
||||
Cache.put(key, value.to_i + 1, time_period)
|
||||
return value.to_i + 1
|
||||
end
|
||||
|
||||
def self.reset_limit(key)
|
||||
Cache.delete(key)
|
||||
end
|
||||
|
||||
end
|
@ -35,6 +35,13 @@ class ApplicationRecord < ActiveRecord::Base
|
||||
where.not("#{qualified_column_for(attr)} ~ ?", "(?e)" + value)
|
||||
end
|
||||
|
||||
def attribute_exact_matches(attribute, value, **options)
|
||||
return all unless value.present?
|
||||
|
||||
column = qualified_column_for_attribute(attribute)
|
||||
where("#{column} = ?", value)
|
||||
end
|
||||
|
||||
def attribute_matches(attribute, value, **options)
|
||||
return all if value.nil?
|
||||
|
||||
|
@ -1061,6 +1061,32 @@ class Post < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
module SetMethods
|
||||
def post_sets
|
||||
@post_sets ||= begin
|
||||
return PostSet.none if pool_string.blank?
|
||||
post_set_ids = pool_string.scan(/\d+/)
|
||||
PostSet.where(id: post_set_ids)
|
||||
end
|
||||
end
|
||||
|
||||
def add_set!(set, force = false)
|
||||
with_lock do
|
||||
self.pool_string = "#{pool_string} set:#{set.id}".strip
|
||||
update_column(:pool_string, pool_string) unless new_record?
|
||||
end
|
||||
# TODO: Add some indexing step here to trigger an index update when elasticsearch is merged
|
||||
end
|
||||
|
||||
def remove_set!(set)
|
||||
with_lock do
|
||||
self.pool_string = pool_string.gsub(/(?:\A| )set:#{set.id}(?:\Z| )/, " ").strip
|
||||
update_column(:pool_string, pool_string) unless new_record?
|
||||
end
|
||||
# TODO: Add some indexing step here to trigger an index update when elasticsearch is merged
|
||||
end
|
||||
end
|
||||
|
||||
module PoolMethods
|
||||
def pools
|
||||
@pools ||= begin
|
||||
@ -1886,6 +1912,7 @@ class Post < ApplicationRecord
|
||||
include FavoriteMethods
|
||||
include UploaderMethods
|
||||
include PoolMethods
|
||||
include SetMethods
|
||||
include VoteMethods
|
||||
extend CountMethods
|
||||
include ParentMethods
|
||||
|
310
app/models/post_set.rb
Normal file
310
app/models/post_set.rb
Normal file
@ -0,0 +1,310 @@
|
||||
# -*- encoding : utf-8 -*-
|
||||
class PostSet < ApplicationRecord
|
||||
array_attribute :post_ids, parse: /\d+/, cast: :to_i
|
||||
|
||||
has_many :post_set_maintainers, dependent: :destroy do
|
||||
def in_cooldown(user)
|
||||
where(creator_id: user.id, status: 'cooldown').where('created_at < ?', 24.hours.ago)
|
||||
end
|
||||
def active
|
||||
where(status: 'approved')
|
||||
end
|
||||
def pending
|
||||
where(status: 'pending')
|
||||
end
|
||||
def banned
|
||||
where(status: 'banned')
|
||||
end
|
||||
end
|
||||
has_many :maintainers, class_name: "User", through: :post_set_maintainers
|
||||
belongs_to_creator counter_cache: 'set_count'
|
||||
|
||||
validates_length_of :name, :shortname, in: 3..100, message: "must be between three and one hundred characters long"
|
||||
validates_uniqueness_of :name, :shortname, case_sensitive: false, message: "is already taken"
|
||||
validates_length_of :shortname, in: 1..50, message: 'must be between one and fifty characters long'
|
||||
validates_format_of :shortname, with: /\A[\w]+\z/, message: "must only contain numbers, letters, and underscores"
|
||||
validates_format_of :shortname, with: /\A\d*[a-z_][\w]*\z/, message: "must contain at least one letter or underscore"
|
||||
validates_length_of :description, maximum: 10000
|
||||
validate :validate_number_of_posts
|
||||
validate :can_make_public
|
||||
validate :set_per_hour_limit, on: :create
|
||||
validate :can_create_new_set_limit, on: :create
|
||||
|
||||
after_update :send_maintainer_public_dmails
|
||||
before_destroy :send_maintainer_destroy_dmails
|
||||
before_save :update_post_count
|
||||
|
||||
def self.name_to_id(name)
|
||||
if name =~ /^\d+$/
|
||||
name.to_i
|
||||
else
|
||||
select_value_sql("SELECT id FROM post_sets WHERE lower(shortname) = ?", name.downcase.tr(" ", "_")).to_i
|
||||
end
|
||||
end
|
||||
|
||||
def self.name_to_id_visible(name)
|
||||
|
||||
end
|
||||
|
||||
def self.visible(user = CurrentUser)
|
||||
return all() if user.is_admin?
|
||||
where('is_public = true OR creator_id = ?', user.id)
|
||||
end
|
||||
|
||||
def saved_change_to_watched_attributes?
|
||||
saved_change_to_name? || saved_change_to_shortname? || saved_change_to_description?
|
||||
end
|
||||
|
||||
module ValidationMethods
|
||||
def send_maintainer_public_dmails
|
||||
if RateLimiter.check_limit("set.public.#{id}", 1, 24.hours)
|
||||
return
|
||||
end
|
||||
if is_public_changed? && !is_public # If set was made private
|
||||
RateLimiter.hit("set.public.#{id}", 24.hours)
|
||||
PostSetMaintainer.active.where(post_set_id: id).each do |maintainer|
|
||||
Dmail.create_automated(to_id: maintainer.user_id, title: "A set you maintain was made private",
|
||||
body: "The set \"#{name}\":#{post_set_path(self)} by \"#{creator.name}\":#{user_path(creator)} that you maintain was set to private. You will not be able to view, add posts, or remove posts from the set until the owner makes it public again.")
|
||||
end
|
||||
|
||||
PostSetMaintainer.pending.where(post_set_id: id).delete
|
||||
elsif is_public_changed? && is_public # If set was made public
|
||||
RateLimiter.hit("set.public.#{id}", 24.hours)
|
||||
PostSetMaintainer.active.where(post_set_id: id).each do |maintainer|
|
||||
Dmail.create_automated(to_id: maintainer.user_id, titlet: "A private set you had maintained was made public again",
|
||||
body: "The set \"#{name}\":#{post_set_path(self)} by \"#{creaator.name}\":#{user_path(creator)} that you previously maintained was made public again. You are now able to view the set and add/remove posts.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def send_maintainer_destroy_dmails
|
||||
PostSetMaintainer.active.where(post_set_id: id).each do |maintainer|
|
||||
Dmail.create_automated(to_id: maintainer.user_id,
|
||||
title: "A set you maintain was deleted",
|
||||
body: "The set #{name} by \"#{creator.name}\":#{user_path(creator)} that you maintain was deleted.")
|
||||
end
|
||||
end
|
||||
|
||||
def can_make_public
|
||||
if is_public && creator.created_at > 3.days.ago && !creator.is_builder?
|
||||
errors.add(:base, "Can't make a set public until your account is at least three days old")
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def can_create_new_set_limit
|
||||
if PostSet.where(creator_id: creator.id).count() >= 75
|
||||
errors.add(:base, "You can only create 75 sets.")
|
||||
return false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def set_per_hour_limit
|
||||
if where("created_at > ? AND creator_id = ?", 1.hour.ago, creator.id).count() > 6 && !creator.is_builder?
|
||||
first = where("created_at > ? AND creator_id = ?", 1.hour.ago, creator.id).order(:created_at).first()
|
||||
errors.add(:base, "You have already created 6 sets in the last hour. You can make a new set in #{time_ago_in_words(first.created_at)}")
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def validate_number_of_posts
|
||||
if post_ids.size > 10_000
|
||||
errors.add(:base, "Sets can have up to 10,000 posts each")
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module AccessMethods
|
||||
def can_view?(user)
|
||||
is_public || user_id == user.id || user.is_admin?
|
||||
end
|
||||
|
||||
def can_edit?(user)
|
||||
is_owner?(user) || is_maintainer?(user)
|
||||
end
|
||||
|
||||
def is_maintainer?(user)
|
||||
return false if user.is_blocked?
|
||||
post_set_maintainers.where(user_id: user.id, status: 'approved').count() > 0
|
||||
end
|
||||
|
||||
def is_invited?(user)
|
||||
post_set_maintainers.where(user_id: user.id, status: 'pending').count() > 0
|
||||
end
|
||||
|
||||
def is_blocked?(user)
|
||||
post_set_maintainers.where(user_id: user.id, status: 'blocked').count() > 0
|
||||
end
|
||||
|
||||
def is_owner?(user)
|
||||
return false if user.is_blocked?
|
||||
creator_id == user.id || user.is_admin?
|
||||
end
|
||||
end
|
||||
|
||||
module PostMethods
|
||||
def contains?(post_id)
|
||||
post_ids.include?(post_id)
|
||||
end
|
||||
|
||||
def page_number(post_id)
|
||||
post_ids.find_index(post_id).to_i + 1
|
||||
end
|
||||
|
||||
def add(ids)
|
||||
real_ids = Post.select(:id).where(id: ids)
|
||||
real_ids.each do |post|
|
||||
next if contains?(post.id)
|
||||
self.post_ids = post_ids + [post.id]
|
||||
end
|
||||
end
|
||||
|
||||
def add!(post)
|
||||
return if contains?(post.id)
|
||||
|
||||
with_lock do
|
||||
update(post_ids: post_ids + [post.id])
|
||||
post.add_set!(self, true)
|
||||
end
|
||||
end
|
||||
|
||||
def remove(ids)
|
||||
ids.each do |id|
|
||||
next if contains?(id)
|
||||
self.post_ids = post_ids - [id]
|
||||
end
|
||||
end
|
||||
|
||||
def remove!(post)
|
||||
return unless contains?(post.id)
|
||||
|
||||
with_lock do
|
||||
update(post_ids: post_ids - [post.id])
|
||||
post.remove_set!(self)
|
||||
end
|
||||
end
|
||||
|
||||
def posts(options = {})
|
||||
offset = options[:offset] || 0
|
||||
limit = options[:limit] || Danbooru.config.posts_per_page
|
||||
slice = post_ids.slice(offset, limit)
|
||||
if slice && slice.any?
|
||||
Post.where(id: slice).sort_by {|record| slice.index {|id| id == record.id}}
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def post_count
|
||||
post_ids.size
|
||||
end
|
||||
|
||||
def first_post?(post_id)
|
||||
post_id == post_ids.first
|
||||
end
|
||||
|
||||
def last_post?(post_id)
|
||||
post_id == post_ids.last
|
||||
end
|
||||
|
||||
def previous_post_id(post_id)
|
||||
return nil if first_post?(post_id) || !contains?(post_id)
|
||||
|
||||
n = post_ids.index(post_id) - 1
|
||||
post_ids[n]
|
||||
end
|
||||
|
||||
def next_post_id(post_id)
|
||||
return nil if last_post?(post_id) || !contains?(post_id)
|
||||
|
||||
n = post_ids.index(post_id) + 1
|
||||
post_ids[n]
|
||||
end
|
||||
|
||||
def synchronize
|
||||
post_ids_before = post_ids_before_last_save || post_ids_was
|
||||
added = post_ids - post_ids_before
|
||||
removed = post_ids_before - post_ids
|
||||
|
||||
added_posts = Post.where(id: added)
|
||||
added_posts.each do |post|
|
||||
post.add_set!(self, true)
|
||||
end
|
||||
|
||||
removed_posts = Post.where(id: removed)
|
||||
removed_posts.each do |post|
|
||||
post.remove_set!(self)
|
||||
end
|
||||
|
||||
normalize_post_ids
|
||||
end
|
||||
|
||||
def synchronize!
|
||||
synchronize
|
||||
save if will_save_change_to_post_ids?
|
||||
end
|
||||
|
||||
def normalize_post_ids
|
||||
self.post_ids = post_ids.uniq
|
||||
end
|
||||
|
||||
def update_post_count
|
||||
normalize_post_ids
|
||||
self.post_count = post_ids.size
|
||||
end
|
||||
end
|
||||
|
||||
module SearchMethods
|
||||
def selected_first(current_set_id)
|
||||
return where("true") if current_set_id.blank?
|
||||
current_set_id = current_set_id.to_i
|
||||
reorder(Arel.sql("(case post_sets.id when #{current_set_id} then 0 else 1 end), post_sets.name"))
|
||||
end
|
||||
|
||||
def self.search(prams)
|
||||
q = super
|
||||
|
||||
q = q.where('is_public = true')
|
||||
q = q.or(where('(is_public = false AND creator_id = ?', creator_id)) if creator_id
|
||||
|
||||
if params[:creator_name].present?
|
||||
user = User.find_by_name(params[:username])
|
||||
q = q.where(creator_id: user)
|
||||
end
|
||||
|
||||
q = q.attribute_exact_matches(:creator_id, params[:creator_id])
|
||||
q = q.search_text_attribute(:name, params)
|
||||
q = q.attribute_exact_matches(:shortname, params[:shortname])
|
||||
|
||||
case params[:order]
|
||||
when 'name'
|
||||
q = q.order(:name, id: :desc)
|
||||
when 'shortname'
|
||||
q = q.order(:shortname, id: :desc)
|
||||
when 'postcount'
|
||||
q = q.order(post_count: :desc, id: :desc)
|
||||
when 'created_at'
|
||||
q = q.order(:id)
|
||||
when 'update'
|
||||
q = q.order(updated_at: :desc)
|
||||
else
|
||||
q = q.order(id: :desc)
|
||||
end
|
||||
|
||||
q
|
||||
end
|
||||
end
|
||||
|
||||
extend SearchMethods
|
||||
include ValidationMethods
|
||||
include AccessMethods
|
||||
include PostMethods
|
||||
end
|
149
app/models/post_set_maintainer.rb
Normal file
149
app/models/post_set_maintainer.rb
Normal file
@ -0,0 +1,149 @@
|
||||
class PostSetMaintainer < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :post_set
|
||||
|
||||
validate :ensure_not_set_owner, on: :create
|
||||
validate :ensure_set_public, on: :create
|
||||
validate :ensure_maintainer_count, on: :create
|
||||
validate :ensure_not_duplicate, on: :create
|
||||
|
||||
after_create :notify_maintainer
|
||||
|
||||
def notify_maintainer
|
||||
body = "\"#{post_set.creator.name}\":/users/#{post_set.creator_id} invited you to be a maintainer of the \"#{post_set.name}\":/post_sets/#{post_set_id} set. This would allow you to add and remove posts from it.
|
||||
|
||||
\"Click here\":/post_set_maintainers/approve/#{id} to approve the request and become a maintainer.
|
||||
|
||||
\"Click here\":/post_set_maintainers/deny/#{id} to deny the request.
|
||||
|
||||
\"Click here\":/post_set_maintainers/block/#{id} to deny the request and prevent yourself from being invited to this set again in the future."
|
||||
Dmail.create_automated(
|
||||
to_id: user_id,
|
||||
title: "You were invite to be a maintainer of #{post_set.name}",
|
||||
body: body
|
||||
)
|
||||
end
|
||||
|
||||
def notify_destroy
|
||||
|
||||
end
|
||||
|
||||
def cancel!
|
||||
if status == 'pending'
|
||||
self.status = 'cooldown'
|
||||
save
|
||||
return
|
||||
end
|
||||
|
||||
if status == 'approved'
|
||||
body = "\"#{post_set.creator.name}\":/users/#{post_set.creator_id} removed you as a maintainer of the \"#{post_set.name}\":/post_sets/#{post_set.id} set."
|
||||
Dmail.create_automated(
|
||||
to_id: user_id,
|
||||
title: "You were removed as a set maintainer of #{post_set.name}",
|
||||
body: body
|
||||
)
|
||||
end
|
||||
destroy
|
||||
end
|
||||
|
||||
def approve!
|
||||
self.status = 'approved'
|
||||
save
|
||||
Dmail.create_automated(
|
||||
to_id: post_set.creator_id,
|
||||
title: "#{user.name} approved your invite to maintain #{post_set.name}",
|
||||
body: "\"#{user.name}\":/users/#{user_id} approved your invite to maintain \"#{post_set.name}\":/post_sets/#{post_set.id}."
|
||||
)
|
||||
end
|
||||
|
||||
def deny!
|
||||
if status == "pending"
|
||||
Dmail.create_automated(
|
||||
to_id: post_set.creator_id,
|
||||
title: "#{user.name} denied your invite to maintain #{post_set.name}",
|
||||
body: "\"#{user.name}\":/users/#{user.id} denied your invite to maintain \"#{post_set.name}\":/post_sets/#{post_set.id}."
|
||||
)
|
||||
elsif status == "approved"
|
||||
Dmail.create_automated(
|
||||
to_id: post_set.creator_id,
|
||||
title: "#{user.name} removed themselves as a maintainer of #{post_set.name}",
|
||||
body: "\"#{user.name}\":/users/#{user.id} removed themselves as a maintainer of \"#{post_set.name}\":/post_sets/#{post_set.id}."
|
||||
)
|
||||
end
|
||||
destroy
|
||||
end
|
||||
|
||||
def block!
|
||||
if status == "approved"
|
||||
Dmail.create_automated(
|
||||
to_id: post_set.creator_id,
|
||||
title: "#{user.name} removed themselves as a maintainer of #{post_set.name}",
|
||||
body: "\"#{user.name}\":/users/#{user.id} removed themselves as a maintainer of \"#{post_set.name}\":/post_sets/#{post_set.id} and blocked all future invites."
|
||||
)
|
||||
elsif status == "pending"
|
||||
Dmail.create_automated(
|
||||
to_id: post_set.creator_id,
|
||||
title: "#{user.name} denied your invite to maintain #{post_set.name}",
|
||||
body: "\"#{user.name}\":/users/#{user.id} denied your invite to maintain \"#{post_set.name}\":/post_sets/#{post_set.id} and blocked all future invites."
|
||||
)
|
||||
end
|
||||
self.status = 'blocked'
|
||||
save
|
||||
end
|
||||
|
||||
module ValidaitonMethods
|
||||
def ensure_not_set_owner
|
||||
if post_set.creator_id == user_id
|
||||
errors.add(:user, "owns this set and can't be added as a maintainer")
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_maintainer_count
|
||||
if PostSetMaintainer.where(post_set_id: post_set_id).count >= 75
|
||||
errors.add(:post_set, "current have too many maintainers")
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_not_duplicate
|
||||
existing = PostSetMaintainer.where(post_set_id: post_set_id, user_id: user_id).first
|
||||
if existing.nil?
|
||||
return
|
||||
end
|
||||
if ['approved', 'pending'].include?(existing.status)
|
||||
errors.add(:base, "Already a maintainer of this set")
|
||||
return false
|
||||
end
|
||||
if existing.status == 'blocked'
|
||||
errors.add(:base, 'User has blocked you from inviting them to maintain this set')
|
||||
return false
|
||||
end
|
||||
if existing.status == 'cooldown' && existing.created_at > 24.hours.ago
|
||||
errors.add(:base, "User has been invited to maintain this set too recently")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_set_public
|
||||
unless post_set.is_public
|
||||
errors.add(:post_set, 'must be public')
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def maintained(user)
|
||||
where(user_id: user.id, status: 'approved').joins(:post_set).where('post_set.is_public = true')
|
||||
end
|
||||
|
||||
def self.active
|
||||
where(status: 'approved')
|
||||
end
|
||||
|
||||
def self.pending
|
||||
where(status: 'pending')
|
||||
end
|
||||
|
||||
include ValidaitonMethods
|
||||
end
|
@ -18,7 +18,7 @@ class Tag < ApplicationRecord
|
||||
-locked locked width height mpixels ratio score favcount filesize source
|
||||
-source id -id date age order limit -status status tagcount parent -parent
|
||||
child pixiv_id pixiv search upvote downvote filetype -filetype flagger
|
||||
-flagger appealer -appealer disapproval -disapproval
|
||||
-flagger appealer -appealer disapproval -disapproval set -set
|
||||
] + TagCategory.short_name_list.map {|x| "#{x}tags"} + COUNT_METATAGS + COUNT_METATAG_SYNONYMS
|
||||
|
||||
SUBQUERY_METATAGS = %w[commenter comm noter noteupdater artcomm flagger -flagger appealer -appealer]
|
||||
@ -657,6 +657,25 @@ class Tag < ApplicationRecord
|
||||
q[:tags][:related] << "pool:#{pool_id}"
|
||||
q[:ordpool] = pool_id
|
||||
|
||||
when "set"
|
||||
post_set_id = PostSet.name_to_id(g2)
|
||||
post_set = PostSet.find(post_set_id)
|
||||
|
||||
unless post_set.can_view?(CurrentUser.user)
|
||||
raise User::PrivilegeError
|
||||
end
|
||||
|
||||
q[:tags][:related] << "set:#{post_set_id}"
|
||||
|
||||
when "-set"
|
||||
post_set_id = PostSet.name_to_id(g2)
|
||||
post_set = PostSet.find(post_set_id)
|
||||
|
||||
unless post_set.can_view?(CurrentUser.user)
|
||||
raise User::PrivilegeError
|
||||
end
|
||||
q[:tags][:exclude] << "set:#{post_set_id}"
|
||||
|
||||
when "-favgroup"
|
||||
favgroup_id = FavoriteGroup.name_to_id(g2)
|
||||
favgroup = FavoriteGroup.find(favgroup_id)
|
||||
|
@ -36,8 +36,8 @@ class PostPresenter < Presenter
|
||||
if options[:pool_id]
|
||||
locals[:link_params]["pool_id"] = options[:pool_id]
|
||||
end
|
||||
if options[:favgroup_id]
|
||||
locals[:link_params]["favgroup_id"] = options[:favgroup_id]
|
||||
if options[:post_set_id]
|
||||
locals[:link_params]["post_set_id"] = options[:post_set_id]
|
||||
end
|
||||
|
||||
locals[:tooltip] = "#{post.tag_string} rating:#{post.rating} score:#{post.score}"
|
||||
@ -160,12 +160,12 @@ class PostPresenter < Presenter
|
||||
end
|
||||
|
||||
def has_nav_links?(template)
|
||||
has_sequential_navigation?(template.params) || @post.pools.undeleted.any? || @post.favorite_groups(active_id=template.params[:favgroup_id]).any?
|
||||
has_sequential_navigation?(template.params) || @post.pools.undeleted.any? || @post.post_sets.visible.any?
|
||||
end
|
||||
|
||||
def has_sequential_navigation?(params)
|
||||
return false if Tag.has_metatag?(params[:q], :order, :ordfav, :ordpool)
|
||||
return false if params[:pool_id].present? || params[:favgroup_id].present?
|
||||
return false if params[:pool_id].present? || params[:post_set_id].present?
|
||||
return CurrentUser.user.enable_sequential_post_navigation
|
||||
end
|
||||
end
|
||||
|
@ -8,7 +8,7 @@ module PostSetPresenters
|
||||
html = ""
|
||||
|
||||
if posts.empty?
|
||||
return template.render("post_sets/blank")
|
||||
return template.render("posts/blank")
|
||||
end
|
||||
|
||||
posts.each do |post|
|
||||
|
@ -4,7 +4,7 @@ module PostSetPresenters
|
||||
html = ""
|
||||
|
||||
if posts.empty?
|
||||
return template.render("post_sets/blank")
|
||||
return template.render("posts/blank")
|
||||
end
|
||||
|
||||
posts.each do |post|
|
||||
|
@ -16,7 +16,7 @@ module PostSetPresenters
|
||||
html = ""
|
||||
|
||||
if posts.empty?
|
||||
return template.render("post_sets/blank")
|
||||
return template.render("posts/blank")
|
||||
end
|
||||
|
||||
posts.each do |post|
|
||||
|
@ -11,7 +11,7 @@ module PostSetPresenters
|
||||
html = ""
|
||||
|
||||
if pools.empty?
|
||||
return template.render("post_sets/blank")
|
||||
return template.render("posts/blank")
|
||||
end
|
||||
|
||||
pools.each do |pool|
|
||||
|
1
app/views/post_set_maintainers/approve.js.erb
Normal file
1
app/views/post_set_maintainers/approve.js.erb
Normal file
@ -0,0 +1 @@
|
||||
location.reload();
|
1
app/views/post_set_maintainers/block.js.erb
Normal file
1
app/views/post_set_maintainers/block.js.erb
Normal file
@ -0,0 +1 @@
|
||||
location.reload();
|
1
app/views/post_set_maintainers/deny.js.erb
Normal file
1
app/views/post_set_maintainers/deny.js.erb
Normal file
@ -0,0 +1 @@
|
||||
location.reload();
|
74
app/views/post_set_maintainers/index.html.erb
Normal file
74
app/views/post_set_maintainers/index.html.erb
Normal file
@ -0,0 +1,74 @@
|
||||
<div id="c-set-maintainer">
|
||||
<div id="a-index">
|
||||
<h2>Pending Invites</h2>
|
||||
<table class="rounded nomargin" width="600px" style="margin-bottom:30px;">
|
||||
<thead>
|
||||
<th width="55%"><%= link_to "Set", action: "index", order: "set" %></th>
|
||||
<th width="20%"><%= link_to "Status", action: "index", order: "status" %></th>
|
||||
<th width="25%"></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @invites.where(status: 'pending').each do |m| %>
|
||||
<% if m.post_set.is_public %>
|
||||
<tr>
|
||||
<td><%= link_to m.post_set.name, post_set_path(m.post_set) %></td>
|
||||
<td><%= m.status.capitalize %></td>
|
||||
<td>
|
||||
<%= link_to "Accept", approve_post_set_maintainer_path(m), remote: true, method: :post, 'data-confirm': 'Are you sure you want to accept this invite?' %>
|
||||
<%= link_to "Ignore", deny_post_set_maintainer_path(m), remote: true, method: :post, 'data-confirm': 'Are you sure you want to ignore this invite?' %>
|
||||
<%= link_to "Block", block_post_set_maintainer_path(m), remote: true, method: :post, 'data-confirm': 'Aer you sure you want to ignore and block future invites for this set?' %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Approved Invites</h2>
|
||||
<table class="rounded nomargin" width="600px" style="margin-bottom:30px;">
|
||||
<thead>
|
||||
<th width="55%"><%= link_to "Set", action: "index", order: "set" %></th>
|
||||
<th width="20%"><%= link_to "Status", action: "index", order: "status" %></th>
|
||||
<th width="25%"></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @invites.where(status: 'approved').each do |m| %>
|
||||
<% if m.post_set.is_public %>
|
||||
<tr>
|
||||
<td><%= link_to m.post_set.name, post_set_path(m.post_set) %></td>
|
||||
<td><%= m.status.capitalize %></td>
|
||||
<td>
|
||||
<%= link_to "Remove", deny_post_set_maintainer_path(m), remote: true, method: :post, 'data-confirm': 'Are you sure you want to remove yourself as maintainer of this set?' %>
|
||||
<%= link_to "Block", block_post_set_maintainer_path(m), remote: true, method: :post, 'data-confirm': 'Aer you sure you want to remove yourself and block future invites for this set?' %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Blocked Invites</h2>
|
||||
<table class="rounded nomargin" width="600px">
|
||||
<thead>
|
||||
<th width="55%"><%= link_to "Set", action: "index", order: "set" %></th>
|
||||
<th width="20%"><%= link_to "Status", action: "index", order: "status" %></th>
|
||||
<th width="25%"></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @invites.where(status: 'blocked').each do |m| %>
|
||||
<% if m.post_set.is_public %>
|
||||
<tr>
|
||||
<td><%= link_to m.post_set.name, post_set_path(m.post_set) %></td>
|
||||
<td><%= m.status.capitalize %></td>
|
||||
<td>
|
||||
<%= link_to "Unblock", deny_post_set_maintainer_path(m), remote: true, method: :post, 'data-confirm': 'Are you sure you want to unblock invites for this set?' %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= render partial: "post_sets/secondary_links" %>
|
29
app/views/post_sets/_search.html.erb
Normal file
29
app/views/post_sets/_search.html.erb
Normal file
@ -0,0 +1,29 @@
|
||||
<% unless params[:show] %><div id='searchform_hide'><%= link_to_function "Show Search Options", "$j('#searchform').fadeIn('fast'); $('searchform_hide').hide();" %></div><% end %>
|
||||
<div class='section' id='searchform' style='width:400px;<% unless params[:show] %>display:none;<% end %>'>
|
||||
<% unless params[:show] %><%= link_to_function "Hide Search Options", "$j('#searchform').fadeOut('fast'); $('searchform_hide').show();" %><% end %>
|
||||
<% form_tag({action: "index"}, method: :get) do %>
|
||||
<table class='nomargin'>
|
||||
<tr><td><label for="name">Name</label></td> <td><%= text_field_tag "name", params[:name], style: "width:195px"%></td></tr>
|
||||
<tr><td><label for="name">Short Name</label></td> <td><%= text_field_tag "shortname", params[:shortname], style: "width:195px"%></td></tr>
|
||||
<tr><td><label for="name">Username</label></td> <td><%= text_field_tag "username", params[:username], style: "width:195px"%></td></tr>
|
||||
|
||||
<tr><td><label for="type">Order</label></td> <td>
|
||||
<%= select_tag "order", options_for_select([
|
||||
["Name", "name"],
|
||||
["Short Name", "shortname"],
|
||||
["Creator", "creator"],
|
||||
["Post Count", "postcount"],
|
||||
["Created", "created"],
|
||||
["Updated", "updated"]
|
||||
], params[:order]), style: "width:200px;" %>
|
||||
</td></tr>
|
||||
<tr><td colspan="2"><%= submit_tag "Search", name: nil %></td></tr>
|
||||
</table>
|
||||
<% if params[:show] %>
|
||||
<input type='hidden' name='show' value='1'/>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% if params[:name] || params[:type] || params[:status] %>
|
||||
<script type='text/javascript'>$('searchform_hide').hide(); $('searchform').show();</script>
|
||||
<% end %>
|
29
app/views/post_sets/_secondary_links.html.erb
Normal file
29
app/views/post_sets/_secondary_links.html.erb
Normal file
@ -0,0 +1,29 @@
|
||||
<% content_for(:secondary_links) do %>
|
||||
<menu>
|
||||
<%= subnav_link_to "List", post_sets_path %>
|
||||
<%= subnav_link_to "New", new_post_set_path %>
|
||||
<%= subnav_link_to "Help", wiki_pages_path(title: 'help:sets') %>
|
||||
<% if CurrentUser.is_member? %>
|
||||
<%= subnav_link_to "Mine", post_sets_path(search: {creator_id: CurrentUser.id}) %>
|
||||
<%= subnav_link_to "Invites", post_set_maintainers_path %>
|
||||
<% end %>
|
||||
<% if @set %>
|
||||
<li>|</li>
|
||||
<%= subnav_link_to "Posts", posts_path(tags: "set:#{@set.shortname}") %>
|
||||
<%= subnav_link_to "Maintainers", action: "maintainers", id: params[:id] %>
|
||||
|
||||
<% if @set.is_owner?(CurrentUser.user) || CurrentUser.is_admin? %>
|
||||
<%= subnav_link_to "Edit", edit_post_set_path(@set) %>
|
||||
<% end %>
|
||||
|
||||
<% if @set.is_owner?(CurrentUser.user) || @set.is_maintainer?(CurrentUser.user) %>
|
||||
<%= subnav_link_to "Edit Posts", post_list_post_set_path(@set) %>
|
||||
<% end %>
|
||||
|
||||
<% if @set.is_owner?(CurrentUser.user) || CurrentUser.is_admin? %>
|
||||
<%= subnav_link_to "Delete", post_set_path(@set), method: 'delete', data: {confirm: 'Are you sure you want to delete this set?'} %>
|
||||
<% end %>
|
||||
<%= subnav_link_to "Report", new_ticket_path(type: 'set', disp_id: @set.id) %>
|
||||
<% end %>
|
||||
</menu>
|
||||
<% end %>
|
30
app/views/post_sets/atom.html.erb
Normal file
30
app/views/post_sets/atom.html.erb
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<title><%= Danbooru.config.app_name %> - Sets</title>
|
||||
<link href="<%= atom_post_sets_url() %>" rel="self"/>
|
||||
<link href="<%= post_sets_url() %>>" rel="alternate"/>
|
||||
<id><%= atom_post_sets_url() %></id>
|
||||
<% if @sets.any? %>
|
||||
<updated><%= @sets[0].created_at.gmtime.xmlschema %></updated>
|
||||
<% end %>
|
||||
<author><name><%= Danbooru.config.app_name %></name></author>
|
||||
|
||||
<% @sets.each do |set| %>
|
||||
<entry>
|
||||
<title><%= set.name %></title>
|
||||
<link href="<%= post_set_url(set) %>" rel="alternate"/>
|
||||
<id><%= post_set_url(set) %></id>
|
||||
<updated><%= set.created_at.gmtime.xmlschema %></updated>
|
||||
<summary><%= Nokogiri::HTML(format_text set.description).text %></summary>
|
||||
<content type="xhtml">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml">
|
||||
<%= format_text set.description %>
|
||||
</div>
|
||||
</content>
|
||||
<author>
|
||||
<name><%= set.creator.pretty_name %></name>
|
||||
</author>
|
||||
</entry>
|
||||
<% end %>
|
||||
</feed>
|
10
app/views/post_sets/destroy.html.erb
Normal file
10
app/views/post_sets/destroy.html.erb
Normal file
@ -0,0 +1,10 @@
|
||||
<h3>Delete Set</h3>
|
||||
|
||||
<div class='section' style='width:500px;'>
|
||||
<% form_tag(action: "destroy") do %>
|
||||
<p>Are you sure you wish to delete "<%= h(@set.name) %>"?</p>
|
||||
<%= submit_tag "Yes" %> <%= button_to_function "No", "history.back()" %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%= render partial: "footer" %>
|
38
app/views/post_sets/edit.html.erb
Normal file
38
app/views/post_sets/edit.html.erb
Normal file
@ -0,0 +1,38 @@
|
||||
<div id="a-set">
|
||||
<div id="c-edit">
|
||||
<h2>Editing <span class="set-name"><%= @set.name %></span></h2>
|
||||
|
||||
<% if @can_edit %>
|
||||
<div class='section'>
|
||||
<%= simple_form_for(@set) do |f| %>
|
||||
<%= f.input :name, as: :string, input_html: {onkeyup: 'fillShortName()'} %>
|
||||
|
||||
<%= f.input :shortname, label: 'Short Name', as: :string, input_html: {onchange: 'shortNameChanged()'},
|
||||
hint: "The short name is used for the set's metatag name. Can only contain letters, numbers, and underscores
|
||||
and must contain at least one letter or underscore. <a href=\"/post?tags=set%3Aexample\">set:example</a>".html_safe %>
|
||||
<%= dtext_field "post_set", "description", :classes => "autocomplete-mentions", :input_id => "set_description", :preview_id => "dtext-preview" %>
|
||||
|
||||
<%= f.input :is_public, label: 'Public', hint: "Private sets are only visible to you. Public sets are visible to anyone, but only you and users you
|
||||
assign as maintainers can edit the set. Only accounts three days or older can make public sets." %>
|
||||
|
||||
<%= f.input :transfer_on_delete, label: 'Transfer on Deletion', hint: 'If "Transfer on Delete" is enabled, when a post is deleted from the site, its parent (if any) will be
|
||||
added to this set in its place. Disable if you want posts to simply be removed from this set with no
|
||||
replacement.' %>
|
||||
<%= f.button :submit, "Update" %>
|
||||
<%= dtext_preview_button :post_set, :body, :input_id => "set_description", :preview_id => "dtext-preview" %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if @set.is_owner?(CurrentUser) %>
|
||||
<%= link_to "» Click here to edit maintainers.".html_safe, maintainers_post_set_path(@set) %><br/>
|
||||
<% end %>
|
||||
|
||||
<% if @set.is_owner?(CurrentUser) || @set.is_maintainer?(CurrentUser) %>
|
||||
<%= link_to "» Click here to add/remove posts.".html_safe, post_list_post_set_path(@set) %><br/>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<%= render partial: "secondary_links" %>
|
60
app/views/post_sets/index.html.erb
Normal file
60
app/views/post_sets/index.html.erb
Normal file
@ -0,0 +1,60 @@
|
||||
<div id="c-sets">
|
||||
<div id="a-index">
|
||||
<%= render partial: 'search' %>
|
||||
|
||||
<h2>Sets</h2>
|
||||
|
||||
<div id="set-index">
|
||||
<table width="100%" class="rounded">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="30%"><%= link_to "Name", action: "index", order: "username" %></th>
|
||||
<th width="20%"><%= link_to "Short Name", action: "index", order: "shortname" %></th>
|
||||
<th width="15%">Creator</th>
|
||||
<th width="5%"><%= link_to "Posts", action: "index", order: "postcount" %></th>
|
||||
<th width="10%"><%= link_to "Created", action: "index", order: "created" %></th>
|
||||
<th width="10%"><%= link_to "Updated", action: "index", order: "updated" %></th>
|
||||
<th width="10%">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% @sets.each do |s| %>
|
||||
<tr>
|
||||
<td><%= link_to s.name, post_set_path(s) %></td>
|
||||
<td><%= link_to s.shortname, posts_path(tags: "set:#{s.shortname}") %></td>
|
||||
<td><%= link_to_user s.creator %></td>
|
||||
<td><%= s.post_count %></td>
|
||||
<td><span class="date" title="<%= s.created_at.strftime("%b %d, %Y %I:%M %p") %>"><%= time_ago_in_words(s.created_at) + " ago" %></span></td>
|
||||
<td><span class="date" title="<%= s.updated_at.strftime("%b %d, %Y %I:%M %p") %>"><%= time_ago_in_words(s.updated_at) + " ago" %></span></td>
|
||||
<td>
|
||||
<% if s.is_public %>
|
||||
<div class='set-status set-status-public' title='This set is public'>Public</div>
|
||||
<% else %>
|
||||
<div class='set-status set-status-private' title='This set is private and only visible to you'>Private</div>
|
||||
<% end %>
|
||||
|
||||
<% if s.is_owner?(CurrentUser.user) %>
|
||||
<div class='set-status set-status-owner' title='You own this set'>Owner</div>
|
||||
<% elsif s.is_maintainer?(CurrentUser.user) %>
|
||||
<div class='set-status set-status-maintainer' title='You are a maintainer of this set and can add and remove posts. Click to view invites'><%= link_to "Maint.", controller: "post_set_maintainers", action: "index" %></div>
|
||||
<% elsif s.is_invited?(CurrentUser.user) %>
|
||||
<div class='set-status set-status-invited' title='You have been invited to maintain this set. Click to view invites'><%= link_to "Invited", controller: "post_set_maintainers", action: "index" %></div>
|
||||
<% elsif s.is_blocked?(CurrentUser.user) %>
|
||||
<div class='set-status set-status-blocked' title='You have blocked the owner of this set from inviting you to maintain it. Click to view invites'><%= link_to "Blocked", controller: "post_set_maintainers", action: "index" %></div>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="paginator">
|
||||
<%= numbered_paginator(@sets) %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<%= render partial: "secondary_links" %>
|
74
app/views/post_sets/maintainers.html.erb
Normal file
74
app/views/post_sets/maintainers.html.erb
Normal file
@ -0,0 +1,74 @@
|
||||
<div id="c-sets">
|
||||
<div id="a-maintainers">
|
||||
<% @maintainers = @set.post_set_maintainers %>
|
||||
<h2>Maintainers for set <span class="set-name"><%= @set.name %></span></h2>
|
||||
<p>Maintainers are users who are assigned to a set and have the ability to add or remove posts from it. They cannot
|
||||
edit any other details of the set.</p>
|
||||
<h3>Approved</h3>
|
||||
<table class="rounded nomargin" width="400px" style="margin-bottom:30px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="55%"><%= link_to "Username", action: "maintainers", id: @set.id, order: "username" %></th>
|
||||
<th width="30%"><%= link_to "Status", action: "maintainers", id: @set.id, order: "status" %></th>
|
||||
<th width="15%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @maintainers.active.each do |m| %>
|
||||
<tr>
|
||||
<td><%= link_to_user m.user %></td>
|
||||
<td><%= m.status.capitalize %></td>
|
||||
|
||||
<td>
|
||||
<% if @set.is_owner?(CurrentUser) %>
|
||||
<%= form_tag({controller: "set_maintainer", action: "destroy"}) do %>
|
||||
<%= hidden_field_tag "id", m.id %>
|
||||
<%= submit_tag "Remove" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Pending</h3>
|
||||
<table class="rounded nomargin" width="400px">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="55%"><%= link_to "Username", action: "maintainers", id: @set.id, order: "username" %></th>
|
||||
<th width="30%"><%= link_to "Status", action: "maintainers", id: @set.id, order: "status" %></th>
|
||||
<th width="15%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @maintainers.pending.each do |m| %>
|
||||
<tr>
|
||||
<td><%= link_to_user m.user %></td>
|
||||
<td><%= m.status.capitalize %></td>
|
||||
|
||||
<td>
|
||||
<% if @set.is_owner?(CurrentUser) %>
|
||||
<%= link_to "Remove", post_set_maintainer_path(m), method: :delete, remote: true, data: {confirm: "Are you sure you want to remove this pending invite?"} %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<% if @set.is_owner?(CurrentUser) && @set.is_public %>
|
||||
<h3 style="margin-top:15px;">Add new Maintainer</h3>
|
||||
<div class='section' style="width:380px;">
|
||||
<%= form_tag(post_set_maintainers_path) do %>
|
||||
<label for="set_maintainer_name">Username</label><br/>
|
||||
<%= hidden_field_tag "post_set_id", @set.id %>
|
||||
<%= text_field_tag "username" %>
|
||||
<%= submit_tag "Create" %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= render partial: "secondary_links" %>
|
31
app/views/post_sets/new.html.erb
Normal file
31
app/views/post_sets/new.html.erb
Normal file
@ -0,0 +1,31 @@
|
||||
<div id="c-sets">
|
||||
<div id="a-new">
|
||||
<h2>New Set</h2>
|
||||
|
||||
<div>
|
||||
<%= simple_form_for(@set) do |f| %>
|
||||
<%= error_messages_for :post_set %>
|
||||
<%= f.input :name, as: :string, input_html: {onkeyup: 'fillShortName()'} %>
|
||||
|
||||
<%= f.input :shortname, label: 'Short Name', as: :string, input_html: {onchange: 'shortNameChanged()'},
|
||||
hint: "The short name is used for the set's metatag name. Can only contain letters, numbers, and underscores
|
||||
and must contain at least one letter or underscore. <a href=\"/post?tags=set%3Aexample\">set:example</a>".html_safe %>
|
||||
<%= dtext_field "post_set", "description", :classes => "autocomplete-mentions", :input_id => "set_description", :preview_id => "dtext-preview" %>
|
||||
|
||||
<%= f.input :is_public, label: 'Public', hint: "Private sets are only visible to you. Public sets are visible to anyone, but only you and users you
|
||||
assign as maintainers can edit the set. Only accounts three days or older can make public sets." %>
|
||||
|
||||
<%= f.input :transfer_on_delete, label: 'Transfer on Deletion', hint: 'If "Transfer on Delete" is enabled, when a post is deleted from the site, its parent (if any) will be
|
||||
added to this set in its place. Disable if you want posts to simply be removed from this set with no
|
||||
replacement.' %>
|
||||
|
||||
<p>You can add posts to the set and assign maintainers on the next page.</p>
|
||||
|
||||
<%= f.button :submit, "Create" %>
|
||||
<%= dtext_preview_button :post_set, :body, :input_id => "set_description", :preview_id => "dtext-preview" %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= render partial: "secondary_links" %>
|
12
app/views/post_sets/post_list.erb
Normal file
12
app/views/post_sets/post_list.erb
Normal file
@ -0,0 +1,12 @@
|
||||
<div id="c-sets">
|
||||
<div id="a-posts">
|
||||
<h2>Post list for set <span class="set-name"><%= @set.name %></span></h2>
|
||||
|
||||
<%= simple_form_for(@set, url: update_posts_post_set_path(@set), method: :post) do |f| %>
|
||||
<%= f.input :post_ids_string, as: :text, label: 'Post IDs' %>
|
||||
<%= f.button :submit, "Update" %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= render partial: "secondary_links" %>
|
7
app/views/post_sets/select.html.erb
Normal file
7
app/views/post_sets/select.html.erb
Normal file
@ -0,0 +1,7 @@
|
||||
<% form_tag(action: "add_post") do %>
|
||||
<%= hidden_field_tag "post_id", params[:post_id] %>
|
||||
<%= select_tag "set_id", unsorted_grouped_options_for_select(@grouped_options, session[:last_set_id]) %>
|
||||
<% if !params[:mode] %>
|
||||
<%= button_to_function "Add", "PostSet.add_post(#{params[:post_id]}, jQuery('#set_id').val())" %>
|
||||
<% end %>
|
||||
<% end %>
|
79
app/views/post_sets/show.html.erb
Normal file
79
app/views/post_sets/show.html.erb
Normal file
@ -0,0 +1,79 @@
|
||||
<div id="c-sets">
|
||||
<div id="a-show">
|
||||
<div class="set-header"><span class="set-name"><%= @set.name %></span> by
|
||||
<span class="set-creator"><%= link_to_user @set.creator %></span>
|
||||
<% if @set.is_public %>
|
||||
<div class='set-status set-status-public' title='This set is public'>Public</div>
|
||||
<% else %>
|
||||
<div class='set-status set-status-private' title='This set is private and only visible to you'>Private</div>
|
||||
<% end %>
|
||||
|
||||
<% if @set.is_owner?(CurrentUser.user) %>
|
||||
<div class='set-status set-status-owner' title='You own this set'>Owner</div>
|
||||
<% elsif @set.is_maintainer?(CurrentUser.user) %>
|
||||
<div class='set-status set-status-maintainer' title='You are a maintainer of this set and can add and remove posts'>Maint.</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="set-shortname">Short
|
||||
Name: <%= link_to @set.shortname, posts_path(tags: "set:#{@set.shortname}") %></div>
|
||||
Created:
|
||||
<span class="date" title="<%= @set.created_at.strftime("%b %d, %Y %I:%M %p") %>"><%= time_ago_in_words(@set.created_at) + " ago" %></span>
|
||||
|
|
||||
Updated:
|
||||
<span class="date" title="<%= @set.updated_at.strftime("%b %d, %Y %I:%M %p") %>"><%= time_ago_in_words(@set.updated_at) + " ago" %></span><br/>
|
||||
|
||||
<% if @set.description.blank? %>
|
||||
<div class='set-description'>No description.</div>
|
||||
<% else %>
|
||||
<div class='set-description'><%= format_text @set.description %></div>
|
||||
<% end %>
|
||||
<div class='set-description-bottom'></div>
|
||||
|
||||
<span class="set-viewposts"><%= link_to "» View Posts (#{@set.post_count.to_s})".html_safe, posts_path(tags: "set:#{@set.shortname}") %></span><br/>
|
||||
<%#= link_to "» Maintainers (" + PostSetMaintainer.count(conditions: ["post_set_id = ?", @set.id]).to_s + ")", action: "maintainers", id: @set.id %>
|
||||
<br/>
|
||||
|
||||
<% if @set.post_count == 0 %>
|
||||
<div class="set-empty section">
|
||||
<p>This set has no posts in it.</p>
|
||||
|
||||
<% if @set.is_owner?(CurrentUser.user) || @set.is_maintainer?(CurrentUser.user) %>
|
||||
To start adding posts to this set:
|
||||
<ul>
|
||||
<li>On a post's page, click <strong>Add to Set</strong> under <strong>Options</strong> in the sidebar,
|
||||
select a set, and click <strong>Add</strong></li>
|
||||
<li>When viewing the <%= link_to "post index", posts_path %>, select
|
||||
<strong>Add to set...</strong> from the mode dropdown under the search bar, select a set from the new
|
||||
dropdown, and click a post thumbnail to add it to the set.
|
||||
</li>
|
||||
</ul>
|
||||
<% end %>
|
||||
</div>
|
||||
<% elsif @set.is_owner?(CurrentUser.user) || @set.is_maintainer?(CurrentUser.user) %>
|
||||
<div class="section">
|
||||
To add posts to this set:
|
||||
<ul>
|
||||
<li>On a post's page, click <strong>Add to Set</strong> under <strong>Options</strong> in the sidebar, select
|
||||
a set, and click <strong>Add</strong></li>
|
||||
<li>When viewing the <%= link_to "post index", posts_path %>, select
|
||||
<strong>Add to set...</strong> from the mode dropdown under the search bar, select a set from the new
|
||||
dropdown, and click a post thumbnail to add it to the set.
|
||||
</li>
|
||||
</ul>
|
||||
To remove posts from this set:
|
||||
<ul>
|
||||
<li>When viewing the <%= link_to "post index", posts_path %>, select
|
||||
<strong>Remove from set...</strong> from the mode dropdown under the search bar, select a set from the new
|
||||
dropdown, and click a post thumbnail to remove it to the set.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% content_for(:secondary_links) do %>
|
||||
|
||||
<% end %>
|
||||
|
||||
<%= render partial: "secondary_links" %>
|
@ -5,7 +5,7 @@
|
||||
<p>Recent updates may not have been processed yet. The most recently processed version was <%= time_ago_in_words_tagged(PostArchive.maximum(:updated_at)) %>.</p>
|
||||
|
||||
<% if @post_versions.length == 0 %>
|
||||
<%= render "post_sets/blank" %>
|
||||
<%= render "posts/blank" %>
|
||||
<% else %>
|
||||
<% if params.dig(:search, :post_id).present? %>
|
||||
<%= render "revert_listing" %>
|
||||
|
@ -5,10 +5,14 @@
|
||||
<% end %>
|
||||
|
||||
<% if post.pools.undeleted.any? %>
|
||||
<%= render "posts/partials/show/pool_list", post: post, pools: post.pools.undeleted.selected_first(params[:pool_id]) %>
|
||||
<%= render "posts/partials/show/pool_list", post: post, pools: post.pools.undeleted.selected_first(params[:pool_id]).limit(5) %>
|
||||
<% end %>
|
||||
|
||||
<% if post.favorite_groups(active_id=params[:favgroup_id]).any? %>
|
||||
<% if post.post_sets.visible.any? %>
|
||||
<%= render "posts/partials/show/post_set_list", post: post, post_sets: post.post_sets.visible.selected_first(params[:post_set_id]).limit(5) %>
|
||||
<% end %>
|
||||
|
||||
<% if post.favorite_groups(active_id = params[:favgroup_id]).any? %>
|
||||
<%= render "posts/partials/show/favorite_groups", :post => post %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
7
app/views/posts/partials/show/_post_set_list.html.erb
Normal file
7
app/views/posts/partials/show/_post_set_list.html.erb
Normal file
@ -0,0 +1,7 @@
|
||||
<div id="set-nav">
|
||||
<ul>
|
||||
<% post_sets.each do |set| %>
|
||||
<%= render "posts/partials/show/post_set_list_item", post_set: set, post: post, selected: is_post_set_selected?(set) %>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
33
app/views/posts/partials/show/_post_set_list_item.html.erb
Normal file
33
app/views/posts/partials/show/_post_set_list_item.html.erb
Normal file
@ -0,0 +1,33 @@
|
||||
<%= content_tag :li, id: "nav-link-for-set-#{post_set.id}", class: "set-selected-#{selected}" do -%>
|
||||
<% if !post_set.first_post?(post.id) && post_set.post_ids.first -%>
|
||||
<%= link_to("«".html_safe, post_path(post_set.post_ids.first, post_set_id: post_set.id), class: "first", title: "to first") %>
|
||||
<% else -%>
|
||||
<span class="first">«</span>
|
||||
<% end -%>
|
||||
|
||||
<% post_set.previous_post_id(post.id).tap do |previous_post_id| -%>
|
||||
<% if previous_post_id %>
|
||||
<%= link_to "‹ prev".html_safe, post_path(previous_post_id, post_set_id: post_set.id), rel: selected ? "prev" : nil, class: "prev", title: "to page #{post_set.page_number(previous_post_id)}" -%>
|
||||
<% else -%>
|
||||
<span class="prev">‹ prev</span>
|
||||
<% end %>
|
||||
<% end -%>
|
||||
|
||||
<span class="set-name">
|
||||
<%= link_to("Set: #{post_set.name}", post_set_path(post_set), title: "page #{post_set.page_number(post.id)}/#{post_set.post_count}") -%>
|
||||
</span>
|
||||
|
||||
<% post_set.next_post_id(post.id).tap do |next_post_id| -%>
|
||||
<% if next_post_id %>
|
||||
<%= link_to("next ›".html_safe, post_path(next_post_id, post_set_id: post_set.id), rel: selected ? "next" : nil, class: "next", title: "to page #{post_set.page_number(next_post_id)}") -%>
|
||||
<% else -%>
|
||||
<span class="next">next ›</span>
|
||||
<% end -%>
|
||||
<% end -%>
|
||||
|
||||
<% if !post_set.last_post?(post.id) && post_set.post_ids.last -%>
|
||||
<%= link_to("»".html_safe, post_path(post_set.post_ids.last, post_set_id: post_set.id), class: "last", title: "to page #{post_set.post_count}") -%>
|
||||
<% else -%>
|
||||
<span class="last">»</span>
|
||||
<% end -%>
|
||||
<% end -%>
|
@ -360,6 +360,25 @@ Rails.application.routes.draw do
|
||||
end
|
||||
end
|
||||
resources :post_report_reasons
|
||||
resources :post_sets do
|
||||
collection do
|
||||
get :atom
|
||||
end
|
||||
member do
|
||||
get :maintainers
|
||||
get :post_list
|
||||
post :update_posts
|
||||
post :add_posts
|
||||
post :remove_posts
|
||||
end
|
||||
end
|
||||
resources :post_set_maintainers do
|
||||
member do
|
||||
post :approve
|
||||
post :block
|
||||
post :deny
|
||||
end
|
||||
end
|
||||
|
||||
# aliases
|
||||
resources :wpages, :controller => "wiki_pages"
|
||||
|
25
db/migrate/20190317024446_create_post_sets.rb
Normal file
25
db/migrate/20190317024446_create_post_sets.rb
Normal file
@ -0,0 +1,25 @@
|
||||
class CreatePostSets < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :post_sets do |t|
|
||||
t.string :name, null: false
|
||||
t.string :shortname, null: false
|
||||
t.text :description, default: ''
|
||||
t.boolean :is_public, null: false, default: false
|
||||
t.boolean :transfer_on_delete, null: false, default: false
|
||||
t.integer :creator_id, null: false
|
||||
t.column :creator_ip_addr, 'inet', null: true
|
||||
t.column :post_ids, 'integer[]', null: false, default: '{}'
|
||||
t.integer :post_count, null: false, default: 0
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
create_table :post_set_maintainers do |t|
|
||||
t.integer :post_set_id, null: false
|
||||
t.integer :user_id, null: false
|
||||
t.string :status, null: false, default: 'pending'
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_column :users, :set_count, :integer, null: false, default: 0
|
||||
end
|
||||
end
|
108
db/structure.sql
108
db/structure.sql
@ -1528,6 +1528,78 @@ CREATE SEQUENCE public.post_report_reasons_id_seq
|
||||
ALTER SEQUENCE public.post_report_reasons_id_seq OWNED BY public.post_report_reasons.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_set_maintainers; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE public.post_set_maintainers (
|
||||
id bigint NOT NULL,
|
||||
post_set_id integer NOT NULL,
|
||||
user_id integer NOT NULL,
|
||||
status character varying DEFAULT 'pending'::character varying NOT NULL,
|
||||
created_at timestamp without time zone NOT NULL,
|
||||
updated_at timestamp without time zone NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_set_maintainers_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.post_set_maintainers_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_set_maintainers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE public.post_set_maintainers_id_seq OWNED BY public.post_set_maintainers.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_sets; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE public.post_sets (
|
||||
id bigint NOT NULL,
|
||||
name character varying NOT NULL,
|
||||
shortname character varying NOT NULL,
|
||||
description text DEFAULT ''::text,
|
||||
is_public boolean DEFAULT false NOT NULL,
|
||||
transfer_on_delete boolean DEFAULT false NOT NULL,
|
||||
creator_id integer NOT NULL,
|
||||
creator_ip_addr inet,
|
||||
post_ids integer[] DEFAULT '{}'::integer[] NOT NULL,
|
||||
post_count integer DEFAULT 0 NOT NULL,
|
||||
created_at timestamp without time zone NOT NULL,
|
||||
updated_at timestamp without time zone NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_sets_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.post_sets_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_sets_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE public.post_sets_id_seq OWNED BY public.post_sets.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_updates; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
@ -2208,7 +2280,8 @@ furry -rating:s'::text,
|
||||
custom_style text,
|
||||
bit_prefs bigint DEFAULT 0 NOT NULL,
|
||||
last_ip_addr inet,
|
||||
unread_dmail_count integer DEFAULT 0 NOT NULL
|
||||
unread_dmail_count integer DEFAULT 0 NOT NULL,
|
||||
set_count integer DEFAULT 0 NOT NULL
|
||||
);
|
||||
|
||||
|
||||
@ -2583,6 +2656,20 @@ ALTER TABLE ONLY public.post_replacements ALTER COLUMN id SET DEFAULT nextval('p
|
||||
ALTER TABLE ONLY public.post_report_reasons ALTER COLUMN id SET DEFAULT nextval('public.post_report_reasons_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_set_maintainers id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.post_set_maintainers ALTER COLUMN id SET DEFAULT nextval('public.post_set_maintainers_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_sets id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.post_sets ALTER COLUMN id SET DEFAULT nextval('public.post_sets_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_votes id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
@ -3029,6 +3116,22 @@ ALTER TABLE ONLY public.post_report_reasons
|
||||
ADD CONSTRAINT post_report_reasons_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_set_maintainers post_set_maintainers_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.post_set_maintainers
|
||||
ADD CONSTRAINT post_set_maintainers_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_sets post_sets_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.post_sets
|
||||
ADD CONSTRAINT post_sets_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_votes post_votes_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
@ -4485,6 +4588,7 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20190222082952'),
|
||||
('20190228144206'),
|
||||
('20190305165101'),
|
||||
('20190313221440');
|
||||
('20190313221440'),
|
||||
('20190317024446');
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user