From a377f927337374a4959de4157776a4d612a43564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20B=C4=82LTEANU?= Date: Mon, 26 Oct 2026 23:59:01 +0200 Subject: [PATCH] Extract the auto watch logic shared by Issue and Journal into the Issue::AutoWatchable concern. --- app/models/concerns/issue/auto_watchable.rb | 40 +++++++ app/models/issue.rb | 22 +--- app/models/journal.rb | 21 +--- test/unit/issue_auto_watchable_test.rb | 120 ++++++++++++++++++++ 4 files changed, 163 insertions(+), 40 deletions(-) create mode 100644 app/models/concerns/issue/auto_watchable.rb create mode 100644 test/unit/issue_auto_watchable_test.rb diff --git a/app/models/concerns/issue/auto_watchable.rb b/app/models/concerns/issue/auto_watchable.rb new file mode 100644 index 000000000..28bed1c84 --- /dev/null +++ b/app/models/concerns/issue/auto_watchable.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006- Jean-Philippe Lang +# +# 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. + +module Issue::AutoWatchable + extend ActiveSupport::Concern + + # Adds user as a watcher if the given auto watch option is enabled + # in their preferences and they are allowed to watch the issue + def auto_watch(user, option) + return unless user.is_a?(User) && user.active? && user.pref.auto_watch_on?(option) + return unless user.allowed_to?(:add_issue_watchers, project) + return if !valid_watcher?(user) || watched_by?(user) + + add_watcher(user) + end + + private + + # Called after_create_commit + def add_auto_watcher + auto_watch(author, 'issue_created') + auto_watch(assigned_to, 'issue_assigned_to_me') + end +end diff --git a/app/models/issue.rb b/app/models/issue.rb index 6fc1a893c..27ab48af2 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -62,6 +62,7 @@ class Issue < ApplicationRecord acts_as_mentionable :attributes => ['description'] acts_as_webhookable include Issue::Webhookable + include Issue::AutoWatchable DONE_RATIO_OPTIONS = %w(issue_field issue_status) @@ -2109,27 +2110,6 @@ class Issue < ApplicationRecord end end - def add_auto_watcher - if author.is_a?(User) && - author.pref.auto_watch_on?('issue_created') && - auto_watcher?(author) - self.set_watcher(author, true) - end - - if assigned_to.is_a?(User) && - assigned_to.pref.auto_watch_on?('issue_assigned_to_me') && - auto_watcher?(assigned_to) - self.set_watcher(assigned_to, true) - end - end - - def auto_watcher?(user) - user.active? && - user.allowed_to?(:add_issue_watchers, project) && - valid_watcher?(user) && - !watched_by?(user) - end - def send_notification if notify? && Setting.notified_events.include?('issue_added') Mailer.deliver_issue_add(self) diff --git a/app/models/journal.rb b/app/models/journal.rb index 8435ec069..87c6435b6 100644 --- a/app/models/journal.rb +++ b/app/models/journal.rb @@ -354,25 +354,8 @@ class Journal < ApplicationRecord end def add_watcher - if user.is_a?(User) && - user.pref.auto_watch_on?('issue_contributed_to') && - valid_watcher?(user) - journalized.set_watcher(user, true) - end - - assignee = journalized.assigned_to - if assignee.is_a?(User) && - assignee.pref.auto_watch_on?('issue_assigned_to_me') && - valid_watcher?(assignee) - journalized.set_watcher(assignee, true) - end - end - - def valid_watcher?(user) - user.active? && - user.allowed_to?(:add_issue_watchers, journalized.project) && - journalized.valid_watcher?(user) && - !journalized.watched_by?(user) + journalized.auto_watch(user, 'issue_contributed_to') + journalized.auto_watch(journalized.assigned_to, 'issue_assigned_to_me') end def send_notification diff --git a/test/unit/issue_auto_watchable_test.rb b/test/unit/issue_auto_watchable_test.rb new file mode 100644 index 000000000..d67403344 --- /dev/null +++ b/test/unit/issue_auto_watchable_test.rb @@ -0,0 +1,120 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006- Jean-Philippe Lang +# +# 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. + +require_relative '../test_helper' + +class IssueAutoWatchableTest < ActiveSupport::TestCase + def setup + User.current = nil + end + + def test_auto_watch_should_add_watcher_if_option_is_enabled + issue = Issue.find(1) + user = User.find(2) + user.pref.auto_watch_on = ['issue_created'] + user.pref.save + + assert_difference 'Watcher.count', 1 do + issue.auto_watch(user, 'issue_created') + end + assert issue.watched_by?(user) + end + + def test_auto_watch_should_not_add_watcher_if_option_is_disabled + issue = Issue.find(1) + user = User.find(2) + user.pref.auto_watch_on = [] + user.pref.save + + assert_no_difference 'Watcher.count' do + issue.auto_watch(user, 'issue_created') + end + end + + def test_auto_watch_should_not_add_watcher_without_add_issue_watchers_permission + Role.find(1).remove_permission!(:add_issue_watchers) + issue = Issue.find(1) + user = User.find(2) + user.pref.auto_watch_on = ['issue_created'] + user.pref.save + + assert_no_difference 'Watcher.count' do + issue.auto_watch(user, 'issue_created') + end + end + + def test_auto_watch_should_not_add_watcher_if_user_cannot_view_issue + Role.find(1).remove_permission!(:view_issues) + issue = Issue.find(1) + user = User.find(2) + user.pref.auto_watch_on = ['issue_created'] + user.pref.save + + assert_no_difference 'Watcher.count' do + issue.auto_watch(user, 'issue_created') + end + end + + def test_auto_watch_should_not_add_locked_user + issue = Issue.find(1) + user = User.find(2) + user.pref.auto_watch_on = ['issue_created'] + user.pref.save + user.lock! + + assert_no_difference 'Watcher.count' do + issue.auto_watch(user, 'issue_created') + end + end + + def test_auto_watch_should_not_add_watcher_twice + issue = Issue.find(1) + user = User.find(2) + user.pref.auto_watch_on = ['issue_created'] + user.pref.save + issue.add_watcher(user) + + assert_no_difference 'Watcher.count' do + issue.auto_watch(user, 'issue_created') + end + end + + def test_auto_watch_should_not_add_watcher_if_a_group_of_the_user_is_watching + issue = Issue.find(1) + user = User.find(2) + user.pref.auto_watch_on = ['issue_created'] + user.pref.save + group = Group.find(10) + group.users << user + issue.add_watcher(group) + + assert_no_difference 'Watcher.count' do + issue.auto_watch(user, 'issue_created') + end + end + + def test_auto_watch_should_ignore_groups_and_nil + issue = Issue.find(1) + + assert_no_difference 'Watcher.count' do + issue.auto_watch(Group.find(10), 'issue_assigned_to_me') + issue.auto_watch(nil, 'issue_assigned_to_me') + end + end +end -- 2.54.0 (Apple Git-157)