Commit 96dded3ec8401e9832b3888338f37c846bd43583

Authored by Pierre de La Morinerie
1 parent 57cb1ca7

Send emails from the author

This changes the email "From" field from "gitlab@example.com" to either:

* "John Doe <gitlab@example.com>" if the author of the action is known,
* "GitLab <gitlab@example.com>" otherwise.

Rationale: this allow mails to appear as if they were sent by the
author. It appears in the mailbox more like a real discussion between
the sender and the receiver ("John sent: we should refactor this") and
less like a robot notifying about something.
app/mailers/emails/issues.rb
... ... @@ -3,15 +3,17 @@ module Emails
3 3 def new_issue_email(recipient_id, issue_id)
4 4 @issue = Issue.find(issue_id)
5 5 @project = @issue.project
6   - mail(to: recipient(recipient_id),
  6 + mail(from: sender(@issue.author_id),
  7 + to: recipient(recipient_id),
7 8 subject: subject("#{@issue.title} (##{@issue.iid})"))
8 9 end
9 10  
10   - def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id)
  11 + def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id, updated_by_user_id)
11 12 @issue = Issue.find(issue_id)
12 13 @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
13 14 @project = @issue.project
14   - mail(to: recipient(recipient_id),
  15 + mail(from: sender(updated_by_user_id),
  16 + to: recipient(recipient_id),
15 17 subject: subject("#{@issue.title} (##{@issue.iid})"))
16 18 end
17 19  
... ... @@ -19,7 +21,8 @@ module Emails
19 21 @issue = Issue.find issue_id
20 22 @project = @issue.project
21 23 @updated_by = User.find updated_by_user_id
22   - mail(to: recipient(recipient_id),
  24 + mail(from: sender(updated_by_user_id),
  25 + to: recipient(recipient_id),
23 26 subject: subject("#{@issue.title} (##{@issue.iid})"))
24 27 end
25 28  
... ... @@ -28,7 +31,8 @@ module Emails
28 31 @issue_status = status
29 32 @project = @issue.project
30 33 @updated_by = User.find updated_by_user_id
31   - mail(to: recipient(recipient_id),
  34 + mail(from: sender(updated_by_user_id),
  35 + to: recipient(recipient_id),
32 36 subject: subject("#{@issue.title} (##{@issue.iid})"))
33 37 end
34 38 end
... ...
app/mailers/emails/merge_requests.rb
... ... @@ -3,15 +3,17 @@ module Emails
3 3 def new_merge_request_email(recipient_id, merge_request_id)
4 4 @merge_request = MergeRequest.find(merge_request_id)
5 5 @project = @merge_request.project
6   - mail(to: recipient(recipient_id),
  6 + mail(from: sender(@merge_request.author_id),
  7 + to: recipient(recipient_id),
7 8 subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
8 9 end
9 10  
10   - def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id)
  11 + def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, updated_by_user_id)
11 12 @merge_request = MergeRequest.find(merge_request_id)
12 13 @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
13 14 @project = @merge_request.project
14   - mail(to: recipient(recipient_id),
  15 + mail(from: sender(updated_by_user_id),
  16 + to: recipient(recipient_id),
15 17 subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
16 18 end
17 19  
... ... @@ -19,14 +21,16 @@ module Emails
19 21 @merge_request = MergeRequest.find(merge_request_id)
20 22 @updated_by = User.find updated_by_user_id
21 23 @project = @merge_request.project
22   - mail(to: recipient(recipient_id),
  24 + mail(from: sender(updated_by_user_id),
  25 + to: recipient(recipient_id),
23 26 subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
24 27 end
25 28  
26 29 def merged_merge_request_email(recipient_id, merge_request_id)
27 30 @merge_request = MergeRequest.find(merge_request_id)
28 31 @project = @merge_request.project
29   - mail(to: recipient(recipient_id),
  32 + mail(from: sender(@merge_request.author_id_of_changes),
  33 + to: recipient(recipient_id),
30 34 subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
31 35 end
32 36 end
... ...
app/mailers/emails/notes.rb
... ... @@ -4,7 +4,8 @@ module Emails
4 4 @note = Note.find(note_id)
5 5 @commit = @note.noteable
6 6 @project = @note.project
7   - mail(to: recipient(recipient_id),
  7 + mail(from: sender(@note.author_id),
  8 + to: recipient(recipient_id),
8 9 subject: subject("#{@commit.title} (#{@commit.short_id})"))
9 10 end
10 11  
... ... @@ -12,7 +13,8 @@ module Emails
12 13 @note = Note.find(note_id)
13 14 @issue = @note.noteable
14 15 @project = @note.project
15   - mail(to: recipient(recipient_id),
  16 + mail(from: sender(@note.author_id),
  17 + to: recipient(recipient_id),
16 18 subject: subject("#{@issue.title} (##{@issue.iid})"))
17 19 end
18 20  
... ... @@ -20,14 +22,16 @@ module Emails
20 22 @note = Note.find(note_id)
21 23 @merge_request = @note.noteable
22 24 @project = @note.project
23   - mail(to: recipient(recipient_id),
  25 + mail(from: sender(@note.author_id),
  26 + to: recipient(recipient_id),
24 27 subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
25 28 end
26 29  
27 30 def note_wall_email(recipient_id, note_id)
28 31 @note = Note.find(note_id)
29 32 @project = @note.project
30   - mail(to: recipient(recipient_id),
  33 + mail(from: sender(@note.author_id),
  34 + to: recipient(recipient_id),
31 35 subject: subject("Note on wall"))
32 36 end
33 37 end
... ...
app/mailers/emails/projects.rb
... ... @@ -22,7 +22,9 @@ module Emails
22 22 @diffs = compare.diffs
23 23 @branch = branch
24 24  
25   - mail(to: recipient, subject: subject("New push to repository"))
  25 + mail(from: sender(author_id),
  26 + to: recipient,
  27 + subject: subject("New push to repository"))
26 28 end
27 29 end
28 30 end
... ...
app/mailers/notify.rb
... ... @@ -15,7 +15,7 @@ class Notify &lt; ActionMailer::Base
15 15 default_url_options[:port] = Gitlab.config.gitlab.port unless Gitlab.config.gitlab_on_standard_port?
16 16 default_url_options[:script_name] = Gitlab.config.gitlab.relative_url_root
17 17  
18   - default from: Gitlab.config.gitlab.email_from
  18 + default from: Proc.new { default_sender_address.format }
19 19 default reply_to: "noreply@#{Gitlab.config.gitlab.host}"
20 20  
21 21 # Just send email with 2 seconds delay
... ... @@ -25,6 +25,23 @@ class Notify &lt; ActionMailer::Base
25 25  
26 26 private
27 27  
  28 + # The default email address to send emails from
  29 + def default_sender_address
  30 + address = Mail::Address.new(Gitlab.config.gitlab.email_from)
  31 + address.display_name = "GitLab"
  32 + address
  33 + end
  34 +
  35 + # Return an email address that displays the name of the sender.
  36 + # Only the displayed name changes; the actual email address is always the same.
  37 + def sender(sender_id)
  38 + if sender = User.find(sender_id)
  39 + address = default_sender_address
  40 + address.display_name = sender.name
  41 + address.format
  42 + end
  43 + end
  44 +
28 45 # Look up a User by their ID and return their email address
29 46 #
30 47 # recipient_id - User ID
... ...
app/services/notification_service.rb
... ... @@ -257,7 +257,7 @@ class NotificationService
257 257 recipients.delete(current_user)
258 258  
259 259 recipients.each do |recipient|
260   - mailer.send(method, recipient.id, target.id, target.assignee_id_was)
  260 + mailer.send(method, recipient.id, target.id, target.assignee_id_was, current_user.id)
261 261 end
262 262 end
263 263  
... ...
config/initializers/devise.rb
... ... @@ -4,7 +4,7 @@ Devise.setup do |config|
4 4 # ==> Mailer Configuration
5 5 # Configure the e-mail address which will be shown in Devise::Mailer,
6 6 # note that it will be overwritten if you use your own mailer class with default "from" parameter.
7   - config.mailer_sender = Gitlab.config.gitlab.email_from
  7 + config.mailer_sender = "GitLab <#{Gitlab.config.gitlab.email_from}>"
8 8  
9 9  
10 10 # Configure the class responsible to send e-mails.
... ...
spec/mailers/notify_spec.rb
... ... @@ -4,6 +4,7 @@ describe Notify do
4 4 include EmailSpec::Helpers
5 5 include EmailSpec::Matchers
6 6  
  7 + let(:gitlab_sender) { Gitlab.config.gitlab.email_from }
7 8 let(:recipient) { create(:user, email: 'recipient@example.com') }
8 9 let(:project) { create(:project) }
9 10  
... ... @@ -13,12 +14,22 @@ describe Notify do
13 14 end
14 15 end
15 16  
  17 + shared_examples 'an email sent from GitLab' do
  18 + it 'is sent from GitLab' do
  19 + sender = subject.header[:from].addrs[0]
  20 + sender.display_name.should eq('GitLab')
  21 + sender.address.should eq(gitlab_sender)
  22 + end
  23 + end
  24 +
16 25 describe 'for new users, the email' do
17 26 let(:example_site_path) { root_path }
18 27 let(:new_user) { create(:user, email: 'newguy@example.com', created_by_id: 1) }
19 28  
20 29 subject { Notify.new_user_email(new_user.id, new_user.password) }
21 30  
  31 + it_behaves_like 'an email sent from GitLab'
  32 +
22 33 it 'is sent to the new user' do
23 34 should deliver_to new_user.email
24 35 end
... ... @@ -47,6 +58,8 @@ describe Notify do
47 58  
48 59 subject { Notify.new_user_email(new_user.id, new_user.password) }
49 60  
  61 + it_behaves_like 'an email sent from GitLab'
  62 +
50 63 it 'is sent to the new user' do
51 64 should deliver_to new_user.email
52 65 end
... ... @@ -73,6 +86,8 @@ describe Notify do
73 86  
74 87 subject { Notify.new_ssh_key_email(key.id) }
75 88  
  89 + it_behaves_like 'an email sent from GitLab'
  90 +
76 91 it 'is sent to the new user' do
77 92 should deliver_to key.user.email
78 93 end
... ... @@ -114,17 +129,24 @@ describe Notify do
114 129  
115 130 context 'for a project' do
116 131 describe 'items that are assignable, the email' do
  132 + let(:current_user) { create(:user, email: "current@email.com") }
117 133 let(:assignee) { create(:user, email: 'assignee@example.com') }
118 134 let(:previous_assignee) { create(:user, name: 'Previous Assignee') }
119 135  
120 136 shared_examples 'an assignee email' do
  137 + it 'is sent as the author' do
  138 + sender = subject.header[:from].addrs[0]
  139 + sender.display_name.should eq(current_user.name)
  140 + sender.address.should eq(gitlab_sender)
  141 + end
  142 +
121 143 it 'is sent to the assignee' do
122 144 should deliver_to assignee.email
123 145 end
124 146 end
125 147  
126 148 context 'for issues' do
127   - let(:issue) { create(:issue, assignee: assignee, project: project ) }
  149 + let(:issue) { create(:issue, author: current_user, assignee: assignee, project: project ) }
128 150  
129 151 describe 'that are new' do
130 152 subject { Notify.new_issue_email(issue.assignee_id, issue.id) }
... ... @@ -132,7 +154,7 @@ describe Notify do
132 154 it_behaves_like 'an assignee email'
133 155  
134 156 it 'has the correct subject' do
135   - should have_subject /#{project.name} \| New issue ##{issue.iid} \| #{issue.title}/
  157 + should have_subject /#{project.name} \| #{issue.title} \(##{issue.iid}\)/
136 158 end
137 159  
138 160 it 'contains a link to the new issue' do
... ... @@ -141,14 +163,18 @@ describe Notify do
141 163 end
142 164  
143 165 describe 'that have been reassigned' do
144   - before(:each) { issue.stub(:assignee_id_was).and_return(previous_assignee.id) }
145   -
146   - subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id) }
  166 + subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user) }
147 167  
148 168 it_behaves_like 'a multiple recipients email'
149 169  
  170 + it 'is sent as the author' do
  171 + sender = subject.header[:from].addrs[0]
  172 + sender.display_name.should eq(current_user.name)
  173 + sender.address.should eq(gitlab_sender)
  174 + end
  175 +
150 176 it 'has the correct subject' do
151   - should have_subject /Changed issue ##{issue.iid} \| #{issue.title}/
  177 + should have_subject /#{issue.title} \(##{issue.iid}\)/
152 178 end
153 179  
154 180 it 'contains the name of the previous assignee' do
... ... @@ -165,12 +191,17 @@ describe Notify do
165 191 end
166 192  
167 193 describe 'status changed' do
168   - let(:current_user) { create(:user, email: "current@email.com") }
169 194 let(:status) { 'closed' }
170 195 subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) }
171 196  
  197 + it 'is sent as the author' do
  198 + sender = subject.header[:from].addrs[0]
  199 + sender.display_name.should eq(current_user.name)
  200 + sender.address.should eq(gitlab_sender)
  201 + end
  202 +
172 203 it 'has the correct subject' do
173   - should have_subject /Changed issue ##{issue.iid} \| #{issue.title}/i
  204 + should have_subject /#{issue.title} \(##{issue.iid}\)/i
174 205 end
175 206  
176 207 it 'contains the new status' do
... ... @@ -189,7 +220,7 @@ describe Notify do
189 220 end
190 221  
191 222 context 'for merge requests' do
192   - let(:merge_request) { create(:merge_request, assignee: assignee, source_project: project, target_project: project) }
  223 + let(:merge_request) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project) }
193 224  
194 225 describe 'that are new' do
195 226 subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) }
... ... @@ -197,7 +228,7 @@ describe Notify do
197 228 it_behaves_like 'an assignee email'
198 229  
199 230 it 'has the correct subject' do
200   - should have_subject /New merge request ##{merge_request.iid}/
  231 + should have_subject /#{merge_request.title} \(!#{merge_request.iid}\)/
201 232 end
202 233  
203 234 it 'contains a link to the new merge request' do
... ... @@ -214,14 +245,18 @@ describe Notify do
214 245 end
215 246  
216 247 describe 'that are reassigned' do
217   - before(:each) { merge_request.stub(:assignee_id_was).and_return(previous_assignee.id) }
218   -
219   - subject { Notify.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id) }
  248 + subject { Notify.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id) }
220 249  
221 250 it_behaves_like 'a multiple recipients email'
222 251  
  252 + it 'is sent as the author' do
  253 + sender = subject.header[:from].addrs[0]
  254 + sender.display_name.should eq(current_user.name)
  255 + sender.address.should eq(gitlab_sender)
  256 + end
  257 +
223 258 it 'has the correct subject' do
224   - should have_subject /Changed merge request ##{merge_request.iid}/
  259 + should have_subject /#{merge_request.title} \(!#{merge_request.iid}\)/
225 260 end
226 261  
227 262 it 'contains the name of the previous assignee' do
... ... @@ -245,6 +280,8 @@ describe Notify do
245 280 let(:user) { create(:user) }
246 281 subject { Notify.project_was_moved_email(project.id, user.id) }
247 282  
  283 + it_behaves_like 'an email sent from GitLab'
  284 +
248 285 it 'has the correct subject' do
249 286 should have_subject /Project was moved/
250 287 end
... ... @@ -265,6 +302,9 @@ describe Notify do
265 302 project: project,
266 303 user: user) }
267 304 subject { Notify.project_access_granted_email(users_project.id) }
  305 +
  306 + it_behaves_like 'an email sent from GitLab'
  307 +
268 308 it 'has the correct subject' do
269 309 should have_subject /Access to project was granted/
270 310 end
... ... @@ -285,6 +325,12 @@ describe Notify do
285 325 end
286 326  
287 327 shared_examples 'a note email' do
  328 + it 'is sent as the author' do
  329 + sender = subject.header[:from].addrs[0]
  330 + sender.display_name.should eq(note_author.name)
  331 + sender.address.should eq(gitlab_sender)
  332 + end
  333 +
288 334 it 'is sent to the given recipient' do
289 335 should deliver_to recipient.email
290 336 end
... ... @@ -324,7 +370,7 @@ describe Notify do
324 370 it_behaves_like 'a note email'
325 371  
326 372 it 'has the correct subject' do
327   - should have_subject /Note for commit #{commit.short_id}/
  373 + should have_subject /#{commit.title} \(#{commit.short_id}\)/
328 374 end
329 375  
330 376 it 'contains a link to the commit' do
... ... @@ -342,7 +388,7 @@ describe Notify do
342 388 it_behaves_like 'a note email'
343 389  
344 390 it 'has the correct subject' do
345   - should have_subject /Note for merge request ##{merge_request.iid}/
  391 + should have_subject /#{merge_request.title} \(!#{merge_request.iid}\)/
346 392 end
347 393  
348 394 it 'contains a link to the merge request note' do
... ... @@ -360,7 +406,7 @@ describe Notify do
360 406 it_behaves_like 'a note email'
361 407  
362 408 it 'has the correct subject' do
363   - should have_subject /Note for issue ##{issue.iid}/
  409 + should have_subject /#{issue.title} \(##{issue.iid}\)/
364 410 end
365 411  
366 412 it 'contains a link to the issue note' do
... ... @@ -377,6 +423,8 @@ describe Notify do
377 423  
378 424 subject { Notify.group_access_granted_email(membership.id) }
379 425  
  426 + it_behaves_like 'an email sent from GitLab'
  427 +
380 428 it 'has the correct subject' do
381 429 should have_subject /Access to group was granted/
382 430 end
... ... @@ -401,6 +449,8 @@ describe Notify do
401 449  
402 450 subject { ActionMailer::Base.deliveries.last }
403 451  
  452 + it_behaves_like 'an email sent from GitLab'
  453 +
404 454 it 'is sent to the new user' do
405 455 should deliver_to 'new-email@mail.com'
406 456 end
... ... @@ -421,6 +471,12 @@ describe Notify do
421 471  
422 472 subject { Notify.repository_push_email(project.id, 'devs@company.name', user.id, 'master', compare) }
423 473  
  474 + it 'is sent as the author' do
  475 + sender = subject.header[:from].addrs[0]
  476 + sender.display_name.should eq(user.name)
  477 + sender.address.should eq(gitlab_sender)
  478 + end
  479 +
424 480 it 'is sent to recipient' do
425 481 should deliver_to 'devs@company.name'
426 482 end
... ...
spec/services/notification_service_spec.rb
... ... @@ -137,11 +137,11 @@ describe NotificationService do
137 137 end
138 138  
139 139 def should_email(user_id)
140   - Notify.should_receive(:reassigned_issue_email).with(user_id, issue.id, issue.assignee_id)
  140 + Notify.should_receive(:reassigned_issue_email).with(user_id, issue.id, issue.assignee_id, @u_disabled.id)
141 141 end
142 142  
143 143 def should_not_email(user_id)
144   - Notify.should_not_receive(:reassigned_issue_email).with(user_id, issue.id, issue.assignee_id)
  144 + Notify.should_not_receive(:reassigned_issue_email).with(user_id, issue.id, issue.assignee_id, @u_disabled.id)
145 145 end
146 146 end
147 147  
... ... @@ -201,11 +201,11 @@ describe NotificationService do
201 201 end
202 202  
203 203 def should_email(user_id)
204   - Notify.should_receive(:reassigned_merge_request_email).with(user_id, merge_request.id, merge_request.assignee_id)
  204 + Notify.should_receive(:reassigned_merge_request_email).with(user_id, merge_request.id, merge_request.assignee_id, merge_request.author_id)
205 205 end
206 206  
207 207 def should_not_email(user_id)
208   - Notify.should_not_receive(:reassigned_merge_request_email).with(user_id, merge_request.id, merge_request.assignee_id)
  208 + Notify.should_not_receive(:reassigned_merge_request_email).with(user_id, merge_request.id, merge_request.assignee_id, merge_request.author_id)
209 209 end
210 210 end
211 211  
... ...