Merge pull request #3062 from evazion/fix-post-replacements
Post replacements: rescale notes, don't spam @mentions, add help:replacement_notice.
This commit is contained in:
@@ -127,6 +127,14 @@ class Note < ActiveRecord::Base
|
||||
User.id_to_name(creator_id)
|
||||
end
|
||||
|
||||
def rescale!(x_scale, y_scale)
|
||||
self.x *= x_scale
|
||||
self.y *= y_scale
|
||||
self.width *= x_scale
|
||||
self.height *= y_scale
|
||||
save!
|
||||
end
|
||||
|
||||
def update_post
|
||||
if self.changed?
|
||||
if Note.where(:is_active => true, :post_id => post_id).exists?
|
||||
|
||||
@@ -17,11 +17,6 @@ class PostReplacement < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def process!
|
||||
# TODO for posts with notes we need to rescale the notes if the dimensions change.
|
||||
if post.notes.any?
|
||||
raise NotImplementedError.new("Replacing images with notes not yet supported.")
|
||||
end
|
||||
|
||||
# TODO for ugoiras we need to replace the frame data.
|
||||
if post.is_ugoira?
|
||||
raise NotImplementedError.new("Replacing ugoira images not yet supported.")
|
||||
@@ -49,9 +44,10 @@ class PostReplacement < ActiveRecord::Base
|
||||
post.file_size = upload.file_size
|
||||
post.source = upload.source
|
||||
post.tag_string = upload.tag_string
|
||||
rescale_notes
|
||||
|
||||
post.comments.create!({creator: User.system, body: post.presenter.comment_replacement_message(creator), do_not_bump_post: true}, without_protection: true)
|
||||
ModAction.log(post.presenter.modaction_replacement_message)
|
||||
post.comments.create!({creator: User.system, body: comment_replacement_message, do_not_bump_post: true}, without_protection: true)
|
||||
ModAction.log(modaction_replacement_message)
|
||||
|
||||
post.save!
|
||||
end
|
||||
@@ -62,6 +58,15 @@ class PostReplacement < ActiveRecord::Base
|
||||
post.update_iqdb_async
|
||||
end
|
||||
|
||||
def rescale_notes
|
||||
x_scale = post.image_width.to_f / post.image_width_was.to_f
|
||||
y_scale = post.image_height.to_f / post.image_height_was.to_f
|
||||
|
||||
post.notes.each do |note|
|
||||
note.rescale!(x_scale, y_scale)
|
||||
end
|
||||
end
|
||||
|
||||
module SearchMethods
|
||||
def search(params = {})
|
||||
q = all
|
||||
@@ -88,5 +93,55 @@ class PostReplacement < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
module PresenterMethods
|
||||
def comment_replacement_message
|
||||
%("#{creator.name}":[/users/#{creator.id}] replaced this post with a new image:\n\n#{replacement_message})
|
||||
end
|
||||
|
||||
def modaction_replacement_message
|
||||
"replaced post ##{post.id}:\n\n#{replacement_message}"
|
||||
end
|
||||
|
||||
def replacement_message
|
||||
linked_source = linked_source(post.source)
|
||||
linked_source_was = linked_source(post.source_was)
|
||||
|
||||
<<-EOS.strip_heredoc
|
||||
[table]
|
||||
[tbody]
|
||||
[tr]
|
||||
[th]Old[/th]
|
||||
[td]#{linked_source_was}[/td]
|
||||
[td]#{post.md5_was}[/td]
|
||||
[td]#{post.file_ext_was}[/td]
|
||||
[td]#{post.image_width_was} x #{post.image_height_was}[/td]
|
||||
[td]#{post.file_size_was.to_s(:human_size, precision: 4)}[/td]
|
||||
[/tr]
|
||||
[tr]
|
||||
[th]New[/th]
|
||||
[td]#{linked_source}[/td]
|
||||
[td]#{post.md5}[/td]
|
||||
[td]#{post.file_ext}[/td]
|
||||
[td]#{post.image_width} x #{post.image_height}[/td]
|
||||
[td]#{post.file_size.to_s(:human_size, precision: 4)}[/td]
|
||||
[/tr]
|
||||
[/tbody]
|
||||
[/table]
|
||||
EOS
|
||||
end
|
||||
|
||||
def linked_source(source)
|
||||
# truncate long sources in the middle: "www.pixiv.net...lust_id=23264933"
|
||||
truncated_source = source.gsub(%r{\Ahttps?://}, "").truncate(64, omission: "...#{source.last(32)}")
|
||||
|
||||
if source =~ %r{\Ahttps?://}i
|
||||
%("#{truncated_source}":[#{source}])
|
||||
else
|
||||
truncated_source
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
include PresenterMethods
|
||||
extend SearchMethods
|
||||
end
|
||||
|
||||
@@ -279,53 +279,4 @@ class PostPresenter < Presenter
|
||||
pool_html << "</li>"
|
||||
pool_html
|
||||
end
|
||||
|
||||
def comment_replacement_message(replacer = CurrentUser.user)
|
||||
"@#{replacer.name} replaced this post with a new image:\n\n#{replacement_message}"
|
||||
end
|
||||
|
||||
def modaction_replacement_message
|
||||
"replaced post ##{@post.id}:\n\n#{replacement_message}"
|
||||
end
|
||||
|
||||
def replacement_message
|
||||
linked_source = linked_source(@post.source)
|
||||
linked_source_was = linked_source(@post.source_was)
|
||||
|
||||
<<-EOS.strip_heredoc
|
||||
[table]
|
||||
[tbody]
|
||||
[tr]
|
||||
[th]Old[/th]
|
||||
[td]#{linked_source_was}[/td]
|
||||
[td]#{@post.md5_was}[/td]
|
||||
[td]#{@post.file_ext_was}[/td]
|
||||
[td]#{@post.image_width_was} x #{@post.image_height_was}[/td]
|
||||
[td]#{@post.file_size_was.to_s(:human_size, precision: 4)}[/td]
|
||||
[/tr]
|
||||
[tr]
|
||||
[th]New[/th]
|
||||
[td]#{linked_source}[/td]
|
||||
[td]#{@post.md5}[/td]
|
||||
[td]#{@post.file_ext}[/td]
|
||||
[td]#{@post.image_width} x #{@post.image_height}[/td]
|
||||
[td]#{@post.file_size.to_s(:human_size, precision: 4)}[/td]
|
||||
[/tr]
|
||||
[/tbody]
|
||||
[/table]
|
||||
EOS
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def linked_source(source)
|
||||
# truncate long sources in the middle: "www.pixiv.net...lust_id=23264933"
|
||||
truncated_source = source.gsub(%r{\Ahttps?://}, "").truncate(64, omission: "...#{source.last(32)}")
|
||||
|
||||
if source =~ %r{\Ahttps?://}i
|
||||
%("#{truncated_source}":[#{source}])
|
||||
else
|
||||
truncated_source
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
<%= format_text(WikiPage.titled(Danbooru.config.replacement_notice_wiki_page).first.try(&:body), ragel: true) %>
|
||||
|
||||
<%= simple_form_for(post_replacement, url: post_replacements_path(post_id: post_replacement.post_id), method: :post) do |f| %>
|
||||
<h1>Replace Image</h1>
|
||||
|
||||
<p>
|
||||
Delete the current image and replace it with another one, keeping
|
||||
everything else in the post intact. This is meant for upgrading
|
||||
lower-quality images, such as image samples, to higher-quality versions.
|
||||
</p>
|
||||
|
||||
<%= f.input :replacement_url, label: "New Source", input_html: { value: "" } %>
|
||||
<% end %>
|
||||
|
||||
@@ -271,6 +271,10 @@ module Danbooru
|
||||
"help:appeal_notice"
|
||||
end
|
||||
|
||||
def replacement_notice_wiki_page
|
||||
"help:replacement_notice"
|
||||
end
|
||||
|
||||
# The number of posts displayed per page.
|
||||
def posts_per_page
|
||||
20
|
||||
|
||||
157
test/unit/post_replacement_test.rb
Normal file
157
test/unit/post_replacement_test.rb
Normal file
@@ -0,0 +1,157 @@
|
||||
require 'test_helper'
|
||||
require 'helpers/iqdb_test_helper'
|
||||
|
||||
class PostReplacementTest < ActiveSupport::TestCase
|
||||
include IqdbTestHelper
|
||||
|
||||
def setup
|
||||
mock_iqdb_service!
|
||||
Delayed::Worker.delay_jobs = true # don't delete the old images right away
|
||||
|
||||
@system = FactoryGirl.create(:user, created_at: 2.weeks.ago)
|
||||
Danbooru.config.stubs(:system_user).returns(@system)
|
||||
|
||||
@uploader = FactoryGirl.create(:user, created_at: 2.weeks.ago, can_upload_free: true)
|
||||
@replacer = FactoryGirl.create(:user, created_at: 2.weeks.ago, can_approve_posts: true)
|
||||
CurrentUser.user = @replacer
|
||||
CurrentUser.ip_addr = "127.0.0.1"
|
||||
end
|
||||
|
||||
def teardown
|
||||
CurrentUser.user = nil
|
||||
CurrentUser.ip_addr = nil
|
||||
Delayed::Worker.delay_jobs = false
|
||||
end
|
||||
|
||||
context "Replacing" do
|
||||
setup do
|
||||
CurrentUser.scoped(@uploader, "127.0.0.2") do
|
||||
upload = FactoryGirl.create(:jpg_upload, as_pending: "0")
|
||||
upload.process!
|
||||
@post = upload.post
|
||||
end
|
||||
end
|
||||
|
||||
context "a post from a generic source" do
|
||||
setup do
|
||||
@post.update(source: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png")
|
||||
@post.replace!(replacement_url: "https://www.google.com/intl/en_ALL/images/logo.gif")
|
||||
@upload = Upload.last
|
||||
@mod_action = ModAction.last
|
||||
end
|
||||
|
||||
context "that is then undone" do
|
||||
setup do
|
||||
Timecop.travel(Time.now + PostReplacement::DELETION_GRACE_PERIOD + 1.day) do
|
||||
Delayed::Worker.new.work_off
|
||||
end
|
||||
|
||||
@replacement = @post.replacements.first
|
||||
@replacement.undo!
|
||||
@post.reload
|
||||
end
|
||||
|
||||
should "update the attributes" do
|
||||
assert_equal(272, @post.image_width)
|
||||
assert_equal(92, @post.image_height)
|
||||
assert_equal(5969, @post.file_size)
|
||||
assert_equal("png", @post.file_ext)
|
||||
assert_equal("8f9327db2597fa57d2f42b4a6c5a9855", @post.md5)
|
||||
assert_equal("8f9327db2597fa57d2f42b4a6c5a9855", Digest::MD5.file(@post.file_path).hexdigest)
|
||||
assert_equal("https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png", @post.source)
|
||||
end
|
||||
end
|
||||
|
||||
should "create a post replacement record" do
|
||||
assert_equal(@post.id, PostReplacement.last.post_id)
|
||||
end
|
||||
|
||||
should "correctly update the attributes" do
|
||||
assert_equal(@post.id, @upload.post.id)
|
||||
assert_equal("completed", @upload.status)
|
||||
|
||||
assert_equal(276, @post.image_width)
|
||||
assert_equal(110, @post.image_height)
|
||||
assert_equal(8558, @post.file_size)
|
||||
assert_equal("gif", @post.file_ext)
|
||||
assert_equal("e80d1c59a673f560785784fb1ac10959", @post.md5)
|
||||
assert_equal("e80d1c59a673f560785784fb1ac10959", Digest::MD5.file(@post.file_path).hexdigest)
|
||||
assert_equal("https://www.google.com/intl/en_ALL/images/logo.gif", @post.source)
|
||||
end
|
||||
|
||||
should "not change the post status or uploader" do
|
||||
assert_equal("127.0.0.2", @post.uploader_ip_addr.to_s)
|
||||
assert_equal(@uploader.id, @post.uploader_id)
|
||||
assert_equal(false, @post.is_pending)
|
||||
end
|
||||
|
||||
should "log a mod action" do
|
||||
assert_match(/replaced post ##{@post.id}/, @mod_action.description)
|
||||
end
|
||||
|
||||
should "leave a system comment" do
|
||||
comment = @post.comments.last
|
||||
|
||||
assert_not_nil(comment)
|
||||
assert_equal(User.system.id, comment.creator_id)
|
||||
assert_match(/replaced this post/, comment.body)
|
||||
end
|
||||
|
||||
should "not send an @mention to the replacer" do
|
||||
assert_equal(0, @replacer.dmails.size)
|
||||
end
|
||||
end
|
||||
|
||||
context "a post with notes" do
|
||||
setup do
|
||||
@post.update({image_width: 160, image_height: 164}, without_protection: true)
|
||||
CurrentUser.scoped(@uploader, "127.0.0.1") do
|
||||
@note = @post.notes.create(x: 80, y: 82, width: 80, height: 82, body: "test")
|
||||
end
|
||||
end
|
||||
|
||||
should "rescale the notes" do
|
||||
assert_equal([80, 82, 80, 82], [@note.x, @note.y, @note.width, @note.height])
|
||||
|
||||
assert_difference("@replacer.note_versions.count") do
|
||||
# replacement image is 80x82, so we're downscaling by 50% (160x164 -> 80x82).
|
||||
@post.replace!(replacement_url: "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247350")
|
||||
@note.reload
|
||||
end
|
||||
|
||||
assert_equal([40, 41, 40, 41], [@note.x, @note.y, @note.width, @note.height])
|
||||
end
|
||||
end
|
||||
|
||||
context "a post with a pixiv html source" do
|
||||
should "replace with the full size image" do
|
||||
@post.replace!(replacement_url: "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247350")
|
||||
|
||||
assert_equal(80, @post.image_width)
|
||||
assert_equal(82, @post.image_height)
|
||||
assert_equal(16275, @post.file_size)
|
||||
assert_equal("png", @post.file_ext)
|
||||
assert_equal("4ceadc314938bc27f3574053a3e1459a", @post.md5)
|
||||
assert_equal("4ceadc314938bc27f3574053a3e1459a", Digest::MD5.file(@post.file_path).hexdigest)
|
||||
assert_equal("https://i.pximg.net/img-original/img/2017/04/04/08/54/15/62247350_p0.png", @post.source)
|
||||
end
|
||||
|
||||
should "delete the old files after three days" do
|
||||
old_file_path, old_preview_file_path, old_large_file_path = @post.file_path, @post.preview_file_path, @post.large_file_path
|
||||
@post.replace!(replacement_url: "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247350")
|
||||
|
||||
assert(File.exists?(old_file_path))
|
||||
assert(File.exists?(old_preview_file_path))
|
||||
assert(File.exists?(old_large_file_path))
|
||||
|
||||
Timecop.travel(Time.now + PostReplacement::DELETION_GRACE_PERIOD + 1.day) do
|
||||
Delayed::Worker.new.work_off
|
||||
end
|
||||
|
||||
assert_not(File.exists?(old_file_path))
|
||||
assert_not(File.exists?(old_preview_file_path))
|
||||
assert_not(File.exists?(old_large_file_path))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1588,129 +1588,6 @@ class PostTest < ActiveSupport::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
context "Replacing: " do
|
||||
setup do
|
||||
mock_iqdb_service!
|
||||
Delayed::Worker.delay_jobs = true # don't delete the old images right away
|
||||
Danbooru.config.stubs(:use_s3_proxy?).returns(false) # don't fail on post ids < 10000
|
||||
|
||||
@system = FactoryGirl.create(:user, created_at: 2.weeks.ago)
|
||||
Danbooru.config.stubs(:system_user).returns(@system)
|
||||
|
||||
@uploader = FactoryGirl.create(:user, created_at: 2.weeks.ago, can_upload_free: true)
|
||||
@replacer = FactoryGirl.create(:user, created_at: 2.weeks.ago, can_approve_posts: true)
|
||||
CurrentUser.user = @replacer
|
||||
CurrentUser.ip_addr = "127.0.0.1"
|
||||
|
||||
CurrentUser.scoped(@uploader, "127.0.0.2") do
|
||||
upload = FactoryGirl.create(:jpg_upload, as_pending: "0")
|
||||
upload.process!
|
||||
@post = upload.post
|
||||
end
|
||||
end
|
||||
|
||||
teardown do
|
||||
Delayed::Worker.delay_jobs = false
|
||||
end
|
||||
|
||||
context "replacing a post from a generic source" do
|
||||
setup do
|
||||
@post.update(source: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png")
|
||||
@post.replace!("https://www.google.com/intl/en_ALL/images/logo.gif")
|
||||
@upload = Upload.last
|
||||
@mod_action = ModAction.last
|
||||
end
|
||||
|
||||
context "that is then undone" do
|
||||
setup do
|
||||
Timecop.travel(Time.now + PostReplacement::DELETION_GRACE_PERIOD + 1.day) do
|
||||
Delayed::Worker.new.work_off
|
||||
end
|
||||
|
||||
@replacement = @post.replacements.first
|
||||
@replacement.undo!
|
||||
@post.reload
|
||||
end
|
||||
|
||||
should "update the attributes" do
|
||||
assert_equal(272, @post.image_width)
|
||||
assert_equal(92, @post.image_height)
|
||||
assert_equal(5969, @post.file_size)
|
||||
assert_equal("png", @post.file_ext)
|
||||
assert_equal("8f9327db2597fa57d2f42b4a6c5a9855", @post.md5)
|
||||
assert_equal("8f9327db2597fa57d2f42b4a6c5a9855", Digest::MD5.file(@post.file_path).hexdigest)
|
||||
assert_equal("https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png", @post.source)
|
||||
end
|
||||
end
|
||||
|
||||
should "create a post replacement record" do
|
||||
assert_equal(@post.id, PostReplacement.last.post_id)
|
||||
end
|
||||
|
||||
should "correctly update the attributes" do
|
||||
assert_equal(@post.id, @upload.post.id)
|
||||
assert_equal("completed", @upload.status)
|
||||
|
||||
assert_equal(276, @post.image_width)
|
||||
assert_equal(110, @post.image_height)
|
||||
assert_equal(8558, @post.file_size)
|
||||
assert_equal("gif", @post.file_ext)
|
||||
assert_equal("e80d1c59a673f560785784fb1ac10959", @post.md5)
|
||||
assert_equal("e80d1c59a673f560785784fb1ac10959", Digest::MD5.file(@post.file_path).hexdigest)
|
||||
assert_equal("https://www.google.com/intl/en_ALL/images/logo.gif", @post.source)
|
||||
end
|
||||
|
||||
should "not change the post status or uploader" do
|
||||
assert_equal("127.0.0.2", @post.uploader_ip_addr.to_s)
|
||||
assert_equal(@uploader.id, @post.uploader_id)
|
||||
assert_equal(false, @post.is_pending)
|
||||
end
|
||||
|
||||
should "log a mod action" do
|
||||
assert_match(/replaced post ##{@post.id}/, @mod_action.description)
|
||||
end
|
||||
|
||||
should "leave a system comment" do
|
||||
comment = @post.comments.last
|
||||
|
||||
assert_not_nil(comment)
|
||||
assert_equal(User.system.id, comment.creator_id)
|
||||
assert_match(/@#{@replacer.name} replaced this post/, comment.body)
|
||||
end
|
||||
end
|
||||
|
||||
context "replacing a post with a pixiv html source" do
|
||||
should "replace with the full size image" do
|
||||
@post.replace!("https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247350")
|
||||
|
||||
assert_equal(80, @post.image_width)
|
||||
assert_equal(82, @post.image_height)
|
||||
assert_equal(16275, @post.file_size)
|
||||
assert_equal("png", @post.file_ext)
|
||||
assert_equal("4ceadc314938bc27f3574053a3e1459a", @post.md5)
|
||||
assert_equal("4ceadc314938bc27f3574053a3e1459a", Digest::MD5.file(@post.file_path).hexdigest)
|
||||
assert_equal("https://i.pximg.net/img-original/img/2017/04/04/08/54/15/62247350_p0.png", @post.source)
|
||||
end
|
||||
|
||||
should "delete the old files after three days" do
|
||||
old_file_path, old_preview_file_path, old_large_file_path = @post.file_path, @post.preview_file_path, @post.large_file_path
|
||||
@post.replace!("https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247350")
|
||||
|
||||
assert(File.exists?(old_file_path))
|
||||
assert(File.exists?(old_preview_file_path))
|
||||
assert(File.exists?(old_large_file_path))
|
||||
|
||||
Timecop.travel(Time.now + PostReplacement::DELETION_GRACE_PERIOD + 1.day) do
|
||||
Delayed::Worker.new.work_off
|
||||
end
|
||||
|
||||
assert_not(File.exists?(old_file_path))
|
||||
assert_not(File.exists?(old_preview_file_path))
|
||||
assert_not(File.exists?(old_large_file_path))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "Searching:" do
|
||||
setup do
|
||||
mock_pool_archive_service!
|
||||
|
||||
Reference in New Issue
Block a user