about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--app/models/user.rb30
-rw-r--r--app/views/user_mailer/email_changed.html.haml2
-rw-r--r--app/views/user_mailer/email_changed.text.erb2
3 files changed, 31 insertions, 3 deletions
diff --git a/app/models/user.rb b/app/models/user.rb
index 85ee5cd06..ae0cdf29d 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 7e91e87ad..96be8624d 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 345b16a2c..2b58415f5 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' %>