37462d378a vor 13 Tagen Felix Schäfer (HEAD) Add a watcher column to issue queries #967350 diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index fa77a8dab4..ba2d4d1495 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -292,10 +292,11 @@ tr.project.closed, tr.project.archived { color: #aaa; } tr.project.closed a, tr.project.archived a { color: #aaa; } tr.issue { text-align: center; white-space: nowrap; } -tr.issue td.subject, tr.issue td.parent-subject, tr.issue td.category, td.assigned_to, td.last_updated_by, tr.issue td.string, tr.issue td.text, tr.issue td.list, tr.issue td.relations, tr.issue td.parent { white-space: normal; } +tr.issue td.subject, tr.issue td.parent-subject, tr.issue td.category, td.assigned_to, td.last_updated_by, tr.issue td.string, tr.issue td.text, tr.issue td.list, tr.issue td.relations, tr.issue td.parent, tr.issue td.watcher_users { white-space: normal; } tr.issue td.relations { text-align: left; } tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;} -tr.issue td.relations span {white-space: nowrap;} +tr.issue td.relations span, tr.issue td.watcher_users a {white-space: nowrap;} +tr.issue td.watcher_users ul {list-style: none; padding: 0; margin: 0} table.issues td.block_column {color:#777; font-size:90%; padding:4px 4px 4px 24px; text-align:left; white-space:normal;} table.issues td.block_column span {font-weight: bold; display: block; margin-bottom: 4px;} table.issues td.block_column pre {white-space:normal;} diff --git a/app/assets/stylesheets/rtl.css b/app/assets/stylesheets/rtl.css index a8e919de60..65988f5097 100644 --- a/app/assets/stylesheets/rtl.css +++ b/app/assets/stylesheets/rtl.css @@ -61,7 +61,7 @@ tr.project.idnt-7 td.name {padding-left:0; padding-right:9.5em;} tr.project.idnt-8 td.name {padding-left:0; padding-right:11em;} tr.project.idnt-9 td.name {padding-left:0; padding-right:12.5em;} -tr.issue td.subject, tr.issue td.relations { text-align:right; } +tr.issue td.subject, tr.issue td.relations, tr.issue td.watcher_users { text-align:right; } tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;} table.issues td.description {padding:4px 24px 4px 4px; text-align:right;} diff --git a/app/helpers/queries_helper.rb b/app/helpers/queries_helper.rb index 7d132642b9..b9234bd1f8 100644 --- a/app/helpers/queries_helper.rb +++ b/app/helpers/queries_helper.rb @@ -277,6 +277,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 :watcher_users + content_tag('ul', value.to_a.map {|user| content_tag('li', format_object(user))}.join.html_safe) else format_object(value) end @@ -300,6 +302,8 @@ module QueriesHelper case column.name when :attachments value.to_a.map {|a| a.filename}.join("\n") + when :watcher_users + value.to_a.join("\n") else format_object(value, false) do |value| case value.class.name diff --git a/app/models/issue_query.rb b/app/models/issue_query.rb index 97df44b02f..91678874e8 100644 --- a/app/models/issue_query.rb +++ b/app/models/issue_query.rb @@ -40,6 +40,7 @@ class IssueQuery < Query QueryColumn.new(:assigned_to, :sortable => lambda {User.fields_for_order_statement}, :groupable => true), + WatcherQueryColumn.new(:watcher_users, :caption => :label_issue_watchers), TimestampQueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", :default_order => 'desc', :groupable => true), QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => true), @@ -404,6 +405,9 @@ class IssueQuery < Query if has_custom_field_column? scope = scope.preload(:custom_values) end + if has_column?(:watcher_users) + scope = scope.preload(:watcher_users) + end issues = scope.to_a diff --git a/app/models/query.rb b/app/models/query.rb index 2bcc1b1295..90970c96e6 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -104,6 +104,14 @@ class TimestampQueryColumn < QueryColumn end end +class WatcherQueryColumn < QueryColumn + def value_object(object) + return nil unless User.current.allowed_to?(:"view_#{object.class.name.underscore}_watchers", object.try(:project)) + + super + end +end + class QueryAssociationColumn < QueryColumn def initialize(association, attribute, options={}) @association = association diff --git a/lib/redmine/export/pdf/issues_pdf_helper.rb b/lib/redmine/export/pdf/issues_pdf_helper.rb index 6a6f09437c..1b94685f39 100644 --- a/lib/redmine/export/pdf/issues_pdf_helper.rb +++ b/lib/redmine/export/pdf/issues_pdf_helper.rb @@ -402,6 +402,8 @@ module Redmine value = " " * level + value when :attachments value = value.to_a.map {|a| a.filename}.join("\n") + when :watcher_users + value = value.to_a.join("\n") end if value.is_a?(Date) format_date(value) diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb index cce8ddc625..328fea523b 100644 --- a/test/functional/issues_controller_test.rb +++ b/test/functional/issues_controller_test.rb @@ -1923,6 +1923,67 @@ class IssuesControllerTest < Redmine::ControllerTest assert_include "\"source.rb\npicture.jpg\"", response.body end + def test_index_with_watchers_column + @request.session[:user_id] = 2 + get( + :index, + :params => { + :c => %w(subject watcher_users), + :set_filter => '1', + :sort => 'id', + } + ) + + assert_response :success + assert_select 'td.watcher_users' + assert_select 'tr#issue-2' do + assert_select 'td.watcher_users' do + assert_select 'a[href=?]', '/users/1', :text => User.find(1).name + assert_select 'a[href=?]', '/users/3', :text => User.find(3).name + end + end + end + + def test_index_with_watchers_column_only_visible_watchers + @request.session[:user_id] = 3 + User.find(3).roles.first.remove_permission! :view_issue_watchers + get( + :index, + :params => { + :c => %w(subject watcher_users), + :set_filter => '1', + :sort => 'id', + } + ) + + assert_response :success + assert_select 'td.watcher_users' + assert_select 'tr#issue-2' do + assert_select 'td.watcher_users' do + assert_select 'a[href=?]', '/users/1', 0 + # Currently not implemented, see https://www.redmine.org/issues/29894#note-17 + # You can only know that you are a watcher yourself + # assert_select 'a[href=?]', '/users/3', :text => User.find(3).name + end + end + end + + def test_index_with_watchers_column_as_csv + @request.session[:user_id] = 2 + get( + :index, + :params => { + :c => %w(subject watcher_users), + :set_filter => '1', + :sort => 'id', + :format => 'csv', + } + ) + + assert_response :success + assert_include "\"#{User.find(1).name}\n#{User.find(3).name}\"", response.body + end + def test_index_with_estimated_hours_total Issue.delete_all Issue.generate!(:estimated_hours => '5:30')