From 61d98866f5c85e05821c718398e1520deb5a04d0 Mon Sep 17 00:00:00 2001 From: FloWalchs Date: Wed, 22 Apr 2026 19:53:04 +0000 Subject: [PATCH 1/3] Add detailed options for notification events --- app/assets/stylesheets/application.css | 1 + app/helpers/settings_helper.rb | 81 +++++++++++++++++++++- app/models/journal.rb | 22 ++++-- app/views/settings/_notifications.html.erb | 1 - config/settings.yml | 9 +++ lib/redmine/notifiable.rb | 7 +- 6 files changed, 108 insertions(+), 13 deletions(-) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 6684210c1..d03ee3bde 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -1344,6 +1344,7 @@ input#months { inline-size: 46px; } fieldset.settings label { display: block; } fieldset#notified_events .parent { padding-inline-start: 20px; } +fieldset#notified_events .notification-details { padding-inline-start: 40px; border: 0px;} span.required {color: var(--oc-red-9);} .summary {font-style: italic;} diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb index 0c0b01fc9..dd0b79f1e 100644 --- a/app/helpers/settings_helper.rb +++ b/app/helpers/settings_helper.rb @@ -122,15 +122,17 @@ module SettingsHelper # Renders a notification field for a Redmine::Notifiable option def notification_field(notifiable) + checked = setting_value('notified_events').include?(notifiable.name) tag_data = if notifiable.parent.present? - {:parent_notifiable => notifiable.parent} + {:parent_notifiable => notifiable.parent, + :disables => "fieldset[data-parent-notifiable=#{notifiable.name}]"} else {:disables => "input[data-parent-notifiable=#{notifiable.name}]"} end tag = check_box_tag('settings[notified_events][]', notifiable.name, - setting_value('notified_events').include?(notifiable.name), + checked, :id => nil, :data => tag_data) text = l_or_humanize(notifiable.name, :prefix => 'label_') @@ -138,7 +140,13 @@ module SettingsHelper if notifiable.parent.present? options[:class] = "parent" end - content_tag(:label, tag + text, options) + label = content_tag(:label, tag + text, options) + + if detailed_notifiable?(notifiable) + label + notification_details_fieldset(notifiable, checked) + else + label + "
".html_safe + end end def session_lifetime_options @@ -248,4 +256,71 @@ module SettingsHelper ['Retro', 'retro'], ['Robohash', 'robohash']] end + + def issue_attribute_options(except: []) + attrs = Issue.new.safe_attribute_names + options = attrs.filter_map do |attr| + next if except.include?(attr) + + label = l("field_#{attr.gsub(/_id$/, '')}", default: nil) + [label, attr] if label.present? + end + options << [l(:label_subtask_plural), 'child_id'] + options << [l(:field_parent_issue), 'parent_id'] + options.sort_by(&:first) + end + + def issue_custom_field_options + IssueCustomField + .sorted + .map { |cf| [cf.name, cf.id.to_s] } + end + + def issue_relation_options + IssueRelation::TYPES.map do |name, _| + [l("label_relation_#{name}", default: nil) || + l("label_#{name}_to", default: nil) || + l("label_#{name}_by", default: nil) || + l("label_#{name}", default: nil) || name, name] + end + end + + def notification_details_fieldset(notifiable, checked) + notifiable_setting_key = "notified_event_#{notifiable.name}_details" + content_tag( + :fieldset, + class: 'notification-details', + disabled: checked, + data: { parent_notifiable: notifiable.name } + ) do + select_tag( + "settings[#{notifiable_setting_key}][]", + options_for_select( + notifiable_field_options(notifiable), + setting_value(notifiable_setting_key) + ), + multiple: true, + size: 6, + class: 'multiselect wide' + ) + + hidden_field_tag("settings[#{notifiable_setting_key}][]", "", id: nil) + end + end + + def notifiable_field_options(notifiable) + case notifiable.name + when 'issue_attr_updated' + issue_attribute_options(except: %w[notes]) + when 'issue_relation_updated' + issue_relation_options + when 'issue_cf_updated' + issue_custom_field_options + else + [] + end + end + + def detailed_notifiable?(notifiable) + notifiable_field_options(notifiable).present? + end end diff --git a/app/models/journal.rb b/app/models/journal.rb index 5bdae7f12..184cd7e1a 100644 --- a/app/models/journal.rb +++ b/app/models/journal.rb @@ -378,11 +378,10 @@ class Journal < ApplicationRecord ( Setting.notified_events.include?('issue_updated') || (Setting.notified_events.include?('issue_note_added') && notes.present?) || - (Setting.notified_events.include?('issue_status_updated') && new_status.present?) || - (Setting.notified_events.include?('issue_assigned_to_updated') && detail_for_attribute('assigned_to_id').present?) || - (Setting.notified_events.include?('issue_priority_updated') && new_value_for('priority_id').present?) || - (Setting.notified_events.include?('issue_fixed_version_updated') && detail_for_attribute('fixed_version_id').present?) || - (Setting.notified_events.include?('issue_attachment_added') && details.any? {|d| d.property == 'attachment' && d.value }) + (Setting.notified_events.include?('issue_attachment_added') && details.any? {|d| d.property == 'attachment' && d.value }) || + notify_by_details?(Setting.notified_events.include?('issue_attr_updated'), 'attr', Setting.notified_event_issue_attr_updated_details) || + notify_by_details?(Setting.notified_events.include?('issue_relation_updated'), 'relation', Setting.notified_event_issue_relation_updated_details) || + notify_by_details?(Setting.notified_events.include?('issue_cf_updated'), 'cf', Setting.notified_event_issue_cf_updated_details) ) Mailer.deliver_issue_edit(self) end @@ -394,4 +393,17 @@ class Journal < ApplicationRecord end notified end + + def notify_by_details?(notify_all, property, prop_keys) + return false unless notify_all || prop_keys.present? + + relevant_details = details.select { |d| d.property == property } + return false if relevant_details.empty? + return true if notify_all + + allowed = Array(prop_keys).map(&:to_s) + relevant_details.any? do |detail| + allowed.include?(detail.prop_key.to_s) + end + end end diff --git a/app/views/settings/_notifications.html.erb b/app/views/settings/_notifications.html.erb index a4b07f40e..878f5012a 100644 --- a/app/views/settings/_notifications.html.erb +++ b/app/views/settings/_notifications.html.erb @@ -13,7 +13,6 @@ <%= hidden_field_tag 'settings[notified_events][]', '' %> <% @notifiables.each do |notifiable| %> <%= notification_field notifiable %> -
<% end %>

<%= check_all_links('notified_events') %>

diff --git a/config/settings.yml b/config/settings.yml index 982bc5f1f..bcd75c68a 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -203,6 +203,15 @@ notified_events: default: - issue_added - issue_updated +notified_event_issue_attr_updated_details: + serialized: true + default: [] +notified_event_issue_relation_updated_details: + serialized: true + default: [] +notified_event_issue_cf_updated_details: + serialized: true + default: [] mail_handler_body_delimiters: default: '' mail_handler_enable_regex_delimiters: diff --git a/lib/redmine/notifiable.rb b/lib/redmine/notifiable.rb index 95d26609d..882b5274d 100644 --- a/lib/redmine/notifiable.rb +++ b/lib/redmine/notifiable.rb @@ -29,11 +29,10 @@ module Redmine notifications << Notifiable.new('issue_added') notifications << Notifiable.new('issue_updated') notifications << Notifiable.new('issue_note_added', 'issue_updated') - notifications << Notifiable.new('issue_status_updated', 'issue_updated') - notifications << Notifiable.new('issue_assigned_to_updated', 'issue_updated') - notifications << Notifiable.new('issue_priority_updated', 'issue_updated') - notifications << Notifiable.new('issue_fixed_version_updated', 'issue_updated') notifications << Notifiable.new('issue_attachment_added', 'issue_updated') + notifications << Notifiable.new('issue_attr_updated', 'issue_updated') + notifications << Notifiable.new('issue_relation_updated', 'issue_updated') + notifications << Notifiable.new('issue_cf_updated', 'issue_updated') notifications << Notifiable.new('news_added') notifications << Notifiable.new('news_comment_added') notifications << Notifiable.new('document_added') -- 2.43.0