diff --git a/app/models/user.rb b/app/models/user.rb index 85ee5cd064..ae0cdf29d1 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -98,6 +98,7 @@ class User < ApplicationRecord before_validation :sanitize_languages before_create :set_approved + after_commit :send_pending_devise_notifications # This avoids a deprecation warning from Rails 5.1 # It seems possible that a future release of devise-two-factor will @@ -306,11 +307,38 @@ class User < ApplicationRecord protected def send_devise_notification(notification, *args) - devise_mailer.send(notification, self, *args).deliver_later + # This method can be called in `after_update` and `after_commit` hooks, + # but we must make sure the mailer is actually called *after* commit, + # otherwise it may work on stale data. To do this, figure out if we are + # within a transaction. + if ActiveRecord::Base.connection.current_transaction.try(:records)&.include?(self) + pending_devise_notifications << [notification, args] + else + render_and_send_devise_message(notification, *args) + end end private + def send_pending_devise_notifications + pending_devise_notifications.each do |notification, args| + render_and_send_devise_message(notification, *args) + end + + # Empty the pending notifications array because the + # after_commit hook can be called multiple times which + # could cause multiple emails to be sent. + pending_devise_notifications.clear + end + + def pending_devise_notifications + @pending_devise_notifications ||= [] + end + + def render_and_send_devise_message(notification, *args) + devise_mailer.send(notification, self, *args).deliver_later + end + def set_approved self.approved = open_registrations? || valid_invitation? || external? end diff --git a/app/views/user_mailer/email_changed.html.haml b/app/views/user_mailer/email_changed.html.haml index 7e91e87ad1..96be8624d9 100644 --- a/app/views/user_mailer/email_changed.html.haml +++ b/app/views/user_mailer/email_changed.html.haml @@ -38,7 +38,7 @@ %table.input{ align: 'center', cellspacing: 0, cellpadding: 0 } %tbody %tr - %td= @resource.try(:unconfirmed_email) ? @resource.unconfirmed_email : @resource.email + %td= @resource.unconfirmed_email %table.email-table{ cellspacing: 0, cellpadding: 0 } %tbody diff --git a/app/views/user_mailer/email_changed.text.erb b/app/views/user_mailer/email_changed.text.erb index 345b16a2ce..2b58415f56 100644 --- a/app/views/user_mailer/email_changed.text.erb +++ b/app/views/user_mailer/email_changed.text.erb @@ -4,6 +4,6 @@ <%= t 'devise.mailer.email_changed.explanation' %> -<%= @resource.try(:unconfirmed_email) ? @resource.unconfirmed_email : @resource.email %> +<%= @resource.unconfirmed_email %> <%= t 'devise.mailer.email_changed.extra' %>