diff --git a/app/helpers/queries_helper.rb b/app/helpers/queries_helper.rb index 01d10d883c..1d721d7528 100644 --- a/app/helpers/queries_helper.rb +++ b/app/helpers/queries_helper.rb @@ -234,6 +234,8 @@ module QueriesHelper link_to_if(value > 0, format_hours(value), project_time_entries_path(item.project, :issue_id => "~#{item.id}")) when :attachments value.to_a.map {|a| format_object(a)}.join(" ").html_safe + when :visible_watchers + value.to_a.map {|watcher| format_object(watcher.user)}.join(" ").html_safe else format_object(value) end @@ -252,6 +254,8 @@ module QueriesHelper case column.name when :attachments value.to_a.map {|a| a.filename}.join("\n") + when :visible_watchers + value.to_a.map {|watcher| watcher.user.name}.join("\n") else format_object(value, false) do |value| case value.class.name diff --git a/app/models/issue.rb b/app/models/issue.rb index b20da8d91c..f54323dba0 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -871,6 +871,14 @@ class Issue < ActiveRecord::Base result end + def visible_watchers + if User.current.allowed_to?(:view_issue_watchers, project) + watchers.reorder(id: :asc) + else + watchers.where(user: User.current) + end + end + # Returns the initial status of the issue # Returns nil for a new issue def status_was diff --git a/app/models/issue_query.rb b/app/models/issue_query.rb index 9b9b87cc9f..e99562f11b 100644 --- a/app/models/issue_query.rb +++ b/app/models/issue_query.rb @@ -46,6 +46,7 @@ class IssueQuery < Query QueryColumn.new(:last_updated_by, :sortable => lambda {User.fields_for_order_statement("last_journal_user")}), QueryColumn.new(:relations, :caption => :label_related_issues), QueryColumn.new(:attachments, :caption => :label_attachment_plural), + QueryColumn.new(:visible_watchers, :caption => :label_issue_watchers), QueryColumn.new(:description, :inline => false), QueryColumn.new(:last_notes, :caption => :label_last_notes, :inline => false) ] @@ -291,7 +292,7 @@ class IssueQuery < Query limit(options[:limit]). offset(options[:offset]) - scope = scope.preload([:tracker, :author, :assigned_to, :fixed_version, :category, :attachments] & columns.map(&:name)) + scope = scope.preload([:tracker, :author, :assigned_to, :fixed_version, :category, :attachments, :watchers] & columns.map(&:name)) if has_custom_field_column? scope = scope.preload(:custom_values) end diff --git a/lib/redmine/export/pdf/issues_pdf_helper.rb b/lib/redmine/export/pdf/issues_pdf_helper.rb index 7e2c8a85f0..23bdf5c12f 100644 --- a/lib/redmine/export/pdf/issues_pdf_helper.rb +++ b/lib/redmine/export/pdf/issues_pdf_helper.rb @@ -379,6 +379,8 @@ module Redmine value = " " * level + value when :attachments value = value.to_a.map {|a| a.filename}.join("\n") + when :visible_watchers + value = value.to_a.map {|watcher| watcher.user.name}.join("\n") end if value.is_a?(Date) format_date(value) diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css index 19fb90f586..02d1795f28 100644 --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -222,7 +222,7 @@ table.list th, .table-list-header { background-color:#EEEEEE; padding: 4px; whit table.list td {text-align:center; vertical-align:middle; padding-right:10px;} table.list td.id { width: 2%; text-align: center;} table.list td.name, table.list td.description, table.list td.subject, table.list td.comments, table.list td.roles, table.list td.attachments {text-align: left;} -table.list td.attachments a {display:block;} +table.list td.attachments a, table.list td.visible_watchers a {display:block;} table.list td.tick {width:15%} table.list td.checkbox { width: 15px; padding: 2px 0 0 0; } table.list td.checkbox input {padding:0px;} diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb index 62d96bd10b..b117a91cfa 100644 --- a/test/functional/issues_controller_test.rb +++ b/test/functional/issues_controller_test.rb @@ -43,7 +43,8 @@ class IssuesControllerTest < Redmine::ControllerTest :journal_details, :queries, :repositories, - :changesets + :changesets, + :watchers include Redmine::I18n @@ -1385,6 +1386,56 @@ class IssuesControllerTest < Redmine::ControllerTest assert_include "\"source.rb\npicture.jpg\"", response.body end + def test_index_with_permission_should_display_watchers_column + @request.session[:user_id] = 3 + User.find(3).roles.first.add_permission! :view_issue_watchers + get :index, :params => { + :c => %w(subject visible_watchers), + :set_filter => '1', + :sort => 'id' + } + assert_response :success + assert_select 'td.visible_watchers' + assert_select 'tr#issue-2' do + assert_select 'td.visible_watchers' do + assert_select 'a:nth-of-type(1)', :text => 'Redmine Admin' + assert_select 'a:nth-of-type(2)', :text => 'Dave Lopper' + end + end + end + + def test_index_without_permission_should_display_visible_watchers_only + @request.session[:user_id] = 3 + User.find(3).roles.first.remove_permission! :view_issue_watchers + + get :index, :params => { + :c => %w(subject visible_watchers), + :set_filter => '1', + :sort => 'id' + } + assert_response :success + assert_select 'td.visible_watchers' + assert_select 'tr#issue-2' do + assert_select 'td.visible_watchers' do + # You can only know that you are a watcher yourself + assert_select 'a:nth-of-type(1)', :text =>'Dave Lopper' + assert_select 'a', { :text => 'Redmine Admin', :count => 0 } + end + end + end + + def test_index_with_watchers_column_as_csv + @request.session[:user_id] = 2 + get :index, :params => { + :c => %w(subject visible_watchers), + :set_filter => '1', + :sort => 'id', + :format => 'csv' + } + assert_response :success + assert_include "\"Redmine Admin\nDave Lopper\"", response.body + end + def test_index_with_estimated_hours_total Issue.delete_all Issue.generate!(:estimated_hours => 5.5) diff --git a/test/unit/issue_test.rb b/test/unit/issue_test.rb index 432dd8745c..aea09700c3 100644 --- a/test/unit/issue_test.rb +++ b/test/unit/issue_test.rb @@ -2902,6 +2902,22 @@ class IssueTest < ActiveSupport::TestCase assert_equal [Journal.find(1), Journal.find(2)], Issue.find(1).journals_after('') end + def test_visible_watcher_with_permission_should_return_watchers + User.current = User.find(3) + User.current.roles.first.add_permission! :view_issue_watchers + + issue = Issue.find(2) + assert_equal issue.visible_watchers, issue.watchers.reorder(:id => :asc) + end + + def test_visible_watcher_without_permission_should_return_only_current_user_watcher + User.current = User.find(3) + User.current.roles.first.remove_permission! :view_issue_watchers + + issue = Issue.find(2) + assert_equal issue.visible_watchers, issue.watchers.where(:user_id => 3) + end + def test_css_classes_should_include_tracker issue = Issue.new(:tracker => Tracker.find(2)) classes = issue.css_classes.split(' ')