[#73827] Adapt email notifications for project-based work package identifiers

https://community.openproject.org/wp/73827
This commit is contained in:
Judith Roth
2026-04-23 16:19:23 +02:00
parent 8c3ace5f3a
commit 2efc96504a
16 changed files with 153 additions and 31 deletions
+1 -1
View File
@@ -75,7 +75,7 @@ class Reminders::NotificationMailer < ApplicationMailer
end
def work_package_subject_text_wrapper
"=" * ("# #{@work_package.id}#{@work_package.subject}".length + 4)
"=" * ("#{@work_package.formatted_id} #{@work_package.subject}".length + 4)
end
def text_email_wrapper
+1 -1
View File
@@ -49,7 +49,7 @@ class SharingMailer < ApplicationMailer
send_localized_mail(@shared_with_user) do
@role_rights = derive_role_rights(role)
@allowed_work_package_actions = derive_allowed_work_package_actions(role)
I18n.t("mail.sharing.work_packages.subject", id: @work_package.id)
I18n.t("mail.sharing.work_packages.subject", id: @work_package.formatted_id)
end
end
+2 -2
View File
@@ -47,7 +47,7 @@ class WorkPackageMailer < ApplicationMailer
send_localized_mail(recipient) do
I18n.t(:"mail.mention.subject",
user_name: author.name,
id: @work_package.id,
id: @work_package.formatted_id,
subject: @work_package.subject)
end
end
@@ -73,7 +73,7 @@ class WorkPackageMailer < ApplicationMailer
def subject_for_work_package(work_package)
"#{work_package.project.name} - #{work_package.status.name} #{work_package.type.name} " +
"##{work_package.id}: #{work_package.subject}"
"#{work_package.formatted_id}: #{work_package.subject}"
end
def set_work_package_headers(work_package)
@@ -4,9 +4,9 @@
<% @aggregated_notifications.first(DigestMailer::MAX_SHOWN_WORK_PACKAGES).each do |work_package, notifications_by_work_package| %>
<%= "=" * (("# " + work_package.id.to_s + work_package.subject).length + 4) %>
= #<%= work_package.id %> <%= work_package.subject %> =
<%= "=" * (("# " + work_package.id.to_s + work_package.subject).length + 4) %>
<%= "=" * ((work_package.formatted_id + " " + work_package.subject).length + 4) %>
= <%= work_package.formatted_id %> <%= work_package.subject %> =
<%= "=" * ((work_package.formatted_id + " " + work_package.subject).length + 4) %>
<% unique_reasons = unique_reasons_of_notifications(notifications_by_work_package) %>
<% unique_reasons.each_with_index do |reason, index| %><%= I18n.t(:"mail.work_packages.reason.#{reason || :unknown}", default: "-") %><%= ", " unless unique_reasons.size - 1 == index %><% end %>: <% if notifications_by_work_package.first.date_alert? %>
+1 -1
View File
@@ -27,7 +27,7 @@
</td>
<%= placeholder_cell("8px", vertical: true) %>
<td width="100%" style="color: #878787;">
#<%= work_package.id %> - <%= work_package.project %>
<%= work_package.formatted_id %> - <%= work_package.project %>
<%= " - " unless unique_reasons.length === 1 && unique_reasons.first.nil? %>
<% unique_reasons.each_with_index do |reason, index| %>
<%= I18n.t(
@@ -3,7 +3,7 @@
<%= text_email_wrapper %>
<%= work_package_subject_text_wrapper %>
= #<%= @work_package.id %> <%= @work_package.subject %> =
= <%= @work_package.formatted_id %> <%= @work_package.subject %> =
<%= work_package_subject_text_wrapper %>
<%= reminder_timestamp_text %>
@@ -3,9 +3,9 @@
<%= "-" * 100 %>
<%= "=" * (("# " + @work_package.id.to_s + @work_package.subject).length + 4) %>
= #<%= @work_package.id %> <%= @work_package.subject %> =
<%= "=" * (("# " + @work_package.id.to_s + @work_package.subject).length + 4) %>
<%= "=" * ((@work_package.formatted_id + " " + @work_package.subject).length + 4) %>
= <%= @work_package.formatted_id %> <%= @work_package.subject %> =
<%= "=" * ((@work_package.formatted_id + " " + @work_package.subject).length + 4) %>
<%= I18n.t(:label_comment_added) %>:
<%= strip_tags @journal.notes %>
@@ -26,7 +26,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See COPYRIGHT and LICENSE files for more details.
++#%>
<%= t("text_work_package_watcher_#{@action}", id: "##{@work_package.id}", watcher_changer: @watcher_changer) %>
<%= t("text_work_package_watcher_#{@action}", id: @work_package.formatted_id, watcher_changer: @watcher_changer) %>
<hr>
<%= render partial: "work_package_details", locals: { work_package: @work_package } %>
<p>
@@ -27,7 +27,7 @@ See COPYRIGHT and LICENSE files for more details.
++#%>
<%= t("text_work_package_watcher_#{@action}", id: "##{@work_package.id}", watcher_changer: @watcher_changer) %>
<%= t("text_work_package_watcher_#{@action}", id: @work_package.formatted_id, watcher_changer: @watcher_changer) %>
----------------------------------------
<%= render partial: "work_package_details", locals: { work_package: @work_package } %>
+2 -2
View File
@@ -4615,7 +4615,7 @@ en:
you_have: "You have"
logo_alt_text: "Logo"
mention:
subject: "%{user_name} mentioned you in #%{id} - %{subject}"
subject: "%{user_name} mentioned you in %{id} - %{subject}"
notification:
center: "To notification center"
see_in_center: "See comment in notification center"
@@ -4653,7 +4653,7 @@ en:
allowed_actions_html: "You have the following permissions on this work package: %{allowed_actions}. This can change depending on your project role and permissions."
create_account: "To access this work package, you will need to create and activate an account on %{instance}."
open_work_package: "Open work package"
subject: "Work package #%{id} was shared with you"
subject: "Work package %{id} was shared with you"
enterprise_text: "Share work packages with users who are not members of the project."
summary:
user: "%{user} shared a work package with you with %{role_rights} rights"
+1 -1
View File
@@ -49,7 +49,7 @@ FactoryBot.define do
workspace_type { "project" }
sequence(:name) { |n| "My Project No. #{n}" }
sequence(:identifier) { |n| "myproject_no_#{n}" }
sequence(:identifier) { |n| Setting::WorkPackageIdentifier.semantic_mode_active? ? "MPN#{n}" : "myproject_no_#{n}" }
# Use this trait for specs that exercise semantic-mode behaviour.
# Produces a deterministic uppercase identifier that satisfies
@@ -104,7 +104,7 @@ RSpec.describe "Immediate reminder settings", :js do
expect(ActionMailer::Base.deliveries.first.subject)
.to eql I18n.t(:"mail.mention.subject",
user_name: current_user.name,
id: work_package.id,
id: "##{work_package.id}",
subject: work_package.subject)
end
end
+16 -3
View File
@@ -89,13 +89,26 @@ RSpec.describe DigestMailer do
expected_notification_subject = "#{work_package.type.name.upcase} #{work_package.subject}"
expect(mail_body).to have_text(expected_notification_subject, normalize_ws: true)
expected_notification_header = "#{work_package.status.name} ##{work_package.id} - #{work_package.project}"
expect(mail_body).to have_text(expected_notification_header, normalize_ws: true)
expected_text = "#{journal.initial? ? 'Created' : 'Updated'} at #{time_stamp} by #{recipient.name}"
expect(mail_body).to have_text(expected_text, normalize_ws: true)
end
context "with classic mode", with_settings: { work_packages_identifier: "classic" } do
it "shows the # prefixed numeric id in the notification header" do
expected = "#{work_package.status.name} ##{work_package.id} - #{work_package.project}"
expect(mail_body).to have_text(expected, normalize_ws: true)
end
end
context "with semantic mode",
with_flag: { semantic_work_package_ids: true },
with_settings: { work_packages_identifier: "semantic" } do
it "shows the semantic identifier without # prefix in the notification header" do
expected = "#{work_package.status.name} #{work_package.identifier} - #{work_package.project}"
expect(mail_body).to have_text(expected, normalize_ws: true)
end
end
it "includes a reference to the notification center if there are more than the maximum number of shown work packages" do
stub_const("DigestMailer::MAX_SHOWN_WORK_PACKAGES", 0)
@@ -81,5 +81,24 @@ RSpec.describe Reminders::NotificationMailer do
it "mail body includes the reminder note" do
expect(mail_body).to include("Note: “This is an important reminder”")
end
context "with classic mode", with_settings: { work_packages_identifier: "classic" } do
it "shows the # prefixed numeric id in the mail body" do
expect(mail_body).to include("##{work_package.id}")
end
end
context "with semantic mode",
with_flag: { semantic_work_package_ids: true },
with_settings: { work_packages_identifier: "semantic" } do
let(:work_package) do
build_stubbed(:work_package, identifier: "PROJ-42", sequence_number: 42)
end
it "shows the semantic identifier without # prefix in the mail body" do
expect(mail_body).to include("PROJ-42")
expect(mail_body).not_to include("#PROJ-42")
end
end
end
end
+23 -4
View File
@@ -59,10 +59,29 @@ RSpec.describe SharingMailer do
.to contain_exactly(shared_with_user.mail)
end
it "sets the appropriate subject" do
expect(mail.subject)
.to eq(I18n.t("mail.sharing.work_packages.subject",
id: work_package.id))
context "with classic mode", with_settings: { work_packages_identifier: "classic" } do
it "sets the subject with # prefixed numeric id" do
expect(mail.subject)
.to eq(I18n.t("mail.sharing.work_packages.subject", id: "##{work_package.id}"))
end
end
context "with semantic mode",
with_flag: { semantic_work_package_ids: true },
with_settings: { work_packages_identifier: "semantic" } do
let(:work_package) do
build_stubbed(:work_package,
type: build_stubbed(:type_standard),
author: build_stubbed(:user),
project:,
identifier: "PROJ-42",
sequence_number: 42)
end
it "sets the subject with the semantic identifier without # prefix" do
expect(mail.subject)
.to eq(I18n.t("mail.sharing.work_packages.subject", id: "PROJ-42"))
end
end
it "has a project header" do
+77 -6
View File
@@ -55,12 +55,35 @@ RSpec.describe WorkPackageMailer do
describe "#mentioned" do
subject(:mail) { described_class.mentioned(recipient, journal) }
it "has a subject" do
expect(mail.subject)
.to eql I18n.t(:"mail.mention.subject",
user_name: author.name,
id: work_package.id,
subject: work_package.subject)
context "with classic mode", with_settings: { work_packages_identifier: "classic" } do
it "has a subject with # prefixed numeric id" do
expect(mail.subject)
.to eql I18n.t(:"mail.mention.subject",
user_name: author.name,
id: "##{work_package.id}",
subject: work_package.subject)
end
end
context "with semantic mode",
with_flag: { semantic_work_package_ids: true },
with_settings: { work_packages_identifier: "semantic" } do
let(:work_package) do
build_stubbed(:work_package,
type: build_stubbed(:type_standard),
project:,
assigned_to: assignee,
identifier: "PROJ-42",
sequence_number: 42)
end
it "has a subject with semantic identifier and no # prefix" do
expect(mail.subject)
.to eql I18n.t(:"mail.mention.subject",
user_name: author.name,
id: "PROJ-42",
subject: work_package.subject)
end
end
it "is sent to the recipient" do
@@ -122,6 +145,30 @@ RSpec.describe WorkPackageMailer do
.to include(work_package.subject)
end
context "with classic mode", with_settings: { work_packages_identifier: "classic" } do
it "includes the # prefixed numeric id in the subject" do
expect(mail.subject).to include("##{work_package.id}")
end
end
context "with semantic mode",
with_flag: { semantic_work_package_ids: true },
with_settings: { work_packages_identifier: "semantic" } do
let(:work_package) do
build_stubbed(:work_package,
type: build_stubbed(:type_standard),
project:,
assigned_to: assignee,
identifier: "PROJ-42",
sequence_number: 42)
end
it "includes the semantic identifier without # prefix in the subject" do
expect(mail.subject).to include("PROJ-42")
expect(mail.subject).not_to match(/#PROJ-42/)
end
end
it "has a references header" do
expect(mail.references)
.to eql "op.work_package-#{work_package.id}@example.net"
@@ -136,6 +183,30 @@ RSpec.describe WorkPackageMailer do
.to include(work_package.subject)
end
context "with classic mode", with_settings: { work_packages_identifier: "classic" } do
it "includes the # prefixed numeric id in the subject" do
expect(mail.subject).to include("##{work_package.id}")
end
end
context "with semantic mode",
with_flag: { semantic_work_package_ids: true },
with_settings: { work_packages_identifier: "semantic" } do
let(:work_package) do
build_stubbed(:work_package,
type: build_stubbed(:type_standard),
project:,
assigned_to: assignee,
identifier: "PROJ-42",
sequence_number: 42)
end
it "includes the semantic identifier without # prefix in the subject" do
expect(mail.subject).to include("PROJ-42")
expect(mail.subject).not_to match(/#PROJ-42/)
end
end
it "has a references header" do
expect(mail.references)
.to eql "op.work_package-#{work_package.id}@example.net"