]> source.dussan.org Git - redmine.git/commitdiff
Fix missing deletion of custom field attachments upon issue deletion (#38966).
authorGo MAEDA <maeda@farend.jp>
Sat, 26 Oct 2024 02:14:09 +0000 (02:14 +0000)
committerGo MAEDA <maeda@farend.jp>
Sat, 26 Oct 2024 02:14:09 +0000 (02:14 +0000)
Patch by Takenori TAKAKI (user:takenory).

git-svn-id: https://svn.redmine.org/redmine/trunk@23161 e93f8b46-1217-0410-a6f0-8f06a7374b81

db/migrate/20241022095140_remove_orphaned_custom_value_attachments.rb [new file with mode: 0644]
lib/plugins/acts_as_customizable/lib/acts_as_customizable.rb
test/unit/issue_test.rb

diff --git a/db/migrate/20241022095140_remove_orphaned_custom_value_attachments.rb b/db/migrate/20241022095140_remove_orphaned_custom_value_attachments.rb
new file mode 100644 (file)
index 0000000..a598b35
--- /dev/null
@@ -0,0 +1,11 @@
+class RemoveOrphanedCustomValueAttachments < ActiveRecord::Migration[7.2]
+  def up
+    Attachment.where(container_type: 'CustomValue')
+              .where('NOT EXISTS (SELECT 1 FROM custom_values WHERE custom_values.id = attachments.container_id)')
+              .destroy_all
+  end
+
+  def down
+    # no-op
+  end
+end
index a63af9d2a9b4cda54d95a6cf9236ed0453577d94..146fa95c2979325d56fee8b54b6ba24a14fed1eb 100644 (file)
@@ -29,6 +29,7 @@ module Redmine
           return if self.included_modules.include?(Redmine::Acts::Customizable::InstanceMethods)
           cattr_accessor :customizable_options
           self.customizable_options = options
+          before_destroy :store_attachment_custom_value_ids
           has_many :custom_values, lambda {includes(:custom_field)},
                                    :as => :customized,
                                    :inverse_of => :customized,
@@ -38,6 +39,7 @@ module Redmine
           send :include, Redmine::Acts::Customizable::InstanceMethods
           validate :validate_custom_field_values
           after_save :save_custom_field_values
+          after_destroy :destroy_custom_value_attachments
         end
       end
 
@@ -170,6 +172,17 @@ module Redmine
           super
         end
 
+        def store_attachment_custom_value_ids
+          @attachment_custom_value_ids =
+            custom_values.select {|cv| cv.custom_field.field_format == 'attachment'}
+                         .map(&:id)
+        end
+
+        def destroy_custom_value_attachments
+          Attachment.where(:container_id => @attachment_custom_value_ids, :container_type => 'CustomValue')
+                    .destroy_all
+        end
+
         module ClassMethods
         end
       end
index 1ec7ef79c225a49050b0a6eae34c43876d0e1039..5e2e702b1060e1794abb4fbdd2d58e6090c17c4b 100644 (file)
@@ -2190,6 +2190,28 @@ class IssueTest < ActiveSupport::TestCase
     end
   end
 
+  def test_destroy_should_delete_attachments_on_custom_values
+    cf = IssueCustomField.create!(:name => 'Attachable field', :field_format => 'attachment', :is_for_all => true, :tracker_ids => [1])
+    user = User.find(2)
+    issue = Issue.new(:project_id => 1, :tracker_id => 1, :subject => 'test', :author_id => user.id)
+    attachment = Attachment.create!(:container => issue, :file => uploaded_test_file('testfile.txt', 'text/plain'), :author_id => user.id)
+    issue.send(
+      :safe_attributes=,
+      {
+        'custom_fields' =>
+          [
+            {'id' => cf.id.to_s, 'value' => attachment.id.to_s},
+          ]
+      }, user
+    )
+
+    assert_difference 'CustomValue.where(:customized_type => "Issue").count', -(issue.custom_values.count) do
+      assert_difference 'Attachment.count', -1 do
+        issue.destroy
+      end
+    end
+  end
+
   def test_destroying_a_deleted_issue_should_not_raise_an_error
     issue = Issue.find(1)
     Issue.find(1).destroy