Commit c4f9dff480ea030d6878b0197a71670f3a141e0e
Exists in
spb-stable
and in
3 other branches
Merge branch 'simplify-emails-content' into 'master'
Streamline the content of notification emails In notification emails, the actual content of the email is often buried under several blocks of chrome — and may even be truncated or completely missing. Ideally, the notification emails would be like *real emails*: a short message of meaningful text, sent from the author of the change that triggered the notification. This MR includes the following changes to notification emails: * Remove much of the chrome (e.g. the "GitLab" header) * Emphasize the content (no more small, grayed-out content) * Add missing informations to the emails (issue description in "new issue" email, file name in "diff comment" email) * Add a consistent "View in GitLab" link in the footer * The assignee is displayed only if someone is assigned * Fix a rendering bug when viewing emails with [Zimbra](http://www.zimbra.com/) We use these patches at [Capitaine Train](http://www.capitainetrain.com), and it has been a surprisingly big productivity boost for us. 
Showing
22 changed files
with
88 additions
and
90 deletions
Show diff stats
app/mailers/emails/groups.rb
... | ... | @@ -3,7 +3,7 @@ module Emails |
3 | 3 | def group_access_granted_email(user_group_id) |
4 | 4 | @membership = UsersGroup.find(user_group_id) |
5 | 5 | @group = @membership.group |
6 | - | |
6 | + @target_url = group_url(@group) | |
7 | 7 | mail(to: @membership.user.email, |
8 | 8 | subject: subject("Access to group was granted")) |
9 | 9 | end | ... | ... |
app/mailers/emails/issues.rb
... | ... | @@ -3,6 +3,7 @@ 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 | + @target_url = project_issue_url(@project, @issue) | |
6 | 7 | mail(from: sender(@issue.author_id), |
7 | 8 | to: recipient(recipient_id), |
8 | 9 | subject: subject("#{@issue.title} (##{@issue.iid})")) |
... | ... | @@ -12,6 +13,7 @@ module Emails |
12 | 13 | @issue = Issue.find(issue_id) |
13 | 14 | @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id |
14 | 15 | @project = @issue.project |
16 | + @target_url = project_issue_url(@project, @issue) | |
15 | 17 | mail(from: sender(updated_by_user_id), |
16 | 18 | to: recipient(recipient_id), |
17 | 19 | subject: subject("#{@issue.title} (##{@issue.iid})")) |
... | ... | @@ -21,6 +23,7 @@ module Emails |
21 | 23 | @issue = Issue.find issue_id |
22 | 24 | @project = @issue.project |
23 | 25 | @updated_by = User.find updated_by_user_id |
26 | + @target_url = project_issue_url(@project, @issue) | |
24 | 27 | mail(from: sender(updated_by_user_id), |
25 | 28 | to: recipient(recipient_id), |
26 | 29 | subject: subject("#{@issue.title} (##{@issue.iid})")) |
... | ... | @@ -31,6 +34,7 @@ module Emails |
31 | 34 | @issue_status = status |
32 | 35 | @project = @issue.project |
33 | 36 | @updated_by = User.find updated_by_user_id |
37 | + @target_url = project_issue_url(@project, @issue) | |
34 | 38 | mail(from: sender(updated_by_user_id), |
35 | 39 | to: recipient(recipient_id), |
36 | 40 | subject: subject("#{@issue.title} (##{@issue.iid})")) | ... | ... |
app/mailers/emails/merge_requests.rb
... | ... | @@ -3,6 +3,7 @@ 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 | + @target_url = project_merge_request_url(@project, @merge_request) | |
6 | 7 | mail(from: sender(@merge_request.author_id), |
7 | 8 | to: recipient(recipient_id), |
8 | 9 | subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) |
... | ... | @@ -12,6 +13,7 @@ module Emails |
12 | 13 | @merge_request = MergeRequest.find(merge_request_id) |
13 | 14 | @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id |
14 | 15 | @project = @merge_request.project |
16 | + @target_url = project_merge_request_url(@project, @merge_request) | |
15 | 17 | mail(from: sender(updated_by_user_id), |
16 | 18 | to: recipient(recipient_id), |
17 | 19 | subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) |
... | ... | @@ -21,6 +23,7 @@ module Emails |
21 | 23 | @merge_request = MergeRequest.find(merge_request_id) |
22 | 24 | @updated_by = User.find updated_by_user_id |
23 | 25 | @project = @merge_request.project |
26 | + @target_url = project_merge_request_url(@project, @merge_request) | |
24 | 27 | mail(from: sender(updated_by_user_id), |
25 | 28 | to: recipient(recipient_id), |
26 | 29 | subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) |
... | ... | @@ -29,6 +32,7 @@ module Emails |
29 | 32 | def merged_merge_request_email(recipient_id, merge_request_id) |
30 | 33 | @merge_request = MergeRequest.find(merge_request_id) |
31 | 34 | @project = @merge_request.project |
35 | + @target_url = project_merge_request_url(@project, @merge_request) | |
32 | 36 | mail(from: sender(@merge_request.author_id_of_changes), |
33 | 37 | to: recipient(recipient_id), |
34 | 38 | subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) | ... | ... |
app/mailers/emails/notes.rb
... | ... | @@ -4,6 +4,7 @@ module Emails |
4 | 4 | @note = Note.find(note_id) |
5 | 5 | @commit = @note.noteable |
6 | 6 | @project = @note.project |
7 | + @target_url = project_commit_url(@project, @commit, anchor: "note_#{@note.id}") | |
7 | 8 | mail(from: sender(@note.author_id), |
8 | 9 | to: recipient(recipient_id), |
9 | 10 | subject: subject("#{@commit.title} (#{@commit.short_id})")) |
... | ... | @@ -13,6 +14,7 @@ module Emails |
13 | 14 | @note = Note.find(note_id) |
14 | 15 | @issue = @note.noteable |
15 | 16 | @project = @note.project |
17 | + @target_url = project_issue_url(@project, @issue, anchor: "note_#{@note.id}") | |
16 | 18 | mail(from: sender(@note.author_id), |
17 | 19 | to: recipient(recipient_id), |
18 | 20 | subject: subject("#{@issue.title} (##{@issue.iid})")) |
... | ... | @@ -22,6 +24,7 @@ module Emails |
22 | 24 | @note = Note.find(note_id) |
23 | 25 | @merge_request = @note.noteable |
24 | 26 | @project = @note.project |
27 | + @target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{@note.id}") | |
25 | 28 | mail(from: sender(@note.author_id), |
26 | 29 | to: recipient(recipient_id), |
27 | 30 | subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) |
... | ... | @@ -30,6 +33,7 @@ module Emails |
30 | 33 | def note_wall_email(recipient_id, note_id) |
31 | 34 | @note = Note.find(note_id) |
32 | 35 | @project = @note.project |
36 | + @target_url = project_wall_url(@note.project, anchor: "note_#{@note.id}") | |
33 | 37 | mail(from: sender(@note.author_id), |
34 | 38 | to: recipient(recipient_id), |
35 | 39 | subject: subject("Note on wall")) | ... | ... |
app/mailers/emails/profile.rb
... | ... | @@ -3,6 +3,7 @@ module Emails |
3 | 3 | def new_user_email(user_id, password) |
4 | 4 | @user = User.find(user_id) |
5 | 5 | @password = password |
6 | + @target_url = user_url(@user) | |
6 | 7 | mail(to: @user.email, subject: subject("Account was created for you")) |
7 | 8 | end |
8 | 9 | |
... | ... | @@ -15,6 +16,7 @@ module Emails |
15 | 16 | def new_ssh_key_email(key_id) |
16 | 17 | @key = Key.find(key_id) |
17 | 18 | @user = @key.user |
19 | + @target_url = user_url(@user) | |
18 | 20 | mail(to: @user.email, subject: subject("SSH key was added to your account")) |
19 | 21 | end |
20 | 22 | end | ... | ... |
app/mailers/emails/projects.rb
... | ... | @@ -3,6 +3,7 @@ module Emails |
3 | 3 | def project_access_granted_email(user_project_id) |
4 | 4 | @users_project = UsersProject.find user_project_id |
5 | 5 | @project = @users_project.project |
6 | + @target_url = project_url(@project) | |
6 | 7 | mail(to: @users_project.user.email, |
7 | 8 | subject: subject("Access to project was granted")) |
8 | 9 | end |
... | ... | @@ -10,6 +11,7 @@ module Emails |
10 | 11 | def project_was_moved_email(project_id, user_id) |
11 | 12 | @user = User.find user_id |
12 | 13 | @project = Project.find project_id |
14 | + @target_url = project_url(@project) | |
13 | 15 | mail(to: @user.email, |
14 | 16 | subject: subject("Project was moved")) |
15 | 17 | end |
... | ... | @@ -21,6 +23,11 @@ module Emails |
21 | 23 | @commits = Commit.decorate(compare.commits) |
22 | 24 | @diffs = compare.diffs |
23 | 25 | @branch = branch |
26 | + if @commits.length > 1 | |
27 | + @target_url = project_compare_url(@project, from: @commits.first, to: @commits.last) | |
28 | + else | |
29 | + @target_url = project_commit_url(@project, @compare.commit) | |
30 | + end | |
24 | 31 | |
25 | 32 | mail(from: sender(author_id), |
26 | 33 | to: recipient, | ... | ... |
app/views/layouts/notify.html.haml
... | ... | @@ -3,20 +3,24 @@ |
3 | 3 | %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"} |
4 | 4 | %title |
5 | 5 | GitLab |
6 | + :css | |
7 | + p.details { | |
8 | + font-style:italic; | |
9 | + color:#777 | |
10 | + } | |
11 | + .footer p { | |
12 | + font-size:small; | |
13 | + color:#777 | |
14 | + } | |
6 | 15 | |
7 | 16 | %body |
8 | - %h1{style: "background: #EEE; border-bottom: 1px solid #DDD; color: #474D57; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"} | |
9 | - GitLab | |
10 | - - if @project | |
11 | - \| | |
12 | - = link_to @project.name_with_namespace, project_url(@project), style: 'color: #29B; text-decoration: none' | |
13 | - %table{align: "left", border: "0", cellpadding: "0", cellspacing: "0", style: "padding: 10px 0;", width: "100%"} | |
14 | - %tr | |
15 | - %td{align: "left", style: "margin: 0; padding: 10px;"} | |
16 | - = yield | |
17 | - %br | |
18 | - %tr | |
19 | - %td{align: "left", style: "margin: 0; padding: 10px;"} | |
20 | - %p{style: "font-size:small;color:#777"} | |
21 | - - if @project | |
22 | - You're receiving this notification because you are a member of the #{@project.name_with_namespace} project team. | |
17 | + %div.content | |
18 | + = yield | |
19 | + %div.footer{style: "margin-top: 10px;"} | |
20 | + %p | |
21 | + \— | |
22 | + %br | |
23 | + - if @project | |
24 | + You're receiving this notification because you are a member of the #{link_to @project.name_with_namespace, project_url(@project)} project team. | |
25 | + - if @target_url | |
26 | + #{link_to "View in GitLab", @target_url} | ... | ... |
app/views/notify/_note_message.html.haml
app/views/notify/closed_issue_email.html.haml
app/views/notify/closed_merge_request_email.html.haml
1 | 1 | %p |
2 | - = "Merge Request #{@merge_request.iid} was closed by #{@updated_by.name}" | |
3 | -%p | |
4 | - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) | |
5 | -%p | |
6 | - != merge_path_description(@merge_request, '→') | |
7 | -%p | |
8 | - Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} | |
9 | - | |
2 | + = "Merge Request !#{@merge_request.iid} was closed by #{@updated_by.name}" | ... | ... |
app/views/notify/group_access_granted_email.html.haml
app/views/notify/issue_status_changed_email.html.haml
app/views/notify/merged_merge_request_email.html.haml
1 | 1 | %p |
2 | - = "Merge Request #{@merge_request.iid} was merged" | |
3 | -%p | |
4 | - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) | |
5 | -%p | |
6 | - != merge_path_description(@merge_request, '→') | |
7 | -%p | |
8 | - Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} | |
9 | - | |
2 | + = "Merge Request !#{@merge_request.iid} was merged" | ... | ... |
app/views/notify/new_issue_email.html.haml
1 | -%p | |
2 | - New Issue was created. | |
3 | -%p | |
4 | - = "Issue ##{@issue.iid}" | |
5 | - = link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title | |
6 | -%p | |
7 | - Author: #{@issue.author_name} | |
8 | -%p | |
9 | - Assignee: #{@issue.assignee_name} | |
1 | +-if @issue.description | |
2 | + = markdown(@issue.description) | |
3 | + | |
4 | +- if @issue.assignee_id.present? | |
5 | + %p | |
6 | + Assignee: #{@issue.assignee_name} | ... | ... |
app/views/notify/new_merge_request_email.html.haml
1 | -%p | |
2 | - = "New Merge Request ##{@merge_request.iid}" | |
3 | -%p | |
4 | - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) | |
5 | -%p | |
1 | +%p.details | |
6 | 2 | != merge_path_description(@merge_request, '→') |
7 | -%p | |
8 | - Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} | |
9 | 3 | |
4 | +- if @merge_request.assignee_id.present? | |
5 | + %p | |
6 | + Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} | |
7 | + | |
8 | +-if @merge_request.description | |
9 | + = markdown(@merge_request.description) | ... | ... |
app/views/notify/note_commit_email.html.haml
app/views/notify/note_issue_email.html.haml
app/views/notify/note_merge_request_email.html.haml
1 | -%p | |
2 | - - if @note.for_diff_line? | |
3 | - = link_to "New comment on diff", diffs_project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}") | |
4 | - - else | |
5 | - = link_to "New comment", project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}") | |
6 | - for Merge Request ##{@merge_request.iid} | |
7 | - %cite "#{truncate(@merge_request.title, length: 20)}" | |
1 | +- if @note.diff_file_name | |
2 | + %p.details | |
3 | + New comment on diff for | |
4 | + = link_to @note.diff_file_name, @target_url | |
5 | + \: | |
6 | + | |
8 | 7 | = render 'note_message' | ... | ... |
app/views/notify/note_wall_email.html.haml
app/views/notify/reassigned_issue_email.html.haml
app/views/notify/reassigned_merge_request_email.html.haml
spec/mailers/notify_spec.rb
... | ... | @@ -146,7 +146,8 @@ describe Notify do |
146 | 146 | end |
147 | 147 | |
148 | 148 | context 'for issues' do |
149 | - let(:issue) { create(:issue, author: current_user, assignee: assignee, project: project ) } | |
149 | + let(:issue) { create(:issue, author: current_user, assignee: assignee, project: project) } | |
150 | + let(:issue_with_description) { create(:issue, author: current_user, assignee: assignee, project: project, description: Faker::Lorem.sentence) } | |
150 | 151 | |
151 | 152 | describe 'that are new' do |
152 | 153 | subject { Notify.new_issue_email(issue.assignee_id, issue.id) } |
... | ... | @@ -162,6 +163,14 @@ describe Notify do |
162 | 163 | end |
163 | 164 | end |
164 | 165 | |
166 | + describe 'that are new with a description' do | |
167 | + subject { Notify.new_issue_email(issue_with_description.assignee_id, issue_with_description.id) } | |
168 | + | |
169 | + it 'contains the description' do | |
170 | + should have_body_text /#{issue_with_description.description}/ | |
171 | + end | |
172 | + end | |
173 | + | |
165 | 174 | describe 'that have been reassigned' do |
166 | 175 | subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user) } |
167 | 176 | |
... | ... | @@ -221,6 +230,7 @@ describe Notify do |
221 | 230 | |
222 | 231 | context 'for merge requests' do |
223 | 232 | let(:merge_request) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project) } |
233 | + let(:merge_request_with_description) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project, description: Faker::Lorem.sentence) } | |
224 | 234 | |
225 | 235 | describe 'that are new' do |
226 | 236 | subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) } |
... | ... | @@ -244,6 +254,14 @@ describe Notify do |
244 | 254 | end |
245 | 255 | end |
246 | 256 | |
257 | + describe 'that are new with a description' do | |
258 | + subject { Notify.new_merge_request_email(merge_request_with_description.assignee_id, merge_request_with_description.id) } | |
259 | + | |
260 | + it 'contains the description' do | |
261 | + should have_body_text /#{merge_request_with_description.description}/ | |
262 | + end | |
263 | + end | |
264 | + | |
247 | 265 | describe 'that are reassigned' do |
248 | 266 | subject { Notify.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id) } |
249 | 267 | |
... | ... | @@ -335,10 +353,6 @@ describe Notify do |
335 | 353 | should deliver_to recipient.email |
336 | 354 | end |
337 | 355 | |
338 | - it 'contains the name of the note\'s author' do | |
339 | - should have_body_text /#{note_author.name}/ | |
340 | - end | |
341 | - | |
342 | 356 | it 'contains the message from the note' do |
343 | 357 | should have_body_text /#{note.note}/ |
344 | 358 | end |
... | ... | @@ -468,6 +482,8 @@ describe Notify do |
468 | 482 | let(:example_site_path) { root_path } |
469 | 483 | let(:user) { create(:user) } |
470 | 484 | let(:compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, 'cd5c4bac', 'b1e6a9db') } |
485 | + let(:commits) { Commit.decorate(compare.commits) } | |
486 | + let(:diff_path) { project_compare_path(project, from: commits.first, to: commits.last) } | |
471 | 487 | |
472 | 488 | subject { Notify.repository_push_email(project.id, 'devs@company.name', user.id, 'master', compare) } |
473 | 489 | |
... | ... | @@ -492,5 +508,9 @@ describe Notify do |
492 | 508 | it 'includes diffs' do |
493 | 509 | should have_body_text /Checkout wiki pages for installation information/ |
494 | 510 | end |
511 | + | |
512 | + it 'contains a link to the diff' do | |
513 | + should have_body_text /#{diff_path}/ | |
514 | + end | |
495 | 515 | end |
496 | 516 | end | ... | ... |