diff --git a/app/helpers/mail_notification_helper.rb b/app/helpers/mail_notification_helper.rb
index 7e2ad3ea26..9e550b118b 100644
--- a/app/helpers/mail_notification_helper.rb
+++ b/app/helpers/mail_notification_helper.rb
@@ -43,7 +43,7 @@ module MailNotificationHelper
suffix = "svg+xml"
end
- email_image_tag(logo, suffix, options)
+ email_image_tag(logo, suffix, **options)
end
def email_image_tag(image, suffix, **options)
@@ -84,6 +84,20 @@ module MailNotificationHelper
default_options.merge(options).map { |k, v| "#{k}=#{v}" }.join(' ')
end
+ def placeholder_text_styles(**overwrites)
+ {
+ color: '#878787',
+ 'line-height': '24px',
+ 'font-size': '14px',
+ 'white-space': 'normal',
+ overflow: 'hidden',
+ 'max-width': '100%',
+ width: '100%'
+ }.merge(overwrites)
+ .map { |k, v| "#{k}: #{v}" }
+ .join('; ')
+ end
+
def placeholder_cell(number, vertical:)
style = if vertical
"max-width:#{number}; min-width:#{number}; width:#{number}"
diff --git a/app/mailers/announcement_mailer.rb b/app/mailers/announcement_mailer.rb
new file mode 100644
index 0000000000..8ef95ff3da
--- /dev/null
+++ b/app/mailers/announcement_mailer.rb
@@ -0,0 +1,55 @@
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2021 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+# Sends mails for announcements.
+# For now, it cannot handle sending events on any Announcement model but is rather focused
+# on handing very simple mails where only the recipient, subject and body is provided.
+
+class AnnouncementMailer < ApplicationMailer
+ include OpenProject::StaticRouting::UrlHelpers
+ include OpenProject::TextFormatting
+ helper :mail_notification
+
+ def announce(user, subject:, body:, body_header: nil, body_subheader: nil)
+ with_locale_for(user) do
+ mail to: user.mail,
+ subject: subject do |format|
+ locals = {
+ body: body,
+ user: user,
+ header_summary: subject,
+ body_header: body_header,
+ body_subheader: body_subheader
+ }
+
+ format.html { render locals: locals }
+ format.text { render locals: locals }
+ end
+ end
+ end
+end
diff --git a/app/views/announcement_mailer/announce.html.erb b/app/views/announcement_mailer/announce.html.erb
new file mode 100644
index 0000000000..d969fee822
--- /dev/null
+++ b/app/views/announcement_mailer/announce.html.erb
@@ -0,0 +1,42 @@
+<%= render layout: 'mailer/spacer_table' do %>
+ <%= render partial: 'mailer/notification_mailer_header',
+ locals: {
+ summary: header_summary,
+ user: user
+ } %>
+
+ <%= render layout: 'mailer/border_table' do %>
+
+ <%= placeholder_cell('24px', vertical: true) %>
+
+
+ <% if body_subheader %>
+
+
+ <%= format_text body_subheader %>
+ |
+
+ <% end %>
+ <% if body_header %>
+
+
+ <%= format_text body_header %>
+ |
+
+ <% end %>
+
+
+ <%= format_text body %>
+ |
+
+
+ |
+ <%= placeholder_cell('24px', vertical: true) %>
+
+
+ <%= placeholder_cell('24px', vertical: false) %>
+
+ <% end %>
+
+ <%= render partial: 'mailer/notification_settings_table' %>
+<% end %>
diff --git a/app/views/announcement_mailer/announce.text.erb b/app/views/announcement_mailer/announce.text.erb
new file mode 100644
index 0000000000..2dc124efdf
--- /dev/null
+++ b/app/views/announcement_mailer/announce.text.erb
@@ -0,0 +1,13 @@
+<%= I18n.t(:'mail.salutation', user: user.firstname) %>
+<%= header_summary %>
+<%= "-" * 100 %>
+
+
+<%= "+" * 50 %>
+<%= strip_tags body_subheader %>
+<%= strip_tags body_header %>
+<%= "+" * 50 %>
+
+<%= strip_tags body %>
+
+<%= "-" * 100 %>
diff --git a/app/views/digest_mailer/work_packages.html.erb b/app/views/digest_mailer/work_packages.html.erb
index b8ae67e8ae..3fad9b23de 100644
--- a/app/views/digest_mailer/work_packages.html.erb
+++ b/app/views/digest_mailer/work_packages.html.erb
@@ -8,16 +8,13 @@
<%= placeholder_cell('12px', vertical: true) %>
- <%= render layout: 'mailer/notification_mailer_header',
+ <%= render partial: 'mailer/notification_mailer_header',
locals: {
- summary: "#{I18n.t(:'mail.digests.you_have')} #{digest_summary_text(@notification_ids.length, @mentioned_count)}"
- } do %>
-
- <%= I18n.t(:'mail.notification.center') %>
-
- <% end %>
+ summary: "#{I18n.t(:'mail.digests.you_have')} #{digest_summary_text(@notification_ids.length, @mentioned_count)}",
+ button_href: notifications_center_url,
+ button_text: I18n.t(:'mail.notification.center'),
+ user: @user
+ } %>
<% @aggregated_notifications.first(DigestMailer::MAX_SHOWN_WORK_PACKAGES).each do | work_package, notifications_by_work_package| %>
<%= render layout: 'mailer/notification_row',
@@ -25,7 +22,8 @@
work_package: work_package,
unique_reasons: unique_reasons_of_notifications(notifications_by_work_package),
show_count: true,
- count: notifications_by_work_package.length
+ count: notifications_by_work_package.length,
+ user: @user
} do %>
>
@@ -38,50 +36,36 @@
<% end %>
<% end %>
- >
-
-
- >
-
-
- <% if @aggregated_notifications.length > DigestMailer::MAX_SHOWN_WORK_PACKAGES %>
-
-
-
-
- <% number_of_overflowing_work_packages = @aggregated_notifications.length - DigestMailer::MAX_SHOWN_WORK_PACKAGES %>
- <% count = number_of_overflowing_work_packages === 1 ? 'one' : 'other' %>
- <%= I18n.t(:"mail.work_packages.more_to_see.#{count}", count: number_of_overflowing_work_packages) %>
-
- |
- <%= placeholder_cell('10px', vertical: true) %>
-
-
- <%= I18n.t(:'mail.work_packages.see_all') %>
-
- |
-
-
- <% end %>
- |
-
-
- |
- <%= placeholder_cell('10px', vertical: true) %>
-
- <%= render partial: 'mailer/notification_settings_button' %>
- |
-
-
+ <%= render layout: 'mailer/notification_settings_table' do %>
+ <% if @aggregated_notifications.length > DigestMailer::MAX_SHOWN_WORK_PACKAGES %>
+
+
+
+
+ <% number_of_overflowing_work_packages = @aggregated_notifications.length - DigestMailer::MAX_SHOWN_WORK_PACKAGES %>
+ <% count = number_of_overflowing_work_packages === 1 ? 'one' : 'other' %>
+ <%= I18n.t(:"mail.work_packages.more_to_see.#{count}", count: number_of_overflowing_work_packages) %>
+
+ |
+ <%= placeholder_cell('10px', vertical: true) %>
+
+
+ <%= I18n.t(:'mail.work_packages.see_all') %>
+
+ |
+
+
+ <% end %>
+ <% end %>
diff --git a/app/views/mailer/_border_table.html.erb b/app/views/mailer/_border_table.html.erb
new file mode 100644
index 0000000000..c355803bd9
--- /dev/null
+++ b/app/views/mailer/_border_table.html.erb
@@ -0,0 +1,21 @@
+>
+
+ <%= placeholder_cell('12px', vertical: false) %>
+
+
+
+
+ |
+
+
+ <%= placeholder_cell('12px', vertical: false) %>
+
+
+
+
+
+ <%= placeholder_cell('20px', vertical: false) %>
+
+
diff --git a/app/views/mailer/_notification_mailer_header.html.erb b/app/views/mailer/_notification_mailer_header.html.erb
index 1b50cdf351..0c2fe2a1d9 100644
--- a/app/views/mailer/_notification_mailer_header.html.erb
+++ b/app/views/mailer/_notification_mailer_header.html.erb
@@ -8,7 +8,7 @@
- <%= I18n.t(:'mail.salutation', user: @user.firstname) %>
+ <%= I18n.t(:'mail.salutation', user: user.firstname) %>
|
@@ -25,11 +25,17 @@
<%= placeholder_cell('24px', vertical: false) %>
-
-
- <%= yield %>
- |
-
+ <% if defined?(button_href) && defined?(button_text) %>
+
+
+
+ <%= button_text %>
+
+ |
+
+ <% end %>
<%= placeholder_cell('40px', vertical: false) %>
@@ -40,7 +46,7 @@
>
- <%= logo_tag({ alt: "#{Setting.app_title} #{I18n.t(:'mail.logo_alt_text')}", style: "height: 96px;max-width: 240px;"}) %>
+ <%= logo_tag(alt: "#{Setting.app_title} #{I18n.t(:'mail.logo_alt_text')}", style: "height: 96px;max-width: 240px;") %>
|
diff --git a/app/views/mailer/_notification_row.html.erb b/app/views/mailer/_notification_row.html.erb
index a11a1f4f42..67d767713b 100644
--- a/app/views/mailer/_notification_row.html.erb
+++ b/app/views/mailer/_notification_row.html.erb
@@ -1,10 +1,7 @@
->
-
- <%= placeholder_cell('12px', vertical: false) %>
-
+ <%= render layout: 'mailer/border_table' do %>
>
@@ -97,11 +94,5 @@
<%= placeholder_cell('12px', vertical: false) %>
-
-
-
-
- <%= placeholder_cell('20px', vertical: false) %>
-
-
+ <% end %>
\ No newline at end of file
diff --git a/app/views/mailer/_notification_settings_table.html.erb b/app/views/mailer/_notification_settings_table.html.erb
new file mode 100644
index 0000000000..13674c52c2
--- /dev/null
+++ b/app/views/mailer/_notification_settings_table.html.erb
@@ -0,0 +1,17 @@
+>
+
+
+ >
+
+
+ <%= yield %>
+ |
+
+
+ |
+ <%= placeholder_cell('10px', vertical: true) %>
+
+ <%= render partial: 'mailer/notification_settings_button' %>
+ |
+
+
diff --git a/app/views/mailer/_spacer_table.html.erb b/app/views/mailer/_spacer_table.html.erb
new file mode 100644
index 0000000000..93e4ffc076
--- /dev/null
+++ b/app/views/mailer/_spacer_table.html.erb
@@ -0,0 +1,21 @@
+>
+
+ <%= placeholder_cell('20px', vertical: false) %>
+
+
+
+>
+
+ <%= placeholder_cell('12px', vertical: true) %>
+
+ <%= yield %>
+
+
+
+ <%= placeholder_cell('40px', vertical: false) %>
+
+
+ |
+ <%= placeholder_cell('12px', vertical: true) %>
+
+
diff --git a/app/views/work_package_mailer/mentioned.html.erb b/app/views/work_package_mailer/mentioned.html.erb
index a0a7930eda..5d34c04cad 100644
--- a/app/views/work_package_mailer/mentioned.html.erb
+++ b/app/views/work_package_mailer/mentioned.html.erb
@@ -8,48 +8,31 @@
|
<%= placeholder_cell('12px', vertical: true) %>
- <%= render layout: 'mailer/notification_mailer_header',
+ <%= render partial: 'mailer/notification_mailer_header',
locals: {
- summary: "#{I18n.t(:'mail.work_packages.mentioned_by', user: @journal.user)}"
- } do %>
-
- <%= I18n.t(:'mail.notification.see_in_center') %>
-
- <% end %>
+ summary: I18n.t(:'mail.work_packages.mentioned_by', user: @journal.user),
+ button_href: notifications_path(@work_package.id),
+ button_text: I18n.t(:'mail.notification.see_in_center'),
+ user: @user
+ } %>
<%= render layout: 'mailer/notification_row',
locals: {
work_package: @work_package,
unique_reasons: [:mentioned],
- show_count: false
+ show_count: false,
+ user: @user
} do %>
>
-
+ |
<%= format_text @journal.notes, only_path: false %>
|
<% end %>
- >
-
-
-
- |
- <%= placeholder_cell('10px', vertical: true) %>
-
- <%= render partial: 'mailer/notification_settings_button' %>
- |
-
-
+ <%= render partial: 'mailer/notification_settings_table' %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 2ba6bc3d91..9dd250b9de 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1404,6 +1404,29 @@ en:
mentioned: "You have been mentioned in %{work_package}"
responsible: "You have become accountable for %{work_package}"
watched: "You are watching %{work_package}"
+ update_info_mail:
+ body: >
+ We are excited to announce the release of OpenProject 12.0. It's a major release that will hopefully significantly improve the way you use OpenProject.
+
+
+ Starting with this release, we are introducing Notification Center. From now on, you will receive notifications for updates to work packages directly in OpenProject. You can mark these notifications as read, reply to a comment or even directly modify work package attributes without leaving Notification Center.
+
+
+ This also means that we will no longer be using emails for notifications. We think Notification Center is a better place to view and act upon these updates. Nevertheless, if you would like continue receiving updates via email, you can choose to receive daily email reminders at particular times of your choosing.
+
+
+ You can set your preferences for notifications and email reminders via your account settings, or leave them unchanged to keep the default ones. You can do this through the “Change email settings” button bellow.
+
+
+ We hope you find Notification Center useful and that it makes you even more productive.
+
+
+ Sincerely,
+
+ The OpenProject team
+ body_header: 'Version 12.0 with Notification Center'
+ body_subheader: 'News'
+ subject: 'Important changes to notifications with the release of 12.0'
label_accessibility: "Accessibility"
label_account: "Account"
diff --git a/db/migrate/20211105142202_queue_notification_update_mail.rb b/db/migrate/20211105142202_queue_notification_update_mail.rb
new file mode 100644
index 0000000000..f11508b941
--- /dev/null
+++ b/db/migrate/20211105142202_queue_notification_update_mail.rb
@@ -0,0 +1,13 @@
+class QueueNotificationUpdateMail < ActiveRecord::Migration[6.1]
+ def up
+ User.active.find_each do |user|
+ AnnouncementMailer
+ .announce(user,
+ subject: I18n.t(:'notifications.update_info_mail.subject'),
+ body: I18n.t(:'notifications.update_info_mail.body'),
+ body_header: I18n.t(:'notifications.update_info_mail.body_header'),
+ body_subheader: I18n.t(:'notifications.update_info_mail.body_subheader'))
+ .deliver_later
+ end
+ end
+end
diff --git a/spec/mailers/announcement_mailer_spec.rb b/spec/mailers/announcement_mailer_spec.rb
new file mode 100644
index 0000000000..15ecff5a2f
--- /dev/null
+++ b/spec/mailers/announcement_mailer_spec.rb
@@ -0,0 +1,63 @@
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2021 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+require 'spec_helper'
+
+describe AnnouncementMailer, type: :mailer do
+ let(:announcement_subject) { 'Some subject' }
+ let(:recipient) { FactoryBot.build_stubbed(:user) }
+ let(:announcement_body) { 'Some body text' }
+
+ describe '#announce' do
+ subject(:mail) do
+ described_class.announce(recipient,
+ subject: announcement_subject,
+ body: announcement_body)
+ end
+
+ it "has a subject" do
+ expect(mail.subject)
+ .to eq announcement_subject
+ end
+
+ it 'includes the body' do
+ expect(mail.body.encoded)
+ .to include(announcement_body)
+ end
+
+ it "includes the subject in the body as well" do
+ expect(mail.body.encoded)
+ .to include announcement_subject
+ end
+
+ it 'sends to the recipient' do
+ expect(mail.to)
+ .to match_array [recipient.mail]
+ end
+ end
+end
| |