FIX: Reclaim votes when voting is disabled on a category

FIX: Reclaim votes when voting is disabled on a category

diff --git a/db/migrate/20190718152804_reclaim_from_disabled_category.rb b/db/migrate/20190718152804_reclaim_from_disabled_category.rb
new file mode 100644
index 0000000..407edf5
--- /dev/null
+++ b/db/migrate/20190718152804_reclaim_from_disabled_category.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class ReclaimFromDisabledCategory < ActiveRecord::Migration[5.2]
+  def up
+    aliases = {
+      votes: DiscourseVoting::VOTES,
+      votes_archive: DiscourseVoting::VOTES_ARCHIVE,
+      voting_enabled: DiscourseVoting::VOTING_ENABLED
+    }
+
+    # archive votes in non-voting categories
+    DB.exec(<<~SQL, aliases)
+      UPDATE user_custom_fields ucf
+      SET name = :votes_archive
+      FROM topics t
+      WHERE ucf.name = :votes
+      AND t.id::text = ucf.value
+      AND t.category_id NOT IN (
+        SELECT category_id FROM category_custom_fields WHERE name=:voting_enabled AND value='true'
+      )
+    SQL
+  end
+
+  def down
+    raise ActiveRecord::IrreversibleMigration
+  end
+end
diff --git a/plugin.rb b/plugin.rb
index 1e20c85..f4df820 100755
--- a/plugin.rb
+++ b/plugin.rb
@@ -115,11 +115,56 @@ after_initialize do
     end
 
     after_save :reset_voting_cache
+    after_find :track_voting_enabled
+    after_save :reclaim_release_votes
 
     protected
     def reset_voting_cache
       ::Category.reset_voting_cache
     end
+
+    def track_voting_enabled
+      return unless SiteSetting.voting_enabled
+      @old_enable_voting = custom_fields[::DiscourseVoting::VOTING_ENABLED]
+    end
+
+    def reclaim_release_votes
+      return unless SiteSetting.voting_enabled
+
+      aliases = {
+        votes: DiscourseVoting::VOTES,
+        votes_archive: DiscourseVoting::VOTES_ARCHIVE,
+        category_id: id
+      }
+
+      was_enabled = @old_enable_voting
+      is_enabled = custom_fields[::DiscourseVoting::VOTING_ENABLED]
+
+      if !was_enabled && is_enabled
+        # Unarchive all votes in the category
+        DB.exec(<<~SQL, aliases)
+          UPDATE user_custom_fields ucf
+          SET name = :votes
+          FROM topics t
+          WHERE ucf.name = :votes_archive
+          AND NOT t.closed
+          AND NOT t.archived
+          AND t.deleted_at IS NULL
+          AND t.id::text = ucf.value
+          AND t.category_id = :category_id
+        SQL
+      elsif was_enabled && !is_enabled
+        # Archive all votes in category
+        DB.exec(<<~SQL, aliases)
+          UPDATE user_custom_fields ucf
+          SET name = :votes_archive
+          FROM topics t
+          WHERE ucf.name = :votes
+          AND t.id::text = ucf.value
+          AND t.category_id = :category_id
+        SQL
+      end
+    end
   end
 
   require_dependency 'user'
diff --git a/spec/voting_spec.rb b/spec/voting_spec.rb
index c8ac742..325e0c7 100644
--- a/spec/voting_spec.rb
+++ b/spec/voting_spec.rb
@@ -157,4 +157,46 @@ describe DiscourseVoting do
       expect(Jobs::VoteReclaim.jobs.size).to eq(0)
     end
   end
+
+  context "when a category has voting enabled/disabled" do
+    let(:category3) { Fabricate(:category) }
+    let(:topic2) { Fabricate(:topic, category: category3) }
+
+    before do
+      category1.custom_fields["enable_topic_voting"] = true
+      category1.save!
+
+      category2.custom_fields["enable_topic_voting"] = true
+      category2.save!
+
+      category3.custom_fields["enable_topic_voting"] = false
+      category3.save!
+
+      user0.custom_fields[DiscourseVoting::VOTES] = [topic0.id, topic1.id]
+      user0.custom_fields[DiscourseVoting::VOTES_ARCHIVE] = [topic2.id]
+      user0.save!
+    end
+
+    it "reclaims votes when voting is disabled on a category" do
+      category = Category.find(category1.id)
+      category.custom_fields["enable_topic_voting"] = false
+      category.save!
+
+      user0.reload
+
+      expect(user0.custom_fields[DiscourseVoting::VOTES]).to contain_exactly(topic1.id)
+      expect(user0.custom_fields[DiscourseVoting::VOTES_ARCHIVE]).to contain_exactly(topic0.id, topic2.id)
+    end
+
+    it "restores votes when voting is enabled on a category" do
+      category = Category.find(category3.id)
+      category.custom_fields["enable_topic_voting"] = true
+      category.save!
+
+      user0.reload
+
+      expect(user0.custom_fields[DiscourseVoting::VOTES]).to contain_exactly(topic0.id, topic1.id, topic2.id)
+      expect(user0.custom_fields[DiscourseVoting::VOTES_ARCHIVE]).to eq(nil)
+    end
+  end
 end

GitHub sha: fd5c6d91