From 5404c1d231f6d89f0d59a29dcb91d899caddba45 Mon Sep 17 00:00:00 2001
From: Albert Yi
Date: Fri, 13 Jan 2017 17:21:05 -0800
Subject: [PATCH 01/14] implement postarchive
---
Gemfile | 1 +
Gemfile.lock | 1 +
app/controllers/post_versions_controller.rb | 4 +-
app/logical/bulk_revert.rb | 2 +-
app/logical/moderator/ip_addr_search.rb | 4 +-
app/logical/reports/post_versions.rb | 2 +-
app/logical/user_revert.rb | 4 +-
app/models/post.rb | 27 +-
app/models/post_archive.rb | 316 +++++++++++++++---
app/models/post_version.rb | 11 -
.../moderator/dashboards_controller_test.rb | 2 +-
test/functional/posts_controller_test.rb | 4 +-
test/helpers/post_archive_test_helper.rb | 45 +++
test/test_helper.rb | 21 ++
test/unit/note_test.rb | 5 +-
test/unit/post_test.rb | 13 +-
test/unit/post_version_test.rb | 14 +-
17 files changed, 378 insertions(+), 98 deletions(-)
create mode 100644 test/helpers/post_archive_test_helper.rb
diff --git a/Gemfile b/Gemfile
index 48c3c5269..55086cc65 100644
--- a/Gemfile
+++ b/Gemfile
@@ -59,6 +59,7 @@ gem 'google-api-client'
gem 'cityhash'
gem 'bigquery', :git => "https://github.com/abronte/BigQuery.git", :ref => "b92b4e0b54574e3fde7ad910f39a67538ed387ad"
gem 'memcache_mock'
+gem 'memoist'
# needed for looser jpeg header compat
gem 'ruby-imagespec', :require => "image_spec", :git => "https://github.com/r888888888/ruby-imagespec.git", :branch => "exif-fixes"
diff --git a/Gemfile.lock b/Gemfile.lock
index 53b147376..c53f268dc 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -405,6 +405,7 @@ DEPENDENCIES
mechanize
memcache-client
memcache_mock
+ memoist
mocha
net-sftp
net-ssh
diff --git a/app/controllers/post_versions_controller.rb b/app/controllers/post_versions_controller.rb
index af484f500..3b955d283 100644
--- a/app/controllers/post_versions_controller.rb
+++ b/app/controllers/post_versions_controller.rb
@@ -3,7 +3,7 @@ class PostVersionsController < ApplicationController
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 +15,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!
diff --git a/app/logical/bulk_revert.rb b/app/logical/bulk_revert.rb
index e95ea806a..b5929ab2c 100644
--- a/app/logical/bulk_revert.rb
+++ b/app/logical/bulk_revert.rb
@@ -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)
diff --git a/app/logical/moderator/ip_addr_search.rb b/app/logical/moderator/ip_addr_search.rb
index eccbf11ab..c47216c04 100644
--- a/app/logical/moderator/ip_addr_search.rb
+++ b/app/logical/moderator/ip_addr_search.rb
@@ -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)
diff --git a/app/logical/reports/post_versions.rb b/app/logical/reports/post_versions.rb
index 9c4da0bd4..5e58c66a0 100644
--- a/app/logical/reports/post_versions.rb
+++ b/app/logical/reports/post_versions.rb
@@ -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)
diff --git a/app/logical/user_revert.rb b/app/logical/user_revert.rb
index d97f25fae..ba104d691 100644
--- a/app/logical/user_revert.rb
+++ b/app/logical/user_revert.rb
@@ -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
diff --git a/app/models/post.rb b/app/models/post.rb
index 059527a73..77237f812 100644
--- a/app/models/post.rb
+++ b/app/models/post.rb
@@ -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)
diff --git a/app/models/post_archive.rb b/app/models/post_archive.rb
index 1cc456331..b9af6d72e 100644
--- a/app/models/post_archive.rb
+++ b/app/models/post_archive.rb
@@ -1,4 +1,6 @@
class PostArchive < ActiveRecord::Base
+ extend Memoist
+
def self.enabled?
Danbooru.config.aws_sqs_archives_url.present?
end
@@ -6,49 +8,283 @@ 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 = 1451061)
+ 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
+ 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 + [:added_tags, :removed_tags, :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
diff --git a/app/models/post_version.rb b/app/models/post_version.rb
index e3c1be962..2006da241 100644
--- a/app/models/post_version.rb
+++ b/app/models/post_version.rb
@@ -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?
diff --git a/test/functional/moderator/dashboards_controller_test.rb b/test/functional/moderator/dashboards_controller_test.rb
index dbdff63fd..6259601e9 100644
--- a/test/functional/moderator/dashboards_controller_test.rb
+++ b/test/functional/moderator/dashboards_controller_test.rb
@@ -53,7 +53,7 @@ module Moderator
end
should "render" do
- assert_equal(1, PostVersion.count)
+ assert_equal(1, PostArchive.count)
get :show, {}, {:user_id => @admin.id}
assert_response :success
end
diff --git a/test/functional/posts_controller_test.rb b/test/functional/posts_controller_test.rb
index 01da6f29f..d78111855 100644
--- a/test/functional/posts_controller_test.rb
+++ b/test/functional/posts_controller_test.rb
@@ -144,12 +144,12 @@ class PostsControllerTest < ActionController::TestCase
context "revert action" do
setup do
- @post.stubs(:merge_version?).returns(false)
+ PostArchive.sqs_service.stubs(:merge?).returns(false)
@post.update_attributes(:tag_string => "zzz")
end
should "work" do
- @version = @post.versions(true).first
+ @version = @post.versions.first
assert_equal("aaaa", @version.tags)
post :revert, {:id => @post.id, :version_id => @version.id}, {:user_id => @user.id}
assert_redirected_to post_path(@post)
diff --git a/test/helpers/post_archive_test_helper.rb b/test/helpers/post_archive_test_helper.rb
new file mode 100644
index 000000000..7a0a19453
--- /dev/null
+++ b/test/helpers/post_archive_test_helper.rb
@@ -0,0 +1,45 @@
+module PostArchiveTestHelper
+ def setup
+ super
+
+ mock_post_archive_service!
+ start_post_archive_transaction
+ end
+
+ def teardown
+ super
+
+ rollback_post_archive_transaction
+ end
+
+ def mock_post_archive_service!
+ mock_sqs_service = Class.new do
+ def send_message(msg)
+ _, json = msg.split(/\n/)
+ json = JSON.parse(json)
+ json.delete("created_at")
+ json["version"] = 1 + PostArchive.where(post_id: json["post_id"]).count
+ prev = PostArchive.where(post_id: json["post_id"]).order("id desc").first
+ if merge?(prev, json)
+ prev.update_columns(json)
+ else
+ PostArchive.create(json)
+ end
+ end
+
+ def merge?(prev, json)
+ prev && (prev.updater_id == json["updater_id"]) && (prev.updated_at >= 1.hour.ago)
+ end
+ end
+
+ PostArchive.stubs(:sqs_service).returns(mock_sqs_service.new)
+ end
+
+ def start_post_archive_transaction
+ PostArchive.connection.begin_transaction joinable: false
+ end
+
+ def rollback_post_archive_transaction
+ PostArchive.connection.rollback_transaction
+ end
+end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index d428a5de4..75496a920 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -11,6 +11,7 @@ end
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'cache'
+require 'helpers/post_archive_test_helper'
Dir[File.expand_path(File.dirname(__FILE__) + "/factories/*.rb")].each {|file| require file}
@@ -24,6 +25,26 @@ if defined?(MEMCACHE)
Object.send(:remove_const, :MEMCACHE)
end
+class ActiveSupport::TestCase
+ include UploadTestMethods
+ include PostArchiveTestHelper
+end
+
+class ActionController::TestCase
+ include UploadTestMethods
+ include PostArchiveTestHelper
+
+ def assert_authentication_passes(action, http_method, role, params, session)
+ __send__(http_method, action, params, session.merge(:user_id => @users[role].id))
+ assert_response :success
+ end
+
+ def assert_authentication_fails(action, http_method, role)
+ __send__(http_method, action, params, session.merge(:user_id => @users[role].id))
+ assert_redirected_to(new_sessions_path)
+ end
+end
+
MEMCACHE = MemcacheMock.new
Delayed::Worker.delay_jobs = false
diff --git a/test/unit/note_test.rb b/test/unit/note_test.rb
index d2a392162..081f46fbd 100644
--- a/test/unit/note_test.rb
+++ b/test/unit/note_test.rb
@@ -107,11 +107,14 @@ class NoteTest < ActiveSupport::TestCase
setup do
@post = FactoryGirl.create(:post, :image_width => 1000, :image_height => 1000)
@note = FactoryGirl.create(:note, :post => @post)
+ @note.stubs(:merge_version?).returns(false)
end
should "increment the updater's note_update_count" do
- assert_difference("CurrentUser.note_update_count", 1) do
+ @user.reload
+ assert_difference("@user.note_update_count", 1) do
@note.update_attributes(:body => "zzz")
+ @user.reload
end
end
diff --git a/test/unit/post_test.rb b/test/unit/post_test.rb
index 0ac525442..bfd21d421 100644
--- a/test/unit/post_test.rb
+++ b/test/unit/post_test.rb
@@ -1055,7 +1055,7 @@ class PostTest < ActiveSupport::TestCase
context "that has been updated" do
should "create a new version if it's the first version" do
- assert_difference("PostVersion.count", 1) do
+ assert_difference("PostArchive.count", 1) do
post = FactoryGirl.create(:post)
end
end
@@ -1063,7 +1063,7 @@ class PostTest < ActiveSupport::TestCase
should "create a new version if it's been over an hour since the last update" do
post = FactoryGirl.create(:post)
Timecop.travel(6.hours.from_now) do
- assert_difference("PostVersion.count", 1) do
+ assert_difference("PostArchive.count", 1) do
post.update_attributes(:tag_string => "zzz")
end
end
@@ -1071,15 +1071,16 @@ class PostTest < ActiveSupport::TestCase
should "merge with the previous version if the updater is the same user and it's been less than an hour" do
post = FactoryGirl.create(:post)
- assert_difference("PostVersion.count", 0) do
+ assert_difference("PostArchive.count", 0) do
post.update_attributes(:tag_string => "zzz")
end
assert_equal("zzz", post.versions.last.tags)
end
should "increment the updater's post_update_count" do
+ PostArchive.sqs_service.stubs(:merge?).returns(false)
post = FactoryGirl.create(:post, :tag_string => "aaa bbb ccc")
- post.stubs(:merge_version?).returns(false)
+ CurrentUser.reload
assert_difference("CurrentUser.post_update_count", 1) do
post.update_attributes(:tag_string => "zzz")
@@ -2289,8 +2290,8 @@ class PostTest < ActiveSupport::TestCase
context "a post that has been updated" do
setup do
- @post = FactoryGirl.create(:post, :rating => "q", :tag_string => "aaa", :source => nil)
- @post.stubs(:merge_version?).returns(false)
+ @post = FactoryGirl.create(:post, :rating => "q", :tag_string => "aaa")
+ PostArchive.sqs_service.stubs(:merge?).returns(false)
@post.update_attributes(:tag_string => "aaa bbb ccc ddd")
@post.update_attributes(:tag_string => "bbb xxx yyy", :source => "xyz")
@post.update_attributes(:tag_string => "bbb mmm yyy", :source => "abc")
diff --git a/test/unit/post_version_test.rb b/test/unit/post_version_test.rb
index cd9f45a01..c89a64dd6 100644
--- a/test/unit/post_version_test.rb
+++ b/test/unit/post_version_test.rb
@@ -18,28 +18,21 @@ class PostVersionTest < ActiveSupport::TestCase
context "that has multiple versions: " do
setup do
+ PostArchive.sqs_service.stubs(:merge?).returns(false)
@post = FactoryGirl.create(:post, :tag_string => "1")
- @post.stubs(:merge_version?).returns(false)
- @post.stubs(:tag_string_changed?).returns(true)
@post.update_attributes(:tag_string => "1 2")
@post.update_attributes(:tag_string => "2 3")
end
context "a version record" do
setup do
- @version = PostVersion.last
+ @version = PostArchive.last
end
should "know its previous version" do
assert_not_nil(@version.previous)
assert_equal("1 2", @version.previous.tags)
end
-
- should "know the seuqence of all versions for the post" do
- assert_equal(2, @version.sequence_for_post.size)
- assert_equal(%w(3), @version.sequence_for_post[0][:added_tags])
- assert_equal(%w(2), @version.sequence_for_post[1][:added_tags])
- end
end
end
@@ -75,9 +68,8 @@ class PostVersionTest < ActiveSupport::TestCase
context "that has been updated" do
setup do
- @parent = FactoryGirl.create(:post)
+ PostArchive.sqs_service.stubs(:merge?).returns(false)
@post = FactoryGirl.create(:post, :tag_string => "aaa bbb ccc", :rating => "q", :source => "xyz")
- @post.stubs(:merge_version?).returns(false)
@post.update_attributes(:tag_string => "bbb ccc xxx", :source => "")
end
From e699684c619dcda8e679c94c1a5a3726a6de2508 Mon Sep 17 00:00:00 2001
From: r888888888
Date: Mon, 23 Jan 2017 12:15:30 -0800
Subject: [PATCH 02/14] add css for highliting a specific forum post
---
app/assets/stylesheets/specific/forum.css.scss | 4 ++++
app/models/forum_topic.rb | 4 ++++
app/models/post_archive.rb | 2 +-
3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/app/assets/stylesheets/specific/forum.css.scss b/app/assets/stylesheets/specific/forum.css.scss
index f9a741458..e81ea4f87 100644
--- a/app/assets/stylesheets/specific/forum.css.scss
+++ b/app/assets/stylesheets/specific/forum.css.scss
@@ -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;
diff --git a/app/models/forum_topic.rb b/app/models/forum_topic.rb
index d19a90c06..55df3e8fe 100644
--- a/app/models/forum_topic.rb
+++ b/app/models/forum_topic.rb
@@ -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
diff --git a/app/models/post_archive.rb b/app/models/post_archive.rb
index b9af6d72e..fa4e044c2 100644
--- a/app/models/post_archive.rb
+++ b/app/models/post_archive.rb
@@ -74,7 +74,7 @@ class PostArchive < ActiveRecord::Base
sqs_service.send_message(msg)
end
- def export_to_archives(version_id = 1451061)
+ def export_to_archives(version_id = 4394763)
PostVersion.where("id > ?", version_id).find_each do |version|
previous = version.previous
tags = version.tags.scan(/\S+/)
From 6bd03ee4dbfa5b9697de33c9cda68593894ed5a7 Mon Sep 17 00:00:00 2001
From: r888888888
Date: Tue, 21 Feb 2017 14:37:40 -0800
Subject: [PATCH 03/14] fix tests
---
test/test_helper.rb | 2 --
test/unit/post_test.rb | 10 +++++++---
test/unit/post_vote_test.rb | 4 +++-
3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 75496a920..b71c9bbb3 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -26,12 +26,10 @@ if defined?(MEMCACHE)
end
class ActiveSupport::TestCase
- include UploadTestMethods
include PostArchiveTestHelper
end
class ActionController::TestCase
- include UploadTestMethods
include PostArchiveTestHelper
def assert_authentication_passes(action, http_method, role, params, session)
diff --git a/test/unit/post_test.rb b/test/unit/post_test.rb
index bfd21d421..96b0ab8a4 100644
--- a/test/unit/post_test.rb
+++ b/test/unit/post_test.rb
@@ -10,7 +10,9 @@ class PostTest < ActiveSupport::TestCase
assert_equal(posts.map(&:id), Post.tag_match(query).pluck(:id))
end
- setup do
+ def setup
+ super
+
Timecop.travel(2.weeks.ago) do
@user = FactoryGirl.create(:user)
end
@@ -20,7 +22,9 @@ class PostTest < ActiveSupport::TestCase
mock_saved_search_service!
end
- teardown do
+ def teardown
+ super
+
CurrentUser.user = nil
CurrentUser.ip_addr = nil
end
@@ -2290,8 +2294,8 @@ class PostTest < ActiveSupport::TestCase
context "a post that has been updated" do
setup do
- @post = FactoryGirl.create(:post, :rating => "q", :tag_string => "aaa")
PostArchive.sqs_service.stubs(:merge?).returns(false)
+ @post = FactoryGirl.create(:post, :rating => "q", :tag_string => "aaa", :source => nil)
@post.update_attributes(:tag_string => "aaa bbb ccc ddd")
@post.update_attributes(:tag_string => "bbb xxx yyy", :source => "xyz")
@post.update_attributes(:tag_string => "bbb mmm yyy", :source => "abc")
diff --git a/test/unit/post_vote_test.rb b/test/unit/post_vote_test.rb
index 33e874fbf..4b35fa200 100644
--- a/test/unit/post_vote_test.rb
+++ b/test/unit/post_vote_test.rb
@@ -1,7 +1,9 @@
require 'test_helper'
class PostVoteTest < ActiveSupport::TestCase
- setup do
+ def setup
+ super
+
user = FactoryGirl.create(:user)
CurrentUser.user = user
CurrentUser.ip_addr = "127.0.0.1"
From 8a57d38f38c86dcaa218996c2495f89e10b62e58 Mon Sep 17 00:00:00 2001
From: r888888888
Date: Fri, 24 Feb 2017 10:36:45 -0800
Subject: [PATCH 04/14] remove statement about responding in automated messages
---
app/logical/approver_pruner.rb | 2 +-
app/models/janitor_trial.rb | 2 +-
app/views/dmails/show.html.erb | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/logical/approver_pruner.rb b/app/logical/approver_pruner.rb
index 8d9300bfc..bba3ffbf1 100644
--- a/app/logical/approver_pruner.rb
+++ b/app/logical/approver_pruner.rb
@@ -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
diff --git a/app/models/janitor_trial.rb b/app/models/janitor_trial.rb
index d7e1a55cb..6e07fb650 100644
--- a/app/models/janitor_trial.rb
+++ b/app/models/janitor_trial.rb
@@ -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
diff --git a/app/views/dmails/show.html.erb b/app/views/dmails/show.html.erb
index ef0a43623..c5973b319 100644
--- a/app/views/dmails/show.html.erb
+++ b/app/views/dmails/show.html.erb
@@ -19,7 +19,7 @@
<% if @dmail.is_automated? %>
- 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.
<% end %>
From 2ebdfd7ab3f436069502e67b027ec0c959438231 Mon Sep 17 00:00:00 2001
From: r888888888
Date: Fri, 24 Feb 2017 14:00:59 -0800
Subject: [PATCH 05/14] add s3 methods to amazonbackup
---
app/models/amazon_backup.rb | 106 ++++++++++++++++++++++++++++++++++++
1 file changed, 106 insertions(+)
diff --git a/app/models/amazon_backup.rb b/app/models/amazon_backup.rb
index 44f16921b..d7835fe70 100644
--- a/app/models/amazon_backup.rb
+++ b/app/models/amazon_backup.rb
@@ -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?
From 9b40016895c94fdec6b00f20088116e45de699d0 Mon Sep 17 00:00:00 2001
From: evazion
Date: Fri, 24 Feb 2017 19:11:48 -0600
Subject: [PATCH 06/14] /posts/NNN, modqueue: allow dtext in disapproval
reasons.
---
app/views/post_disapprovals/_compact_counts.html.erb | 4 ++--
app/views/post_disapprovals/_counts.html.erb | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/app/views/post_disapprovals/_compact_counts.html.erb b/app/views/post_disapprovals/_compact_counts.html.erb
index 5bb3a6b12..9e08a9745 100644
--- a/app/views/post_disapprovals/_compact_counts.html.erb
+++ b/app/views/post_disapprovals/_compact_counts.html.erb
@@ -12,6 +12,6 @@
<% end %>
<% if disapprovals.with_message.any? %>
- (messages: <%= disapprovals.disinterest.with_message.map(&:message).to_sentence %>)
+ (messages: <%= disapprovals.map(&:message).select(&:present?).map { |msg| format_text(msg, ragel: true, inline: true) }.to_sentence.html_safe %>)
<% end %>
-<% end %>
\ No newline at end of file
+<% end %>
diff --git a/app/views/post_disapprovals/_counts.html.erb b/app/views/post_disapprovals/_counts.html.erb
index e6ec12ead..612e4f7e7 100644
--- a/app/views/post_disapprovals/_counts.html.erb
+++ b/app/views/post_disapprovals/_counts.html.erb
@@ -15,7 +15,7 @@
<% end %>
<% if disapprovals.with_message.any? %>
- Messages: <%= disapprovals.with_message.map(&:message).to_sentence %>
+ Messages: <%= disapprovals.map(&:message).select(&:present?).map { |msg| format_text(msg, ragel: true, inline: true) }.to_sentence.html_safe %>.
<% end %>
-<% end %>
\ No newline at end of file
+<% end %>
From 83915a5d468a53889f4bfd41a0f88aa56340b169 Mon Sep 17 00:00:00 2001
From: evazion
Date: Fri, 24 Feb 2017 19:13:02 -0600
Subject: [PATCH 07/14] modqueue: avoid N+1 queries for disapprovals,
uploaders.
Fixes an N+1 queries problem in the /moderator/post/queue view by
prefetching disapprovals and uploaders.
Also the way disapproval messages were previously rendered triggered a bunch
of sql queries for each post:
SELECT COUNT(*) FROM "post_disapprovals" WHERE "post_disapprovals"."post_id" = $1 [["post_id", 52]]
SELECT COUNT(*) FROM "post_disapprovals" WHERE "post_disapprovals"."post_id" = $1 AND "post_disapprovals"."reason" = $2 [["post_id", 52], ["reason", "breaks_rules"]]
SELECT COUNT(*) FROM "post_disapprovals" WHERE "post_disapprovals"."post_id" = $1 AND "post_disapprovals"."reason" = $2 [["post_id", 52], ["reason", "poor_quality"]]
SELECT COUNT(*) FROM "post_disapprovals" WHERE "post_disapprovals"."post_id" = $1 AND "post_disapprovals"."reason" IN ('disinterest', 'legacy') [["post_id", 52]]
SELECT COUNT(*) FROM "post_disapprovals" WHERE "post_disapprovals"."post_id" = $1 AND (message is not null and message <> '') [["post_id", 52]]
SELECT "post_disapprovals".* FROM "post_disapprovals" WHERE "post_disapprovals"."post_id" = $1 AND (message is not null and message <> '') [["post_id", 52]]
This refactors to bring it down to one:
SELECT "post_disapprovals".* FROM "post_disapprovals" WHERE "post_disapprovals"."post_id" = $1 [["post_id", 52]]
---
.../moderator/post/queues_controller.rb | 4 ++--
.../post_disapprovals/_compact_counts.html.erb | 16 ++++++++--------
app/views/post_disapprovals/_counts.html.erb | 18 +++++++++---------
3 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/app/controllers/moderator/post/queues_controller.rb b/app/controllers/moderator/post/queues_controller.rb
index 06b80dd18..e274c19fb 100644
--- a/app/controllers/moderator/post/queues_controller.rb
+++ b/app/controllers/moderator/post/queues_controller.rb
@@ -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?
diff --git a/app/views/post_disapprovals/_compact_counts.html.erb b/app/views/post_disapprovals/_compact_counts.html.erb
index 9e08a9745..78ced11bc 100644
--- a/app/views/post_disapprovals/_compact_counts.html.erb
+++ b/app/views/post_disapprovals/_compact_counts.html.erb
@@ -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? %>
+ <% 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 %>
diff --git a/app/views/post_disapprovals/_counts.html.erb b/app/views/post_disapprovals/_counts.html.erb
index 612e4f7e7..f858c96af 100644
--- a/app/views/post_disapprovals/_counts.html.erb
+++ b/app/views/post_disapprovals/_counts.html.erb
@@ -1,20 +1,20 @@
-<% 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 %>
- 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? %>
+ <% 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 %>
From b80976bce716f05b616ce5d10218bc1a98ff0b31 Mon Sep 17 00:00:00 2001
From: evazion
Date: Fri, 24 Feb 2017 20:59:21 -0600
Subject: [PATCH 08/14] modqueue: link tags, like they are in /comments.
---
app/presenters/post_presenter.rb | 14 ++++++++++----
app/presenters/tag_set_presenter.rb | 11 +++++++++++
app/views/comments/partials/index/_header.html.erb | 6 +-----
app/views/moderator/post/queues/show.html.erb | 2 +-
4 files changed, 23 insertions(+), 10 deletions(-)
diff --git a/app/presenters/post_presenter.rb b/app/presenters/post_presenter.rb
index adf3a1b9a..9cfa5e712 100644
--- a/app/presenters/post_presenter.rb
+++ b/app/presenters/post_presenter.rb
@@ -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)
diff --git a/app/presenters/tag_set_presenter.rb b/app/presenters/tag_set_presenter.rb
index 2321bc03f..a09d3e586 100644
--- a/app/presenters/tag_set_presenter.rb
+++ b/app/presenters/tag_set_presenter.rb
@@ -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
+
+ #{template.link_to(tag_name.tr("_", " "), template.posts_path(tags: tag_name))}
+
+ EOS
+ end.join.html_safe
+ end
+
private
def general_tags
@general_tags ||= categories.select {|k, v| v == Tag.categories.general}
diff --git a/app/views/comments/partials/index/_header.html.erb b/app/views/comments/partials/index/_header.html.erb
index 0e0e1ce02..1815dc23b 100644
--- a/app/views/comments/partials/index/_header.html.erb
+++ b/app/views/comments/partials/index/_header.html.erb
@@ -24,11 +24,7 @@
Tags
- <% post.tag_array.each do |tag_name| %>
-
- <%= link_to(tag_name.tr("_", " "), posts_path(:tags => tag_name)) %>
-
- <% end %>
+ <%= post.presenter.inline_tag_list_html(self) %>
diff --git a/app/views/moderator/post/queues/show.html.erb b/app/views/moderator/post/queues/show.html.erb
index 1b9fe1aba..f23b1714b 100644
--- a/app/views/moderator/post/queues/show.html.erb
+++ b/app/views/moderator/post/queues/show.html.erb
@@ -60,7 +60,7 @@
Hidden: <%= render "post_disapprovals/compact_counts", :disapprovals => post.disapprovals, :post => post %>
Source: <%= post.source %>
- Tags: <%= post.tag_string %>
+ Tags: <%= post.presenter.inline_tag_list_html(self) %>
From 6966b74e18cb2f620636b5a39d4ff89a5902b7af Mon Sep 17 00:00:00 2001
From: evazion
Date: Sat, 25 Feb 2017 01:47:57 -0600
Subject: [PATCH 09/14] Disallow unicode whitespace in usernames (#2894).
---
app/logical/user_name_validator.rb | 10 ++++++++++
app/models/user.rb | 10 ++++++----
app/models/user_name_change_request.rb | 18 +++---------------
test/unit/user_test.rb | 7 +++++++
4 files changed, 26 insertions(+), 19 deletions(-)
create mode 100644 app/logical/user_name_validator.rb
diff --git a/app/logical/user_name_validator.rb b/app/logical/user_name_validator.rb
new file mode 100644
index 000000000..c86c693db
--- /dev/null
+++ b/app/logical/user_name_validator.rb
@@ -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
diff --git a/app/models/user.rb b/app/models/user.rb
index 73a29581d..6c0ac9129 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -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
diff --git a/app/models/user_name_change_request.rb b/app/models/user_name_change_request.rb
index eaf8cd372..b7de494f9 100644
--- a/app/models/user_name_change_request.rb
+++ b/app/models/user_name_change_request.rb
@@ -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
diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb
index ffb505616..523ff75e8 100644
--- a/test/unit/user_test.rb
+++ b/test/unit/user_test.rb
@@ -187,6 +187,13 @@ class UserTest < ActiveSupport::TestCase
assert_equal(Danbooru.config.default_guest_name, User.id_to_name(-1))
end
+ should "not contain whitespace" do
+ # U+2007: https://en.wikipedia.org/wiki/Figure_space
+ user = FactoryGirl.build(:user, :name => "foo\u2007bar")
+ user.save
+ assert_equal(["Name cannot have whitespace or colons"], user.errors.full_messages)
+ end
+
should "not contain a colon" do
user = FactoryGirl.build(:user, :name => "a:b")
user.save
From 40092f21ba22337f20b29e3cd7e9a88ac0185986 Mon Sep 17 00:00:00 2001
From: evazion
Date: Mon, 26 Dec 2016 16:20:23 -0600
Subject: [PATCH 10/14] Remove redundant rescue_from calls.
These are redundant because ApplicationController rescues these exceptions already.
---
app/controllers/admin/users_controller.rb | 1 -
app/controllers/comments_controller.rb | 1 -
app/controllers/dmails_controller.rb | 1 -
app/controllers/legacy_controller.rb | 1 -
app/controllers/maintenance/user/deletions_controller.rb | 2 --
app/controllers/moderator/post/posts_controller.rb | 1 -
app/controllers/post_versions_controller.rb | 1 -
app/controllers/posts_controller.rb | 4 ----
app/controllers/uploads_controller.rb | 1 -
app/controllers/user_name_change_requests_controller.rb | 1 -
app/controllers/users_controller.rb | 1 -
app/controllers/wiki_pages_controller.rb | 2 --
12 files changed, 17 deletions(-)
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index f917eb6b1..748c8fad9 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -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])
diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb
index 96933d631..86a97ddb9 100644
--- a/app/controllers/comments_controller.rb
+++ b/app/controllers/comments_controller.rb
@@ -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
diff --git a/app/controllers/dmails_controller.rb b/app/controllers/dmails_controller.rb
index 97e19a1d7..8c01895e3 100644
--- a/app/controllers/dmails_controller.rb
+++ b/app/controllers/dmails_controller.rb
@@ -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]
diff --git a/app/controllers/legacy_controller.rb b/app/controllers/legacy_controller.rb
index f2204f345..385080220 100644
--- a/app/controllers/legacy_controller.rb
+++ b/app/controllers/legacy_controller.rb
@@ -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")
diff --git a/app/controllers/maintenance/user/deletions_controller.rb b/app/controllers/maintenance/user/deletions_controller.rb
index 2022595b2..a8254da37 100644
--- a/app/controllers/maintenance/user/deletions_controller.rb
+++ b/app/controllers/maintenance/user/deletions_controller.rb
@@ -1,8 +1,6 @@
module Maintenance
module User
class DeletionsController < ApplicationController
- rescue_from UserDeletion::ValidationError, :with => :rescue_exception
-
def show
end
diff --git a/app/controllers/moderator/post/posts_controller.rb b/app/controllers/moderator/post/posts_controller.rb
index 38e29cd76..94ceb1166 100644
--- a/app/controllers/moderator/post/posts_controller.rb
+++ b/app/controllers/moderator/post/posts_controller.rb
@@ -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
diff --git a/app/controllers/post_versions_controller.rb b/app/controllers/post_versions_controller.rb
index af484f500..9e434b312 100644
--- a/app/controllers/post_versions_controller.rb
+++ b/app/controllers/post_versions_controller.rb
@@ -1,6 +1,5 @@
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])
diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb
index 0ddecd2f0..8708a6adf 100644
--- a/app/controllers/posts_controller.rb
+++ b/app/controllers/posts_controller.rb
@@ -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?
diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb
index 560750ca2..9a353aad9 100644
--- a/app/controllers/uploads_controller.rb
+++ b/app/controllers/uploads_controller.rb
@@ -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
diff --git a/app/controllers/user_name_change_requests_controller.rb b/app/controllers/user_name_change_requests_controller.rb
index 5cbed2686..7c5c79378 100644
--- a/app/controllers/user_name_change_requests_controller.rb
+++ b/app/controllers/user_name_change_requests_controller.rb
@@ -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
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index b446f8cd2..9cda71582 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -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
diff --git a/app/controllers/wiki_pages_controller.rb b/app/controllers/wiki_pages_controller.rb
index 9dd22b3f3..96b92d4ad 100644
--- a/app/controllers/wiki_pages_controller.rb
+++ b/app/controllers/wiki_pages_controller.rb
@@ -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])
From 16cd879e083cd81d5b39a4917b308675e2e099b7 Mon Sep 17 00:00:00 2001
From: r888888888
Date: Mon, 27 Feb 2017 15:22:15 -0800
Subject: [PATCH 11/14] potential fix for #2900: Post Versions JSON API failure
- undefined method join
---
app/models/post_archive.rb | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/models/post_archive.rb b/app/models/post_archive.rb
index fa4e044c2..8b92f1560 100644
--- a/app/models/post_archive.rb
+++ b/app/models/post_archive.rb
@@ -221,6 +221,8 @@ class PostArchive < ActiveRecord::Base
if previous
delta[:unchanged_tags] = tag_array & previous.tag_array
+ else
+ delta[:unchanged_tags] = []
end
delta
From a90a1bd8954358373268b780b86fc8213481edba Mon Sep 17 00:00:00 2001
From: r888888888
Date: Mon, 27 Feb 2017 15:41:45 -0800
Subject: [PATCH 12/14] fix boolean checks in postarchive
---
app/models/post_archive.rb | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/app/models/post_archive.rb b/app/models/post_archive.rb
index 8b92f1560..77fe48047 100644
--- a/app/models/post_archive.rb
+++ b/app/models/post_archive.rb
@@ -192,7 +192,7 @@ class PostArchive < ActiveRecord::Base
latest_tags << "parent:#{post.parent_id}" if post.parent_id.present?
latest_tags << "source:#{post.source}" if post.source.present?
- if parent_changed?
+ if parent_changed
delta[:added_tags] << "parent:#{parent_id}"
if previous
@@ -200,7 +200,7 @@ class PostArchive < ActiveRecord::Base
end
end
- if rating_changed?
+ if rating_changed
delta[:added_tags] << "rating:#{rating}"
if previous
@@ -208,7 +208,7 @@ class PostArchive < ActiveRecord::Base
end
end
- if source_changed?
+ if source_changed
delta[:added_tags] << "source:#{source}"
if previous
@@ -285,7 +285,7 @@ class PostArchive < ActiveRecord::Base
end
def method_attributes
- super + [:added_tags, :removed_tags, :obsolete_added_tags, :obsolete_removed_tags, :unchanged_tags, :updater_name]
+ 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
From b332273378610e21136efa23285b37e2fc92e8e5 Mon Sep 17 00:00:00 2001
From: r888888888
Date: Mon, 27 Feb 2017 16:43:19 -0800
Subject: [PATCH 13/14] fixes #2892: after_destroy callbacks should be fired
upon comment deletion
---
app/models/comment.rb | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/models/comment.rb b/app/models/comment.rb
index e455e5cef..8418c41b8 100644
--- a/app/models/comment.rb
+++ b/app/models/comment.rb
@@ -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]
@@ -25,7 +25,7 @@ class Comment < ActiveRecord::Base
:user_field => :creator_id,
:title => "You were mentioned in a comment",
:body => lambda {|rec, user_name| "You were mentioned in a \"comment\":/posts/#{rec.post_id}#comment-#{rec.id}\n\n---\n\n[i]#{rec.creator.name} said:[/i]\n\n#{ActionController::Base.helpers.excerpt(rec.body, user_name)}"}
- )
+ )
module SearchMethods
def recent
From 80f439f075e81a1dd38397e6c9a0b585b22342ca Mon Sep 17 00:00:00 2001
From: r888888888
Date: Mon, 27 Feb 2017 16:56:59 -0800
Subject: [PATCH 14/14] fixes #2898: Wiki Page Versions still not recording
"is_deleted"
---
app/models/wiki_page.rb | 1 +
app/models/wiki_page_version.rb | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index 1ef615a30..d31b7fb20 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -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
diff --git a/app/models/wiki_page_version.rb b/app/models/wiki_page_version.rb
index 5e580061d..f830eb87b 100644
--- a/app/models/wiki_page_version.rb
+++ b/app/models/wiki_page_version.rb
@@ -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