From eeebde222fb7de500c5fd5e454c5e7390d033787 Mon Sep 17 00:00:00 2001 From: Splines Date: Mon, 17 Jun 2024 20:40:15 +0200 Subject: [PATCH] Destroy talk media upon user deletion --- app/models/user.rb | 17 +++++++++++ spec/models/user_spec.rb | 65 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/app/models/user.rb b/app/models/user.rb index 4b612f20e..d50f4a158 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -99,6 +99,11 @@ class User < ApplicationRecord after_create :set_consented_at before_destroy :destroy_single_submissions, prepend: true + attr_accessor :skip_destroy_talk_media + + before_destroy :destroy_talk_media_upon_user_deletion, prepend: true, + unless: :skip_destroy_talk_media + # users can comment stuff acts_as_commontator @@ -704,6 +709,7 @@ def archive_and_destroy(archive_name) success = transfer_contributions_to(archive_user(archive_name)) return false unless success end + self.skip_destroy_talk_media = true destroy end @@ -826,6 +832,17 @@ def destroy_single_submissions .map(&:id)).destroy_all end + # Destroys all talk media of the user. + # If the user is an editor of media other than talk-related media, + # nothing will happen. + def destroy_talk_media_upon_user_deletion + return if edited_media.where.not(teachable_type: "Talk").any? + + # Only delete media where the user is the sole editor. + sole_editor_media = edited_media.select { |m| m.editors.count == 1 } + Medium.where(id: sole_editor_media.pluck(:id)).destroy_all + end + def archive_email splitting = DefaultSetting::PROJECT_EMAIL.split("@") "#{splitting.first}-archive-#{id}@#{splitting.second}" diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 6675cbb53..27dc503a4 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -99,4 +99,69 @@ # end # end # end + + describe("#destroy_talk_media") do + let(:medium) do + medium = FactoryBot.build(:medium) + medium.teachable_type = "Lecture" + medium.save(validate: false) + medium + end + + let(:medium_talk) do + medium = FactoryBot.build(:medium) + medium.teachable_type = "Talk" + medium.save(validate: false) + medium + end + + context("when user is destroyed") do + it("method is called") do + user = FactoryBot.create(:confirmed_user) + expect(user).to receive(:destroy_talk_media_upon_user_deletion) + user.destroy + end + + context("when user self-deletes (archive & destroy)") do + it("method is not called") do + user = FactoryBot.create(:confirmed_user) + expect(user).not_to receive(:destroy_talk_media_upon_user_deletion) + user.archive_and_destroy("dummy archive user name") + end + end + end + + context("when user has media other than talk media") do + it("does not destroy any media") do + user = FactoryBot.create(:confirmed_user) + medium + medium_talk + + # use .send(:...) to access private method + expect { user.send(:destroy_talk_media_upon_user_deletion) } + .not_to(change { Medium.count }) + end + end + + context("when user has only talk media") do + it("does not destroy talk media that has multiple editors") do + user = FactoryBot.create(:confirmed_user) + user2 = FactoryBot.create(:confirmed_user) + medium_talk.editors << user + medium_talk.editors << user2 + + expect { user.send(:destroy_talk_media_upon_user_deletion) } + .not_to(change { Medium.count }) + end + + it("destroys talk media that has only one editor") do + user = FactoryBot.create(:confirmed_user) + medium_talk.editors << user + + expect { user.send(:destroy_talk_media_upon_user_deletion) } + .to(change { Medium.count }.by(-1) + .and(change { Medium.exists?(medium_talk.id) }.from(true).to(false))) + end + end + end end