Merge branch 'master' into fix-mention-dmails
This commit is contained in:
@@ -9,6 +9,10 @@ div.list-of-forum-posts {
|
||||
margin-bottom: 3em;
|
||||
word-wrap: break-word;
|
||||
|
||||
&:target {
|
||||
background-color: #FFC;
|
||||
}
|
||||
|
||||
div.author {
|
||||
width: 12em;
|
||||
float: left;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
module Admin
|
||||
class UsersController < ApplicationController
|
||||
before_filter :moderator_only
|
||||
rescue_from User::PrivilegeError, :with => :access_denied
|
||||
|
||||
def edit
|
||||
@user = User.find(params[:id])
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
class CommentsController < ApplicationController
|
||||
respond_to :html, :xml, :json
|
||||
before_filter :member_only, :except => [:index, :search, :show]
|
||||
rescue_from ActiveRecord::StatementInvalid, :with => :rescue_exception
|
||||
skip_before_filter :api_check
|
||||
|
||||
def index
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
class DmailsController < ApplicationController
|
||||
respond_to :html, :xml, :json
|
||||
before_filter :member_only
|
||||
rescue_from User::PrivilegeError, :with => :access_denied
|
||||
|
||||
def new
|
||||
if params[:respond_to_id]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
class LegacyController < ApplicationController
|
||||
before_filter :member_only, :only => [:create_post]
|
||||
rescue_from PostSets::SearchError, :with => :rescue_exception
|
||||
|
||||
def posts
|
||||
@post_set = PostSets::Post.new(tag_query, params[:page], params[:limit], format: "json")
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
module Maintenance
|
||||
module User
|
||||
class DeletionsController < ApplicationController
|
||||
rescue_from UserDeletion::ValidationError, :with => :rescue_exception
|
||||
|
||||
def show
|
||||
end
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ module Moderator
|
||||
class PostsController < ApplicationController
|
||||
before_filter :approver_only, :only => [:delete, :undelete, :move_favorites, :ban, :unban, :confirm_delete, :confirm_move_favorites, :confirm_ban]
|
||||
before_filter :admin_only, :only => [:expunge]
|
||||
rescue_from ::PostFlag::Error, ::Post::ApprovalError, :with => :rescue_exception
|
||||
skip_before_filter :api_check
|
||||
|
||||
def confirm_delete
|
||||
|
||||
@@ -15,7 +15,7 @@ module Moderator
|
||||
end
|
||||
|
||||
::Post.without_timeout do
|
||||
@posts = ::Post.order("posts.id asc").pending_or_flagged.available_for_moderation(params[:hidden]).tag_match(params[:query]).paginate(params[:page], :limit => per_page)
|
||||
@posts = ::Post.includes(:disapprovals, :uploader).order("posts.id asc").pending_or_flagged.available_for_moderation(params[:hidden]).tag_match(params[:query]).paginate(params[:page], :limit => per_page)
|
||||
@posts.each # hack to force rails to eager load
|
||||
end
|
||||
respond_with(@posts)
|
||||
@@ -25,7 +25,7 @@ module Moderator
|
||||
cookies.permanent[:moderated] = Time.now.to_i
|
||||
|
||||
::Post.without_timeout do
|
||||
@posts = ::Post.order("posts.id asc").pending_or_flagged.available_for_moderation(false).reorder("random()").limit(RANDOM_COUNT)
|
||||
@posts = ::Post.includes(:disapprovals, :uploader).order("posts.id asc").pending_or_flagged.available_for_moderation(false).reorder("random()").limit(RANDOM_COUNT)
|
||||
@posts.each # hack to force rails to eager load
|
||||
|
||||
if @posts.empty?
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
class PostVersionsController < ApplicationController
|
||||
respond_to :html, :xml, :json
|
||||
rescue_from ActiveRecord::StatementInvalid, :with => :rescue_exception
|
||||
|
||||
def index
|
||||
@post_versions = PostVersion.search(params[:search]).order("updated_at desc, id desc").paginate(params[:page], :limit => params[:limit], :search_count => params[:search])
|
||||
@post_versions = PostArchive.search(params[:search]).order("updated_at desc, id desc").paginate(params[:page], :limit => params[:limit], :search_count => params[:search])
|
||||
respond_with(@post_versions) do |format|
|
||||
format.xml do
|
||||
render :xml => @post_versions.to_xml(:root => "post-versions")
|
||||
@@ -15,7 +14,7 @@ class PostVersionsController < ApplicationController
|
||||
end
|
||||
|
||||
def undo
|
||||
@post_version = PostVersion.find(params[:id])
|
||||
@post_version = PostArchive.find(params[:id])
|
||||
|
||||
if @post_version.post.visible?
|
||||
@post_version.undo!
|
||||
|
||||
@@ -3,10 +3,6 @@ class PostsController < ApplicationController
|
||||
before_filter :builder_only, :only => [:copy_notes]
|
||||
before_filter :enable_cors, :only => [:index, :show]
|
||||
respond_to :html, :xml, :json
|
||||
rescue_from PostSets::SearchError, :with => :rescue_exception
|
||||
rescue_from Post::SearchError, :with => :rescue_exception
|
||||
rescue_from ActiveRecord::StatementInvalid, :with => :rescue_exception
|
||||
rescue_from ActiveRecord::RecordNotFound, :with => :rescue_exception
|
||||
|
||||
def index
|
||||
if params[:md5].present?
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
class UploadsController < ApplicationController
|
||||
before_filter :member_only
|
||||
respond_to :html, :xml, :json, :js
|
||||
rescue_from Upload::Error, :with => :rescue_exception
|
||||
|
||||
def new
|
||||
@upload = Upload.new
|
||||
|
||||
@@ -2,7 +2,6 @@ class UserNameChangeRequestsController < ApplicationController
|
||||
before_filter :member_only, :only => [:index, :show]
|
||||
before_filter :gold_only, :only => [:new, :create]
|
||||
before_filter :admin_only, :only => [:approve, :reject]
|
||||
rescue_from User::PrivilegeError, :with => :access_denied
|
||||
respond_to :html, :json, :xml
|
||||
|
||||
def new
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
class UsersController < ApplicationController
|
||||
respond_to :html, :xml, :json
|
||||
before_filter :member_only, :only => [:edit, :update, :upgrade]
|
||||
rescue_from User::PrivilegeError, :with => :access_denied
|
||||
skip_before_filter :api_check
|
||||
|
||||
def new
|
||||
|
||||
@@ -3,8 +3,6 @@ class WikiPagesController < ApplicationController
|
||||
before_filter :member_only, :except => [:index, :show, :show_or_new]
|
||||
before_filter :builder_only, :only => [:destroy]
|
||||
before_filter :normalize_search_params, :only => [:index]
|
||||
rescue_from ActiveRecord::StatementInvalid, :with => :rescue_exception
|
||||
rescue_from ActiveRecord::RecordNotFound, :with => :rescue_exception
|
||||
|
||||
def new
|
||||
@wiki_page = WikiPage.new(params[:wiki_page])
|
||||
|
||||
@@ -25,7 +25,7 @@ class ApproverPruner
|
||||
Dmail.create_automated(
|
||||
:to_id => user.id,
|
||||
:title => "Approver inactivity",
|
||||
:body => "You haven't approved a post in the past three months. In order to make sure the list of active approvers is up-to-date, you have lost your approver privileges. Please reply to this message if you want to be reinstated."
|
||||
:body => "You haven't approved a post in the past three months. In order to make sure the list of active approvers is up-to-date, you have lost your approver privileges."
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -27,7 +27,7 @@ class BulkRevert
|
||||
end
|
||||
|
||||
def find_post_versions
|
||||
q = PostVersion.where("true")
|
||||
q = PostArchive.where("true")
|
||||
|
||||
if constraints[:user_name]
|
||||
constraints[:user_id] = User.find_by_name(constraints[:user_name]).try(:id)
|
||||
|
||||
@@ -34,7 +34,7 @@ module Moderator
|
||||
add_row(sums, Hash[User.where(last_ip_addr: ip_addrs).collect { |user| [user, 1] }])
|
||||
|
||||
add_row_id(sums, PoolArchive.where(updater_ip_addr: ip_addrs).group(:updater_id).count) if PoolArchive.enabled?
|
||||
add_row_id(sums, PostVersion.where(updater_ip_addr: ip_addrs).group(:updater_id).count)
|
||||
add_row_id(sums, PostArchive.where(updater_ip_addr: ip_addrs).group(:updater_id).count) if PostArchive.enabled?
|
||||
|
||||
sums
|
||||
end
|
||||
@@ -52,7 +52,7 @@ module Moderator
|
||||
add_row(sums, ArtistVersion.where(updater: users).group(:updater_ip_addr).count)
|
||||
add_row(sums, NoteVersion.where(updater: users).group(:updater_ip_addr).count)
|
||||
add_row(sums, PoolArchive.where(updater_id: users.map(&:id)).group(:updater_ip_addr).count) if PoolArchive.enabled?
|
||||
add_row(sums, PostVersion.where(updater_id: users.map(&:id)).group(:updater_ip_addr).count)
|
||||
add_row(sums, PostArchive.where(updater_id: users.map(&:id)).group(:updater_ip_addr).count) if PostArchive.enabled?
|
||||
add_row(sums, WikiPageVersion.where(updater: users).group(:updater_ip_addr).count)
|
||||
add_row(sums, Comment.where(creator: users).group(:ip_addr).count)
|
||||
add_row(sums, Dmail.where(from: users).group(:creator_ip_addr).count)
|
||||
|
||||
@@ -13,7 +13,7 @@ module Reports
|
||||
end
|
||||
|
||||
def mock_version(row)
|
||||
PostVersion.new.tap do |x|
|
||||
PostArchive.new.tap do |x|
|
||||
x.id = row["f"][0]["v"]
|
||||
x.post_id = row["f"][1]["v"]
|
||||
x.updated_at = Time.at(row["f"][2]["v"].to_f)
|
||||
|
||||
10
app/logical/user_name_validator.rb
Normal file
10
app/logical/user_name_validator.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
class UserNameValidator < ActiveModel::EachValidator
|
||||
def validate_each(rec, attr, value)
|
||||
name = User.normalize_name(value)
|
||||
|
||||
rec.errors[attr] << "already exists" if User.find_by_name(name).present?
|
||||
rec.errors[attr] << "must be 2 to 100 characters long" if !name.length.between?(2, 100)
|
||||
rec.errors[attr] << "cannot have whitespace or colons" if name =~ /[[:space:]]|:/
|
||||
rec.errors[attr] << "cannot begin or end with an underscore" if name =~ /\A_|_\z/
|
||||
end
|
||||
end
|
||||
@@ -15,13 +15,13 @@ class UserRevert
|
||||
end
|
||||
|
||||
def validate!
|
||||
if PostVersion.where(updater_id: user_id).count > THRESHOLD
|
||||
if PostArchive.where(updater_id: user_id).count > THRESHOLD
|
||||
raise TooManyChangesError.new("This user has too many changes to be reverted")
|
||||
end
|
||||
end
|
||||
|
||||
def revert_post_changes
|
||||
PostVersion.where(updater_id: user_id).find_each do |x|
|
||||
PostArchive.where(updater_id: user_id).find_each do |x|
|
||||
x.undo!
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,6 +12,112 @@ class AmazonBackup < ActiveRecord::Base
|
||||
first.update_column(:last_id, new_id)
|
||||
end
|
||||
|
||||
def self.restore_from_glacier(min_id, max_id)
|
||||
credentials = Aws::Credentials.new(Danbooru.config.aws_access_key_id, Danbooru.config.aws_secret_access_key)
|
||||
Aws.config.update({
|
||||
region: "us-east-1",
|
||||
credentials: credentials
|
||||
})
|
||||
client = Aws::S3::Client.new
|
||||
bucket = Danbooru.config.aws_s3_bucket_name
|
||||
|
||||
f = lambda do |key|
|
||||
begin
|
||||
client.restore_object(
|
||||
bucket: bucket,
|
||||
key: key,
|
||||
restore_request: {
|
||||
days: 7,
|
||||
glacier_job_parameters: {
|
||||
tier: "Bulk"
|
||||
}
|
||||
}
|
||||
)
|
||||
rescue Aws::S3::Errors::InternalError
|
||||
puts " internal error...retrying"
|
||||
sleep 30
|
||||
retry
|
||||
rescue Aws::S3::Errors::InvalidObjectState
|
||||
puts " already restored #{key}"
|
||||
rescue Aws::S3::Errors::NoSuchKey
|
||||
puts " missing #{key}"
|
||||
file_path = "/var/www/danbooru2/shared/data/#{key}"
|
||||
|
||||
if File.exists?(file_path)
|
||||
base64_md5 = Digest::MD5.base64digest(File.read(file_path))
|
||||
body = open(file_path, "rb")
|
||||
client.put_object(bucket: bucket, key: key, body: body, content_md5: base64_md5, acl: "public-read")
|
||||
puts " uploaded"
|
||||
end
|
||||
rescue Aws::S3::Errors::RestoreAlreadyInProgress
|
||||
puts " already restoring #{key}"
|
||||
end
|
||||
end
|
||||
|
||||
Post.where("id >= ? and id <= ?", min_id, max_id).find_each do |post|
|
||||
if post.has_large?
|
||||
puts "large:#{post.id}"
|
||||
key = "sample/" + File.basename(post.large_file_path)
|
||||
f.call(key)
|
||||
end
|
||||
|
||||
if post.has_preview?
|
||||
puts "preview:#{post.id}"
|
||||
key = "preview/" + File.basename(post.preview_file_path)
|
||||
f.call(key)
|
||||
end
|
||||
|
||||
puts "#{post.id}"
|
||||
key = File.basename(post.file_path)
|
||||
f.call(key)
|
||||
end
|
||||
end
|
||||
|
||||
def self.copy_to_standard(min_id, max_id)
|
||||
credentials = Aws::Credentials.new(Danbooru.config.aws_access_key_id, Danbooru.config.aws_secret_access_key)
|
||||
Aws.config.update({
|
||||
region: "us-east-1",
|
||||
credentials: credentials
|
||||
})
|
||||
client = Aws::S3::Client.new
|
||||
bucket = Danbooru.config.aws_s3_bucket_name
|
||||
|
||||
f = lambda do |key|
|
||||
begin
|
||||
client.copy_object(bucket: bucket, key: key, acl: "public-read", storage_class: "STANDARD", copy_source: "/#{bucket}/#{key}", metadata_directive: "COPY")
|
||||
puts " copied #{key}"
|
||||
rescue Aws::S3::Errors::InternalError
|
||||
puts " internal error...retrying"
|
||||
sleep 30
|
||||
retry
|
||||
rescue Aws::S3::Errors::InvalidObjectState
|
||||
puts " invalid state #{key}"
|
||||
rescue Aws::S3::Errors::NoSuchKey
|
||||
puts " missing #{key}"
|
||||
end
|
||||
end
|
||||
|
||||
Post.where("id >= ? and id <= ?", min_id, max_id).find_each do |post|
|
||||
next unless post.has_preview?
|
||||
|
||||
if post.has_preview?
|
||||
puts "preview:#{post.id}"
|
||||
key = "preview/" + File.basename(post.preview_file_path)
|
||||
f.call(key)
|
||||
end
|
||||
|
||||
if post.has_large?
|
||||
puts "large:#{post.id}"
|
||||
key = "sample/" + File.basename(post.large_file_path)
|
||||
f.call(key)
|
||||
end
|
||||
|
||||
puts "#{post.id}"
|
||||
key = File.basename(post.file_path)
|
||||
f.call(key)
|
||||
end
|
||||
end
|
||||
|
||||
def self.execute
|
||||
return false unless Danbooru.config.aws_s3_enabled?
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ class Comment < ActiveRecord::Base
|
||||
after_update(:if => lambda {|rec| CurrentUser.id != rec.creator_id}) do |rec|
|
||||
ModAction.log("comment ##{rec.id} updated by #{CurrentUser.name}")
|
||||
end
|
||||
after_destroy :update_last_commented_at_on_destroy
|
||||
after_destroy(:if => lambda {|rec| CurrentUser.id != rec.creator_id}) do |rec|
|
||||
after_update :update_last_commented_at_on_destroy, :if => lambda {|rec| rec.is_deleted? && rec.is_deleted_changed?}
|
||||
after_update(:if => lambda {|rec| rec.is_deleted? && rec.is_deleted_changed? && CurrentUser.id != rec.creator_id}) do |rec|
|
||||
ModAction.log("comment ##{rec.id} deleted by #{CurrentUser.name}")
|
||||
end
|
||||
attr_accessible :body, :post_id, :do_not_bump_post, :is_deleted, :as => [:member, :gold, :platinum, :builder, :janitor, :moderator, :admin]
|
||||
|
||||
@@ -154,6 +154,10 @@ class ForumTopic < ActiveRecord::Base
|
||||
self.updater_id = CurrentUser.id
|
||||
end
|
||||
|
||||
def page_for(post_id)
|
||||
(posts.where("id < ?", post_id).count / Danbooru.config.posts_per_page.to_f).ceil
|
||||
end
|
||||
|
||||
def last_page
|
||||
(response_count / Danbooru.config.posts_per_page.to_f).ceil
|
||||
end
|
||||
|
||||
@@ -71,7 +71,7 @@ class JanitorTrial < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def send_dmail
|
||||
body = "You have been selected as a test janitor. You can now approve pending posts and have access to the moderation interface. You should reacquaint yourself with the [[howto:upload]] guide to make sure you understand the site rules.\n\nOver the next several weeks your approvals will be monitored. If the majority of them are not quality uploads you will fail the trial period and lose your approval privileges. You will also receive a negative user record indicating you previously attempted and failed a test janitor trial.\n\nThere is a minimum quota of 1 approval a month to indicate that you are being active. Remember, the goal isn't to approve as much as possible. It's to filter out borderline-quality art.\n\nIf you have any questions please respond to this message."
|
||||
body = "You have been selected as a test janitor. You can now approve pending posts and have access to the moderation interface. You should reacquaint yourself with the [[howto:upload]] guide to make sure you understand the site rules.\n\nOver the next several weeks your approvals will be monitored. If the majority of them are not quality uploads you will fail the trial period and lose your approval privileges. You will also receive a negative user record indicating you previously attempted and failed a test janitor trial.\n\nThere is a minimum quota of 1 approval a month to indicate that you are being active. Remember, the goal isn't to approve as much as possible. It's to filter out borderline-quality art."
|
||||
|
||||
Dmail.create_automated(:title => "Test Janitor Trial Period", :body => body, :to_id => user_id)
|
||||
end
|
||||
|
||||
@@ -42,7 +42,6 @@ class Post < ActiveRecord::Base
|
||||
has_one :pixiv_ugoira_frame_data, :class_name => "PixivUgoiraFrameData", :dependent => :destroy
|
||||
has_many :flags, :class_name => "PostFlag", :dependent => :destroy
|
||||
has_many :appeals, :class_name => "PostAppeal", :dependent => :destroy
|
||||
has_many :versions, lambda {order("post_versions.updated_at ASC, post_versions.id ASC")}, :class_name => "PostVersion", :dependent => :destroy
|
||||
has_many :votes, :class_name => "PostVote", :dependent => :destroy
|
||||
has_many :notes, :dependent => :destroy
|
||||
has_many :comments, lambda {includes(:creator, :updater).order("comments.id")}, :dependent => :destroy
|
||||
@@ -1414,12 +1413,16 @@ class Post < ActiveRecord::Base
|
||||
end
|
||||
|
||||
module VersionMethods
|
||||
def versions
|
||||
if PostArchive.enabled?
|
||||
PostArchive.where(post_id: id).order("updated_at ASC, id asc")
|
||||
else
|
||||
raise "Archive service not configured"
|
||||
end
|
||||
end
|
||||
|
||||
def create_version(force = false)
|
||||
if new_record? || rating_changed? || source_changed? || parent_id_changed? || tag_string_changed? || force
|
||||
if merge_version?
|
||||
delete_previous_version
|
||||
end
|
||||
|
||||
create_new_version
|
||||
end
|
||||
end
|
||||
@@ -1431,19 +1434,7 @@ class Post < ActiveRecord::Base
|
||||
|
||||
def create_new_version
|
||||
User.where(id: CurrentUser.id).update_all("post_update_count = post_update_count + 1")
|
||||
CurrentUser.reload
|
||||
|
||||
versions.create(
|
||||
:rating => rating,
|
||||
:source => source,
|
||||
:tags => tag_string,
|
||||
:parent_id => parent_id
|
||||
)
|
||||
end
|
||||
|
||||
def delete_previous_version
|
||||
prev = versions.last
|
||||
prev.destroy
|
||||
PostArchive.queue(self) if PostArchive.enabled?
|
||||
end
|
||||
|
||||
def revert_to(target)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
class PostArchive < ActiveRecord::Base
|
||||
extend Memoist
|
||||
|
||||
def self.enabled?
|
||||
Danbooru.config.aws_sqs_archives_url.present?
|
||||
end
|
||||
@@ -6,49 +8,285 @@ class PostArchive < ActiveRecord::Base
|
||||
establish_connection (ENV["ARCHIVE_DATABASE_URL"] || "archive_#{Rails.env}".to_sym) if enabled?
|
||||
self.table_name = "post_versions"
|
||||
|
||||
def self.calculate_version(post_id, updated_at, version_id)
|
||||
if updated_at.to_i == Time.zone.parse("2007-03-14T19:38:12Z").to_i
|
||||
# Old post versions which didn't have updated_at set correctly
|
||||
1 + PostVersion.where("post_id = ? and updated_at = ? and id < ?", post_id, updated_at, version_id).count
|
||||
else
|
||||
1 + PostVersion.where("post_id = ? and updated_at < ?", post_id, updated_at).count
|
||||
end
|
||||
end
|
||||
|
||||
def self.export(version_id = 9096768)
|
||||
PostVersion.where("id > ?", version_id).find_each do |version|
|
||||
previous = version.previous
|
||||
tags = version.tags.scan(/\S+/)
|
||||
|
||||
if previous
|
||||
prev_tags = previous.tags.scan(/\S+/)
|
||||
added_tags = tags - previous.tags.scan(/\S+/)
|
||||
removed_tags = previous.tags.scan(/\S+/) - tags
|
||||
module SearchMethods
|
||||
def for_user(user_id)
|
||||
if user_id
|
||||
where("updater_id = ?", user_id)
|
||||
else
|
||||
added_tags = tags
|
||||
removed_tags = []
|
||||
where("false")
|
||||
end
|
||||
end
|
||||
|
||||
def for_user_name(name)
|
||||
user_id = User.name_to_id(name)
|
||||
for_user(user_id)
|
||||
end
|
||||
|
||||
def search(params)
|
||||
q = where("true")
|
||||
params = {} if params.blank?
|
||||
|
||||
if params[:updater_name].present?
|
||||
q = q.for_user_name(params[:updater_name])
|
||||
end
|
||||
|
||||
rating_changed = previous.nil? || version.rating != previous.try(:rating)
|
||||
parent_changed = previous.nil? || version.parent_id != previous.try(:parent_id)
|
||||
source_changed = previous.nil? || version.source != previous.try(:source)
|
||||
create(
|
||||
post_id: version.post_id,
|
||||
tags: version.tags,
|
||||
added_tags: added_tags,
|
||||
removed_tags: removed_tags,
|
||||
updater_id: version.updater_id,
|
||||
updater_ip_addr: version.updater_ip_addr.to_s,
|
||||
updated_at: version.updated_at,
|
||||
version: calculate_version(version.post_id, version.updated_at, version.id),
|
||||
rating: version.rating,
|
||||
rating_changed: rating_changed,
|
||||
parent_id: version.parent_id,
|
||||
parent_changed: parent_changed,
|
||||
source: version.source,
|
||||
source_changed: source_changed
|
||||
)
|
||||
puts "inserted #{version.id}"
|
||||
if params[:updater_id].present?
|
||||
q = q.where("updater_id = ?", params[:updater_id].to_i)
|
||||
end
|
||||
|
||||
if params[:post_id].present?
|
||||
q = q.where("post_id = ?", params[:post_id].to_i)
|
||||
end
|
||||
|
||||
if params[:start_id].present?
|
||||
q = q.where("id <= ?", params[:start_id].to_i)
|
||||
end
|
||||
|
||||
q
|
||||
end
|
||||
end
|
||||
|
||||
module ArchiveServiceMethods
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
def sqs_service
|
||||
SqsService.new(Danbooru.config.aws_sqs_archives_url)
|
||||
end
|
||||
|
||||
def queue(post)
|
||||
# queue updates to sqs so that if archives goes down for whatever reason it won't
|
||||
# block post updates
|
||||
raise "Archive service is not configured" if !enabled?
|
||||
|
||||
json = {
|
||||
"post_id" => post.id,
|
||||
"rating" => post.rating,
|
||||
"parent_id" => post.parent_id,
|
||||
"source" => post.source,
|
||||
"updater_id" => CurrentUser.id,
|
||||
"updater_ip_addr" => CurrentUser.ip_addr.to_s,
|
||||
"updated_at" => post.updated_at.try(:iso8601),
|
||||
"created_at" => post.created_at.try(:iso8601),
|
||||
"tags" => post.tag_string
|
||||
}
|
||||
msg = "add post version\n#{json.to_json}"
|
||||
sqs_service.send_message(msg)
|
||||
end
|
||||
|
||||
def export_to_archives(version_id = 4394763)
|
||||
PostVersion.where("id > ?", version_id).find_each do |version|
|
||||
previous = version.previous
|
||||
tags = version.tags.scan(/\S+/)
|
||||
version_number = if version.updated_at.to_i == Time.zone.parse("2007-03-14T19:38:12Z").to_i
|
||||
# Old post versions which didn't have updated_at set correctly
|
||||
1 + PostVersion.where("post_id = ? and updated_at = ? and id < ?", version.post_id, version.updated_at, version.id).count
|
||||
else
|
||||
1 + PostVersion.where("post_id = ? and updated_at < ?", version.post_id, version.updated_at).count
|
||||
end
|
||||
|
||||
if previous
|
||||
prev_tags = previous.tags.scan(/\S+/)
|
||||
added_tags = tags - prev_tags
|
||||
removed_tags = prev_tags - tags
|
||||
else
|
||||
added_tags = tags
|
||||
removed_tags = []
|
||||
end
|
||||
|
||||
rating_changed = previous.nil? || version.rating != previous.rating
|
||||
parent_changed = previous.nil? || version.parent_id != previous.parent_id
|
||||
source_changed = previous.nil? || version.source != previous.source
|
||||
create(
|
||||
post_id: version.post_id,
|
||||
tags: version.tags,
|
||||
added_tags: added_tags,
|
||||
removed_tags: removed_tags,
|
||||
updater_id: version.updater_id,
|
||||
updater_ip_addr: version.updater_ip_addr.to_s,
|
||||
updated_at: version.updated_at,
|
||||
version: version_number,
|
||||
rating: version.rating,
|
||||
rating_changed: rating_changed,
|
||||
parent_id: version.parent_id,
|
||||
parent_changed: parent_changed,
|
||||
source: version.source,
|
||||
source_changed: source_changed
|
||||
)
|
||||
puts "inserted #{version.id}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
extend SearchMethods
|
||||
include ArchiveServiceMethods
|
||||
|
||||
def tag_array
|
||||
tags.scan(/\S+/)
|
||||
end
|
||||
|
||||
def presenter
|
||||
PostVersionPresenter.new(self)
|
||||
end
|
||||
|
||||
def reload
|
||||
flush_cache
|
||||
super
|
||||
end
|
||||
|
||||
def post
|
||||
Post.where(id: post_id).first
|
||||
end
|
||||
|
||||
def previous
|
||||
PostArchive.where("post_id = ? and version < ?", post_id, version).order("version desc").first
|
||||
end
|
||||
|
||||
def updater
|
||||
User.find(updater_id)
|
||||
end
|
||||
|
||||
def diff(version = nil)
|
||||
if post.nil?
|
||||
latest_tags = tag_array
|
||||
else
|
||||
latest_tags = post.tag_array
|
||||
latest_tags << "rating:#{post.rating}" if post.rating.present?
|
||||
latest_tags << "parent:#{post.parent_id}" if post.parent_id.present?
|
||||
latest_tags << "source:#{post.source}" if post.source.present?
|
||||
end
|
||||
|
||||
new_tags = tag_array
|
||||
new_tags << "rating:#{rating}" if rating.present?
|
||||
new_tags << "parent:#{parent_id}" if parent_id.present?
|
||||
new_tags << "source:#{source}" if source.present?
|
||||
|
||||
old_tags = version.present? ? version.tag_array : []
|
||||
if version.present?
|
||||
old_tags << "rating:#{version.rating}" if version.rating.present?
|
||||
old_tags << "parent:#{version.parent_id}" if version.parent_id.present?
|
||||
old_tags << "source:#{version.source}" if version.source.present?
|
||||
end
|
||||
|
||||
added_tags = new_tags - old_tags
|
||||
removed_tags = old_tags - new_tags
|
||||
|
||||
return {
|
||||
:added_tags => added_tags,
|
||||
:removed_tags => removed_tags,
|
||||
:obsolete_added_tags => added_tags - latest_tags,
|
||||
:obsolete_removed_tags => removed_tags & latest_tags,
|
||||
:unchanged_tags => new_tags & old_tags,
|
||||
}
|
||||
end
|
||||
|
||||
def changes
|
||||
delta = {
|
||||
:added_tags => added_tags,
|
||||
:removed_tags => removed_tags
|
||||
}
|
||||
|
||||
latest_tags = post.tag_array
|
||||
latest_tags << "rating:#{post.rating}" if post.rating.present?
|
||||
latest_tags << "parent:#{post.parent_id}" if post.parent_id.present?
|
||||
latest_tags << "source:#{post.source}" if post.source.present?
|
||||
|
||||
if parent_changed
|
||||
delta[:added_tags] << "parent:#{parent_id}"
|
||||
|
||||
if previous
|
||||
delta[:removed_tags] << "parent:#{previous.parent_id}"
|
||||
end
|
||||
end
|
||||
|
||||
if rating_changed
|
||||
delta[:added_tags] << "rating:#{rating}"
|
||||
|
||||
if previous
|
||||
delta[:removed_tags] << "rating:#{previous.rating}"
|
||||
end
|
||||
end
|
||||
|
||||
if source_changed
|
||||
delta[:added_tags] << "source:#{source}"
|
||||
|
||||
if previous
|
||||
delta[:removed_tags] << "source:#{previous.source}"
|
||||
end
|
||||
end
|
||||
|
||||
delta[:obsolete_added_tags] = delta[:added_tags] - latest_tags
|
||||
delta[:obsolete_removed_tags] = delta[:removed_tags] & latest_tags
|
||||
|
||||
if previous
|
||||
delta[:unchanged_tags] = tag_array & previous.tag_array
|
||||
else
|
||||
delta[:unchanged_tags] = []
|
||||
end
|
||||
|
||||
delta
|
||||
end
|
||||
|
||||
def added_tags_with_fields
|
||||
changes[:added_tags].join(" ")
|
||||
end
|
||||
|
||||
def removed_tags_with_fields
|
||||
changes[:removed_tags].join(" ")
|
||||
end
|
||||
|
||||
def obsolete_added_tags
|
||||
changes[:obsolete_added_tags].join(" ")
|
||||
end
|
||||
|
||||
def obsolete_removed_tags
|
||||
changes[:obsolete_removed_tags].join(" ")
|
||||
end
|
||||
|
||||
def unchanged_tags
|
||||
changes[:unchanged_tags].join(" ")
|
||||
end
|
||||
|
||||
def truncated_source
|
||||
source.gsub(/^http:\/\//, "").sub(/\/.+/, "")
|
||||
end
|
||||
|
||||
def undo
|
||||
added = changes[:added_tags_with_fields] - changes[:obsolete_added_tags]
|
||||
removed = changes[:removed_tags_with_fields] - changes[:obsolete_removed_tags]
|
||||
|
||||
added.each do |tag|
|
||||
if tag =~ /^source:/
|
||||
post.source = ""
|
||||
elsif tag =~ /^parent:/
|
||||
post.parent_id = nil
|
||||
else
|
||||
escaped_tag = Regexp.escape(tag)
|
||||
post.tag_string = post.tag_string.sub(/(?:\A| )#{escaped_tag}(?:\Z| )/, " ").strip
|
||||
end
|
||||
end
|
||||
removed.each do |tag|
|
||||
if tag =~ /^source:(.+)$/
|
||||
post.source = $1
|
||||
else
|
||||
post.tag_string = "#{post.tag_string} #{tag}".strip
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def undo!
|
||||
undo
|
||||
post.save!
|
||||
end
|
||||
|
||||
def updater
|
||||
User.find_by_id(updater_id)
|
||||
end
|
||||
|
||||
def method_attributes
|
||||
super + [:obsolete_added_tags, :obsolete_removed_tags, :unchanged_tags, :updater_name]
|
||||
end
|
||||
|
||||
memoize :previous, :post, :tag_array, :changes, :added_tags_with_fields, :removed_tags_with_fields, :obsolete_removed_tags, :obsolete_added_tags, :unchanged_tags
|
||||
end
|
||||
|
||||
@@ -61,17 +61,6 @@ class PostVersion < ActiveRecord::Base
|
||||
super
|
||||
end
|
||||
|
||||
def sequence_for_post
|
||||
versions = PostVersion.where(:post_id => post_id).order("updated_at desc, id desc")
|
||||
diffs = []
|
||||
versions.each_index do |i|
|
||||
if i < versions.size - 1
|
||||
diffs << versions[i].diff(versions[i + 1])
|
||||
end
|
||||
end
|
||||
return diffs
|
||||
end
|
||||
|
||||
def diff(version)
|
||||
latest_tags = post.tag_array
|
||||
latest_tags << "rating:#{post.rating}" if post.rating.present?
|
||||
|
||||
@@ -54,10 +54,8 @@ class User < ActiveRecord::Base
|
||||
attr_accessor :password, :old_password
|
||||
attr_accessible :dmail_filter_attributes, :enable_privacy_mode, :enable_post_navigation, :new_post_navigation_layout, :password, :old_password, :password_confirmation, :password_hash, :email, :last_logged_in_at, :last_forum_read_at, :has_mail, :receive_email_notifications, :comment_threshold, :always_resize_images, :favorite_tags, :blacklisted_tags, :name, :ip_addr, :time_zone, :default_image_size, :enable_sequential_post_navigation, :per_page, :hide_deleted_posts, :style_usernames, :enable_auto_complete, :custom_style, :show_deleted_children, :disable_categorized_saved_searches, :disable_tagged_filenames, :enable_recent_searches, :as => [:moderator, :janitor, :gold, :platinum, :member, :anonymous, :default, :builder, :admin]
|
||||
attr_accessible :level, :as => :admin
|
||||
validates_length_of :name, :within => 2..100, :on => :create
|
||||
validates_format_of :name, :with => /\A[^\s:]+\Z/, :on => :create, :message => "cannot have whitespace or colons"
|
||||
validates_format_of :name, :with => /\A[^_].*[^_]\Z/, :on => :create, :message => "cannot begin or end with an underscore"
|
||||
validates_uniqueness_of :name, :case_sensitive => false
|
||||
|
||||
validates :name, user_name: true, on: :create
|
||||
validates_uniqueness_of :email, :case_sensitive => false, :if => lambda {|rec| rec.email.present? && rec.email_changed? }
|
||||
validates_length_of :password, :minimum => 5, :if => lambda {|rec| rec.new_record? || rec.password.present?}
|
||||
validates_inclusion_of :default_image_size, :in => %w(large original)
|
||||
@@ -153,6 +151,10 @@ class User < ActiveRecord::Base
|
||||
def id_to_pretty_name(user_id)
|
||||
id_to_name(user_id).gsub(/([^_])_+(?=[^_])/, "\\1 \\2")
|
||||
end
|
||||
|
||||
def normalize_name(name)
|
||||
name.to_s.mb_chars.downcase.strip.tr(" ", "_").to_s
|
||||
end
|
||||
end
|
||||
|
||||
def pretty_name
|
||||
|
||||
@@ -3,11 +3,8 @@ class UserNameChangeRequest < ActiveRecord::Base
|
||||
validates_inclusion_of :status, :in => %w(pending approved rejected)
|
||||
belongs_to :user
|
||||
belongs_to :approver, :class_name => "User"
|
||||
validate :uniqueness_of_desired_name
|
||||
validate :not_limited, :on => :create
|
||||
validates_length_of :desired_name, :within => 2..100, :on => :create
|
||||
validates_format_of :desired_name, :with => /\A[^\s:]+\Z/, :on => :create, :message => "cannot have whitespace or colons"
|
||||
before_validation :normalize_name
|
||||
validates :desired_name, user_name: true
|
||||
attr_accessible :status, :user_id, :original_name, :desired_name, :change_reason, :rejection_reason, :approver_id
|
||||
|
||||
def self.pending
|
||||
@@ -40,8 +37,8 @@ class UserNameChangeRequest < ActiveRecord::Base
|
||||
status == "pending"
|
||||
end
|
||||
|
||||
def normalize_name
|
||||
self.desired_name = desired_name.strip.gsub(/ /, "_")
|
||||
def desired_name=(name)
|
||||
super(User.normalize_name(name))
|
||||
end
|
||||
|
||||
def feedback
|
||||
@@ -71,15 +68,6 @@ class UserNameChangeRequest < ActiveRecord::Base
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
def uniqueness_of_desired_name
|
||||
if User.find_by_name(desired_name)
|
||||
errors.add(:desired_name, "already exists")
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
def hidden_attributes
|
||||
if CurrentUser.is_admin? || user == CurrentUser.user
|
||||
|
||||
@@ -163,6 +163,7 @@ class WikiPage < ActiveRecord::Base
|
||||
:title => title,
|
||||
:body => body,
|
||||
:is_locked => is_locked,
|
||||
:is_deleted => is_deleted,
|
||||
:other_names => other_names
|
||||
)
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@ class WikiPageVersion < ActiveRecord::Base
|
||||
belongs_to :wiki_page
|
||||
belongs_to :updater, :class_name => "User"
|
||||
belongs_to :artist
|
||||
attr_accessible :wiki_page_id, :title, :body, :is_locked, :updater_id, :updater_ip_addr, :version, :other_names
|
||||
attr_accessible :wiki_page_id, :title, :body, :is_locked, :is_deleted, :updater_id, :updater_ip_addr, :version, :other_names
|
||||
delegate :visible?, :to => :wiki_page
|
||||
|
||||
module SearchMethods
|
||||
|
||||
@@ -95,6 +95,10 @@ class PostPresenter < Presenter
|
||||
@post = post
|
||||
end
|
||||
|
||||
def tag_set_presenter
|
||||
@tag_set_presenter ||= TagSetPresenter.new(@post.tag_array)
|
||||
end
|
||||
|
||||
def preview_html
|
||||
PostPresenter.preview(@post)
|
||||
end
|
||||
@@ -170,13 +174,15 @@ class PostPresenter < Presenter
|
||||
end
|
||||
|
||||
def tag_list_html(template, options = {})
|
||||
@tag_set_presenter ||= TagSetPresenter.new(@post.tag_array)
|
||||
@tag_set_presenter.tag_list_html(template, options.merge(:show_extra_links => CurrentUser.user.is_gold?))
|
||||
tag_set_presenter.tag_list_html(template, options.merge(:show_extra_links => CurrentUser.user.is_gold?))
|
||||
end
|
||||
|
||||
def split_tag_list_html(template, options = {})
|
||||
@tag_set_presenter ||= TagSetPresenter.new(@post.tag_array)
|
||||
@tag_set_presenter.split_tag_list_html(template, options.merge(:show_extra_links => CurrentUser.user.is_gold?))
|
||||
tag_set_presenter.split_tag_list_html(template, options.merge(:show_extra_links => CurrentUser.user.is_gold?))
|
||||
end
|
||||
|
||||
def inline_tag_list_html(template)
|
||||
tag_set_presenter.inline_tag_list(template)
|
||||
end
|
||||
|
||||
def has_nav_links?(template)
|
||||
|
||||
@@ -64,6 +64,17 @@ class TagSetPresenter < Presenter
|
||||
html.html_safe
|
||||
end
|
||||
|
||||
# compact (horizontal) list, as seen in the /comments index.
|
||||
def inline_tag_list(template)
|
||||
@tags.map do |tag_name|
|
||||
<<-EOS
|
||||
<span class="category-#{Tag.category_for(tag_name)}">
|
||||
#{template.link_to(tag_name.tr("_", " "), template.posts_path(tags: tag_name))}
|
||||
</span>
|
||||
EOS
|
||||
end.join.html_safe
|
||||
end
|
||||
|
||||
private
|
||||
def general_tags
|
||||
@general_tags ||= categories.select {|k, v| v == Tag.categories.general}
|
||||
|
||||
@@ -24,11 +24,7 @@
|
||||
</div>
|
||||
<div class="row list-of-tags">
|
||||
<strong>Tags</strong>
|
||||
<% post.tag_array.each do |tag_name| %>
|
||||
<span class="category-<%= Tag.category_for(tag_name) %>">
|
||||
<%= link_to(tag_name.tr("_", " "), posts_path(:tags => tag_name)) %>
|
||||
</span>
|
||||
<% end %>
|
||||
<%= post.presenter.inline_tag_list_html(self) %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<% if @dmail.is_automated? %>
|
||||
<p class="tn">
|
||||
This is an automated message. Post in the forums if you have any questions.
|
||||
This is an automated message. Responses will not be seen. If you have any questions either message a moderator or ask in the forum.
|
||||
</p>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<strong>Hidden</strong>: <%= render "post_disapprovals/compact_counts", :disapprovals => post.disapprovals, :post => post %>
|
||||
</li>
|
||||
<li><strong>Source</strong>: <%= post.source %></li>
|
||||
<li><strong>Tags</strong>: <%= post.tag_string %></li>
|
||||
<li><strong>Tags</strong>: <%= post.presenter.inline_tag_list_html(self) %></li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<% if disapprovals.count > 0 && (CurrentUser.can_approve_posts? || post.created_at < 3.days.ago) %>
|
||||
<% if disapprovals.breaks_rules.count > 0 %>
|
||||
(breaks rules: <%= disapprovals.breaks_rules.count %>)
|
||||
<% if CurrentUser.can_approve_posts? || post.created_at < 3.days.ago %>
|
||||
<% if disapprovals.map(&:reason).grep("breaks_rules").count > 0 %>
|
||||
(breaks rules: <%= disapprovals.map(&:reason).grep("breaks_rules").count %>)
|
||||
<% end %>
|
||||
|
||||
<% if disapprovals.poor_quality.count > 0 %>
|
||||
(poor quality: <%= disapprovals.poor_quality.count %>)
|
||||
<% if disapprovals.map(&:reason).grep("poor_quality").count > 0 %>
|
||||
(poor quality: <%= disapprovals.map(&:reason).grep("poor_quality").count %>)
|
||||
<% end %>
|
||||
|
||||
<% if disapprovals.disinterest.count > 0 %>
|
||||
(no interest: <%= disapprovals.disinterest.count %>)
|
||||
<% if disapprovals.map(&:reason).grep(/disinterest|legacy/).count > 0 %>
|
||||
(no interest: <%= disapprovals.map(&:reason).grep(/disinterest|legacy/).count %>)
|
||||
<% end %>
|
||||
|
||||
<% if disapprovals.with_message.any? %>
|
||||
(messages: <%= disapprovals.disinterest.with_message.map(&:message).to_sentence %>)
|
||||
<% if disapprovals.map(&:message).any?(&:present?) %>
|
||||
(messages: <%= disapprovals.map(&:message).select(&:present?).map { |msg| format_text(msg, ragel: true, inline: true) }.to_sentence.html_safe %>)
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
<% if disapprovals.count > 0 && (CurrentUser.can_approve_posts? || post.created_at < 3.days.ago) %>
|
||||
<% if CurrentUser.can_approve_posts? || post.created_at < 3.days.ago %>
|
||||
<p>
|
||||
It has been reviewed by <%= pluralize disapprovals.count, "moderator" %>.
|
||||
It has been reviewed by <%= pluralize disapprovals.length, "moderator" %>.
|
||||
|
||||
<% if disapprovals.breaks_rules.count > 0 %>
|
||||
<%= disapprovals.breaks_rules.count %> believe it breaks the rules.
|
||||
<% if disapprovals.map(&:reason).grep("breaks_rules").count > 0 %>
|
||||
<%= disapprovals.map(&:reason).grep("breaks_rules").count %> believe it breaks the rules.
|
||||
<% end %>
|
||||
|
||||
<% if disapprovals.poor_quality.count > 0 %>
|
||||
<%= disapprovals.poor_quality.count %> believe it has poor quality.
|
||||
<% if disapprovals.map(&:reason).grep("poor_quality").count > 0 %>
|
||||
<%= disapprovals.map(&:reason).grep("poor_quality").count %> believe it has poor quality.
|
||||
<% end %>
|
||||
|
||||
<% if disapprovals.disinterest.count > 0 %>
|
||||
<%= disapprovals.disinterest.count %> did not like the post enough to approve it.
|
||||
<% if disapprovals.map(&:reason).grep(/disinterest|legacy/).count > 0 %>
|
||||
<%= disapprovals.map(&:reason).grep(/disinterest|legacy/).count %> did not like the post enough to approve it.
|
||||
<% end %>
|
||||
|
||||
<% if disapprovals.with_message.any? %>
|
||||
Messages: <%= disapprovals.with_message.map(&:message).to_sentence %>
|
||||
<% if disapprovals.map(&:message).any?(&:present?) %>
|
||||
Messages: <%= disapprovals.map(&:message).select(&:present?).map { |msg| format_text(msg, ragel: true, inline: true) }.to_sentence.html_safe %>.
|
||||
<% end %>
|
||||
</p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
Reference in New Issue
Block a user