mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-30 18:47:51 +00:00 
			
		
		
		
	chore: Update account deletion email copy (#12317)
Update the email copies for account deletion emails ## Manual Deletion <img width="1378" height="678" alt="Screenshot 2025-08-29 at 2 41 40 PM" src="https://github.com/user-attachments/assets/63d7ad97-ad51-4a8b-9ef3-d427fa467f8a" /> ## Inactive Deletion <img width="2946" height="1808" alt="image" src="https://github.com/user-attachments/assets/bb50d08c-8701-4f93-af29-0b1d948a4009" /> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Sojan Jose <sojan@pepalo.com>
This commit is contained in:
		| @@ -1,10 +1,22 @@ | |||||||
| class AdministratorNotifications::AccountNotificationMailer < AdministratorNotifications::BaseMailer | class AdministratorNotifications::AccountNotificationMailer < AdministratorNotifications::BaseMailer | ||||||
|   def account_deletion(account, reason = 'manual_deletion') |   def account_deletion_user_initiated(account, reason) | ||||||
|     subject = 'Your account has been marked for deletion' |     subject = 'Your Chatwoot account deletion has been scheduled' | ||||||
|     action_url = settings_url('general') |     action_url = settings_url('general') | ||||||
|     meta = { |     meta = { | ||||||
|       'account_name' => account.name, |       'account_name' => account.name, | ||||||
|       'deletion_date' => account.custom_attributes['marked_for_deletion_at'], |       'deletion_date' => format_deletion_date(account.custom_attributes['marked_for_deletion_at']), | ||||||
|  |       'reason' => reason | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     send_notification(subject, action_url: action_url, meta: meta) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def account_deletion_for_inactivity(account, reason) | ||||||
|  |     subject = 'Your Chatwoot account is scheduled for deletion due to inactivity' | ||||||
|  |     action_url = settings_url('general') | ||||||
|  |     meta = { | ||||||
|  |       'account_name' => account.name, | ||||||
|  |       'deletion_date' => format_deletion_date(account.custom_attributes['marked_for_deletion_at']), | ||||||
|       'reason' => reason |       'reason' => reason | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -45,4 +57,14 @@ class AdministratorNotifications::AccountNotificationMailer < AdministratorNotif | |||||||
|  |  | ||||||
|     send_notification(subject, action_url: action_url, meta: meta) |     send_notification(subject, action_url: action_url, meta: meta) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   private | ||||||
|  |  | ||||||
|  |   def format_deletion_date(deletion_date_str) | ||||||
|  |     return 'Unknown' if deletion_date_str.blank? | ||||||
|  |  | ||||||
|  |     Time.zone.parse(deletion_date_str).strftime('%B %d, %Y') | ||||||
|  |   rescue StandardError | ||||||
|  |     'Unknown' | ||||||
|  |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -1,16 +0,0 @@ | |||||||
| <p>Hello,</p> |  | ||||||
|  |  | ||||||
| <p>Your account <strong>{{ meta.account_name }}</strong> has been marked for deletion. The account will be permanently deleted on <strong>{{ meta.deletion_date }}</strong>.</p> |  | ||||||
|  |  | ||||||
| {% if meta.reason == 'manual_deletion' %} |  | ||||||
| <p>This action was requested by one of the administrators of your account.</p> |  | ||||||
| {% else %} |  | ||||||
| <p>Reason for deletion: {{ meta.reason }}</p> |  | ||||||
| {% endif %} |  | ||||||
|  |  | ||||||
| <p>If this was done in error, you can cancel the deletion process by visiting your account settings.</p> |  | ||||||
|  |  | ||||||
| <p><a href="{{ action_url }}">Cancel Account Deletion</a></p> |  | ||||||
|  |  | ||||||
| <p>Thank you,<br> |  | ||||||
| Team Chatwoot</p>  |  | ||||||
| @@ -0,0 +1,21 @@ | |||||||
|  | <p>Hello there,</p> | ||||||
|  |  | ||||||
|  | <p>We've noticed that your Chatwoot account <strong>{{ meta.account_name }}</strong> has been inactive for some time. Because of this, it's scheduled for deletion on <strong>{{ meta.deletion_date }}</strong>.</p> | ||||||
|  |  | ||||||
|  | <p><strong>How do I keep my account?</strong></p> | ||||||
|  |  | ||||||
|  | <p>Log in to your Chatwoot account before <strong>{{ meta.deletion_date }}</strong>. From your account settings, you can <a href="{{ action_url }}">cancel the deletion</a> and continue using your account.</p> | ||||||
|  |  | ||||||
|  | <p><strong>What happens if I don't cancel?</strong></p> | ||||||
|  |  | ||||||
|  | <p>Unless you cancel the account deletion before <strong>{{ meta.deletion_date }}</strong>, your account and all associated data — including conversations, contacts, reports, and settings — will be permanently deleted.</p> | ||||||
|  |  | ||||||
|  | <p><strong>Why are we doing this?</strong></p> | ||||||
|  |  | ||||||
|  | <p>To keep things secure and efficient, we regularly remove inactive accounts so our systems remain optimized for active teams.</p> | ||||||
|  |  | ||||||
|  | <p>If you have any questions, feel free to reach us at <a href="mailto:hello@chatwoot.com">hello@chatwoot.com</a>.</p> | ||||||
|  |  | ||||||
|  | <p>— The Chatwoot Team</p> | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -0,0 +1,16 @@ | |||||||
|  | <p>Hello there,</p> | ||||||
|  |  | ||||||
|  | <p>An account administrator has requested deletion of the Chatwoot account <strong>{{ meta.account_name }}</strong>. The account is scheduled for deletion on <strong>{{ meta.deletion_date }}</strong>.</p> | ||||||
|  |  | ||||||
|  | <p><strong>What happens next?</strong></p> | ||||||
|  |  | ||||||
|  | <ul> | ||||||
|  |   <li>The account will remain accessible until the scheduled deletion date.</li> | ||||||
|  |   <li>After that, all data including conversations, contacts, integrations, and settings will be permanently removed.</li> | ||||||
|  |   </ul> | ||||||
|  |  | ||||||
|  | <p>If you change your mind before the deletion date, you can <a href="{{ action_url }}">cancel this request</a> by visiting your account settings.</p> | ||||||
|  |  | ||||||
|  | <p>— The Chatwoot Team</p> | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -4,10 +4,22 @@ module Enterprise::Account | |||||||
|   def manually_managed_features; end |   def manually_managed_features; end | ||||||
|  |  | ||||||
|   def mark_for_deletion(reason = 'manual_deletion') |   def mark_for_deletion(reason = 'manual_deletion') | ||||||
|     result = custom_attributes.merge!('marked_for_deletion_at' => 7.days.from_now.iso8601, 'marked_for_deletion_reason' => reason) && save |     reason = reason.to_s == 'manual_deletion' ? 'manual_deletion' : 'inactivity' | ||||||
|  |  | ||||||
|  |     result = custom_attributes.merge!( | ||||||
|  |       'marked_for_deletion_at' => 7.days.from_now.iso8601, | ||||||
|  |       'marked_for_deletion_reason' => reason | ||||||
|  |     ) && save | ||||||
|  |  | ||||||
|     # Send notification to admin users if the account was successfully marked for deletion |     # Send notification to admin users if the account was successfully marked for deletion | ||||||
|     AdministratorNotifications::AccountNotificationMailer.with(account: self).account_deletion(self, reason).deliver_later if result |     if result | ||||||
|  |       mailer = AdministratorNotifications::AccountNotificationMailer.with(account: self) | ||||||
|  |       if reason == 'manual_deletion' | ||||||
|  |         mailer.account_deletion_user_initiated(self, reason).deliver_later | ||||||
|  |       else | ||||||
|  |         mailer.account_deletion_for_inactivity(self, reason).deliver_later | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|     result |     result | ||||||
|   end |   end | ||||||
|   | |||||||
| @@ -229,18 +229,27 @@ RSpec.describe Account, type: :model do | |||||||
|     describe '#mark_for_deletion' do |     describe '#mark_for_deletion' do | ||||||
|       it 'sets the marked_for_deletion_at and marked_for_deletion_reason attributes' do |       it 'sets the marked_for_deletion_at and marked_for_deletion_reason attributes' do | ||||||
|         expect do |         expect do | ||||||
|           account.mark_for_deletion('test_reason') |           account.mark_for_deletion('inactivity') | ||||||
|         end.to change { account.reload.custom_attributes['marked_for_deletion_at'] }.from(nil).to(be_present) |         end.to change { account.reload.custom_attributes['marked_for_deletion_at'] }.from(nil).to(be_present) | ||||||
|            .and change { account.reload.custom_attributes['marked_for_deletion_reason'] }.from(nil).to('test_reason') |            .and change { account.reload.custom_attributes['marked_for_deletion_reason'] }.from(nil).to('inactivity') | ||||||
|       end |       end | ||||||
|  |  | ||||||
|       it 'sends a notification email to admin users' do |       it 'sends a user-initiated deletion email when reason is manual_deletion' do | ||||||
|         mailer = double |         mailer = double | ||||||
|         expect(AdministratorNotifications::AccountNotificationMailer).to receive(:with).with(account: account).and_return(mailer) |         expect(AdministratorNotifications::AccountNotificationMailer).to receive(:with).with(account: account).and_return(mailer) | ||||||
|         expect(mailer).to receive(:account_deletion).with(account, 'test_reason').and_return(mailer) |         expect(mailer).to receive(:account_deletion_user_initiated).with(account, 'manual_deletion').and_return(mailer) | ||||||
|         expect(mailer).to receive(:deliver_later) |         expect(mailer).to receive(:deliver_later) | ||||||
|  |  | ||||||
|         account.mark_for_deletion('test_reason') |         account.mark_for_deletion('manual_deletion') | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       it 'sends a system-initiated deletion email when reason is not manual_deletion' do | ||||||
|  |         mailer = double | ||||||
|  |         expect(AdministratorNotifications::AccountNotificationMailer).to receive(:with).with(account: account).and_return(mailer) | ||||||
|  |         expect(mailer).to receive(:account_deletion_for_inactivity).with(account, 'inactivity').and_return(mailer) | ||||||
|  |         expect(mailer).to receive(:deliver_later) | ||||||
|  |  | ||||||
|  |         account.mark_for_deletion('inactivity') | ||||||
|       end |       end | ||||||
|  |  | ||||||
|       it 'returns true when successful' do |       it 'returns true when successful' do | ||||||
|   | |||||||
| @@ -1,116 +1,46 @@ | |||||||
| require 'rails_helper' | require 'rails_helper' | ||||||
| require Rails.root.join 'spec/mailers/administrator_notifications/shared/smtp_config_shared.rb' |  | ||||||
|  |  | ||||||
| RSpec.describe AdministratorNotifications::AccountNotificationMailer do | RSpec.describe AdministratorNotifications::AccountNotificationMailer do | ||||||
|   include_context 'with smtp config' |   let(:account) { create(:account, name: 'Test Account') } | ||||||
|  |   let(:mailer) { described_class.with(account: account) } | ||||||
|  |   let(:class_instance) { described_class.new } | ||||||
|  |  | ||||||
|   let!(:account) { create(:account) } |   before do | ||||||
|   let!(:admin) { create(:user, account: account, role: :administrator) } |     allow(described_class).to receive(:new).and_return(class_instance) | ||||||
|  |     allow(class_instance).to receive(:smtp_config_set_or_development?).and_return(true) | ||||||
|  |     account.custom_attributes['marked_for_deletion_at'] = 7.days.from_now.iso8601 | ||||||
|  |     account.save! | ||||||
|  |   end | ||||||
|  |  | ||||||
|   describe 'account_deletion' do |   describe '#account_deletion_user_initiated' do | ||||||
|     let(:reason) { 'manual_deletion' } |     it 'sets the correct subject for user-initiated deletion' do | ||||||
|     let(:mail) { described_class.with(account: account).account_deletion(account, reason) } |       mail = mailer.account_deletion_user_initiated(account, 'manual_deletion') | ||||||
|     let(:deletion_date) { 7.days.from_now.iso8601 } |       expect(mail.subject).to eq('Your Chatwoot account deletion has been scheduled') | ||||||
|  |  | ||||||
|     before do |  | ||||||
|       account.update!(custom_attributes: { |  | ||||||
|                         'marked_for_deletion_at' => deletion_date, |  | ||||||
|                         'marked_for_deletion_reason' => reason |  | ||||||
|                       }) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     it 'renders the subject' do |  | ||||||
|       expect(mail.subject).to eq('Your account has been marked for deletion') |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     it 'renders the receiver email' do |  | ||||||
|       expect(mail.to).to eq([admin.email]) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     it 'includes the account name in the email body' do |  | ||||||
|       expect(mail.body.encoded).to include(account.name) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     it 'includes the deletion date in the email body' do |  | ||||||
|       expect(mail.body.encoded).to include(deletion_date) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     it 'includes a link to cancel the deletion' do |  | ||||||
|       expect(mail.body.encoded).to include('Cancel Account Deletion') |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     context 'when reason is manual_deletion' do |  | ||||||
|       it 'includes the administrator message' do |  | ||||||
|         expect(mail.body.encoded).to include('This action was requested by one of the administrators of your account') |  | ||||||
|       end |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     context 'when reason is not manual_deletion' do |  | ||||||
|       let(:reason) { 'inactivity' } |  | ||||||
|  |  | ||||||
|       it 'includes the reason directly' do |  | ||||||
|         expect(mail.body.encoded).to include('Reason for deletion: inactivity') |  | ||||||
|       end |  | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   describe 'contact_import_complete' do |   describe '#account_deletion_for_inactivity' do | ||||||
|     let!(:data_import) { build(:data_import, total_records: 10, processed_records: 8) } |     it 'sets the correct subject for system-initiated deletion' do | ||||||
|     let(:mail) { described_class.with(account: account).contact_import_complete(data_import).deliver_now } |       mail = mailer.account_deletion_for_inactivity(account, 'Account Inactive') | ||||||
|  |       expect(mail.subject).to eq('Your Chatwoot account is scheduled for deletion due to inactivity') | ||||||
|     it 'renders the subject' do |  | ||||||
|       expect(mail.subject).to eq('Contact Import Completed') |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     it 'renders the processed records' do |  | ||||||
|       expect(mail.body.encoded).to include('Number of records imported: 8') |  | ||||||
|       expect(mail.body.encoded).to include('Number of records failed: 2') |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     it 'renders the receiver email' do |  | ||||||
|       expect(mail.to).to eq([admin.email]) |  | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   describe 'contact_import_failed' do |   describe '#format_deletion_date' do | ||||||
|     let(:mail) { described_class.with(account: account).contact_import_failed.deliver_now } |     it 'formats a valid date string' do | ||||||
|  |       date_str = '2024-12-31T23:59:59Z' | ||||||
|     it 'renders the subject' do |       formatted = described_class.new.send(:format_deletion_date, date_str) | ||||||
|       expect(mail.subject).to eq('Contact Import Failed') |       expect(formatted).to eq('December 31, 2024') | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     it 'renders the receiver email' do |     it 'handles blank dates' do | ||||||
|       expect(mail.to).to eq([admin.email]) |       formatted = described_class.new.send(:format_deletion_date, nil) | ||||||
|     end |       expect(formatted).to eq('Unknown') | ||||||
|   end |  | ||||||
|  |  | ||||||
|   describe 'contact_export_complete' do |  | ||||||
|     let!(:file_url) { 'http://test.com/test' } |  | ||||||
|     let(:mail) { described_class.with(account: account).contact_export_complete(file_url, admin.email).deliver_now } |  | ||||||
|  |  | ||||||
|     it 'renders the subject' do |  | ||||||
|       expect(mail.subject).to eq("Your contact's export file is available to download.") |  | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     it 'renders the receiver email' do |     it 'handles invalid dates' do | ||||||
|       expect(mail.to).to eq([admin.email]) |       formatted = described_class.new.send(:format_deletion_date, 'invalid-date') | ||||||
|     end |       expect(formatted).to eq('Unknown') | ||||||
|   end |  | ||||||
|  |  | ||||||
|   describe 'automation_rule_disabled' do |  | ||||||
|     let(:rule) { instance_double(AutomationRule, name: 'Test Rule') } |  | ||||||
|     let(:mail) { described_class.with(account: account).automation_rule_disabled(rule).deliver_now } |  | ||||||
|  |  | ||||||
|     it 'renders the subject' do |  | ||||||
|       expect(mail.subject).to eq('Automation rule disabled due to validation errors.') |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     it 'renders the receiver email' do |  | ||||||
|       expect(mail.to).to eq([admin.email]) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     it 'includes the rule name in the email body' do |  | ||||||
|       expect(mail.body.encoded).to include('Test Rule') |  | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Pranav
					Pranav