diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 109f840ad..8d4b4564c 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -67,12 +67,17 @@ class PostsController < ApplicationController @post = authorize Post.new_from_upload(permitted_attributes(Post)) @post.save - if @post.errors.any? + if @post.errors.none? + respond_with(@post) + elsif @post.errors.of_kind?(:md5, :taken) + @original_post = Post.find_by!(md5: @post.md5) + @original_post.update(rating: @post.rating, parent_id: @post.parent_id, tag_string: "#{@original_post.tag_string} #{@post.tag_string}") + flash[:notice] = "Duplicate of post ##{@original_post.id}; merging tags" + redirect_to @original_post + else @upload = UploadMediaAsset.find(params[:post][:upload_media_asset_id]).upload flash[:notice] = @post.errors.full_messages.join("; ") respond_with(@post, render: { template: "uploads/show" }) - else - respond_with(@post) end end diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index 67beb3c70..6f3a47ec0 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -38,6 +38,12 @@ class UploadsController < ApplicationController def show @upload = authorize Upload.find(params[:id]) @post = Post.new(uploader: @upload.uploader, uploader_ip_addr: @upload.uploader_ip_addr, source: @upload.source, rating: nil, **permitted_attributes(Post)) - respond_with(@upload) + + if request.format.html? && @upload.is_completed? && @upload.media_assets.first&.post.present? + flash[:notice] = "Duplicate of post ##{@upload.media_assets.first.post.id}" + redirect_to @upload.media_assets.first.post + else + respond_with(@upload) + end end end diff --git a/app/models/media_asset.rb b/app/models/media_asset.rb index 1987d182a..a2703832f 100644 --- a/app/models/media_asset.rb +++ b/app/models/media_asset.rb @@ -12,6 +12,7 @@ class MediaAsset < ApplicationRecord LARGE_IMAGE_WIDTH = Danbooru.config.large_image_width STORAGE_SERVICE = Danbooru.config.storage_manager + has_one :post, foreign_key: :md5, primary_key: :md5 has_one :media_metadata, dependent: :destroy has_one :pixiv_ugoira_frame_data, class_name: "PixivUgoiraFrameData", foreign_key: :md5, primary_key: :md5 diff --git a/test/functional/posts_controller_test.rb b/test/functional/posts_controller_test.rb index 43a32a51a..b53c85f57 100644 --- a/test/functional/posts_controller_test.rb +++ b/test/functional/posts_controller_test.rb @@ -5,9 +5,9 @@ class PostsControllerTest < ActionDispatch::IntegrationTest assert_equal(expected, response.parsed_body.css("link[rel=canonical]").attribute("href").value) end - def create_post!(user: create(:user), rating: "q", tag_string: "tagme", **params) + def create_post!(user: create(:user), media_asset: build(:media_asset), rating: "q", tag_string: "tagme", **params) upload = build(:upload, uploader: user) - asset = create(:upload_media_asset, upload: upload) + asset = create(:upload_media_asset, upload: upload, media_asset: media_asset) post_auth posts_path, user, params: { post: { upload_media_asset_id: asset.id, rating: rating, tag_string: tag_string, **params }} Post.last @@ -655,6 +655,21 @@ class PostsControllerTest < ActionDispatch::IntegrationTest assert_equal("test", @post.tag_string) end + should "re-render the upload page if the upload fails" do + @post = create_post!(rating: "z", tag_string: "tagme") + assert_response :success + end + + should "merge the tags and redirect to the original post if the upload is a duplicate of an existing post" do + media_asset = create(:media_asset) + post1 = create_post!(rating: "s", tag_string: "post1", media_asset: media_asset) + post2 = create_post!(rating: "e", tag_string: "post2", media_asset: media_asset) + + assert_redirected_to post1 + assert_equal("post1 post2", post1.reload.tag_string) + assert_equal("e", post1.rating) + end + should "apply the rating from the tags" do @post = create_post!(rating: nil, tag_string: "rating:s") diff --git a/test/functional/uploads_controller_test.rb b/test/functional/uploads_controller_test.rb index 4961a754a..a1d0fdd7c 100644 --- a/test/functional/uploads_controller_test.rb +++ b/test/functional/uploads_controller_test.rb @@ -130,6 +130,14 @@ class UploadsControllerTest < ActionDispatch::IntegrationTest assert_response :success end + + should "redirect a completed upload to the original post if it's a duplicate of an existing post" do + @upload = create(:completed_file_upload, uploader: @user) + @post = create(:post, md5: @upload.media_assets.first.md5, media_asset: @upload.media_assets.first) + get_auth upload_path(@upload), @user + + assert_redirected_to @post + end end context "create action" do diff --git a/test/unit/upload_service_test.rb b/test/unit/upload_service_test.rb index 7d685a220..ab7ba85a6 100644 --- a/test/unit/upload_service_test.rb +++ b/test/unit/upload_service_test.rb @@ -311,27 +311,6 @@ class UploadServiceTest < ActiveSupport::TestCase CurrentUser.ip_addr = nil end - context "with a preprocessed predecessor" do - setup do - @predecessor = FactoryBot.create(:source_upload, status: "preprocessed", source: @source, image_height: 0, image_width: 0, file_size: 1, md5: 'd34e4cf0a437a5d65f8e82b7bcd02606', file_ext: "jpg") - @tags = 'hello world' - end - - context "when the file has already been uploaded" do - setup do - @asset = MediaAsset.find_by_md5("d34e4cf0a437a5d65f8e82b7bcd02606") - @post = create(:post, md5: "d34e4cf0a437a5d65f8e82b7bcd02606", media_asset: @asset) - @service = subject.new(source: @source) - end - - should "point to the dup post in the upload" do - @upload = subject.new(source: @source, tag_string: @tags).start! - @predecessor.reload - assert_equal("error: ActiveRecord::RecordInvalid - Validation failed: Md5 duplicate: #{@post.id}", @predecessor.status) - end - end - end - context "with a source containing unicode characters" do should "normalize unicode characters in the source field" do source1 = "poke\u0301mon" # pokémon (nfd form)