Patch by Felix Schäfer (@felix). git-svn-id: https://svn.redmine.org/redmine/trunk@22793 e93f8b46-1217-0410-a6f0-8f06a7374b81pull/149/merge
tr.project.closed a, tr.project.archived a { color: #aaa; } | tr.project.closed a, tr.project.archived a { color: #aaa; } | ||||
tr.issue { text-align: center; white-space: nowrap; } | 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.relations { text-align: left; } | ||||
tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;} | 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 {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 span {font-weight: bold; display: block; margin-bottom: 4px;} | ||||
table.issues td.block_column pre {white-space:normal;} | table.issues td.block_column pre {white-space:normal;} |
tr.project.idnt-8 td.name {padding-left:0; padding-right:11em;} | 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.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;} | 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;} | table.issues td.description {padding:4px 24px 4px 4px; text-align:right;} |
link_to_if(value > 0, format_hours(value), project_time_entries_path(item.project, :issue_id => "~#{item.id}")) | link_to_if(value > 0, format_hours(value), project_time_entries_path(item.project, :issue_id => "~#{item.id}")) | ||||
when :attachments | when :attachments | ||||
value.to_a.map {|a| format_object(a)}.join(" ").html_safe | 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 | else | ||||
format_object(value) | format_object(value) | ||||
end | end | ||||
case column.name | case column.name | ||||
when :attachments | when :attachments | ||||
value.to_a.map {|a| a.filename}.join("\n") | value.to_a.map {|a| a.filename}.join("\n") | ||||
when :watcher_users | |||||
value.to_a.join("\n") | |||||
else | else | ||||
format_object(value, false) do |value| | format_object(value, false) do |value| | ||||
case value.class.name | case value.class.name |
QueryColumn.new(:assigned_to, | QueryColumn.new(:assigned_to, | ||||
:sortable => lambda {User.fields_for_order_statement}, | :sortable => lambda {User.fields_for_order_statement}, | ||||
:groupable => true), | :groupable => true), | ||||
WatcherQueryColumn.new(:watcher_users, :caption => :label_issue_watchers), | |||||
TimestampQueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", | TimestampQueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", | ||||
:default_order => 'desc', :groupable => true), | :default_order => 'desc', :groupable => true), | ||||
QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => true), | QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => true), | ||||
if has_custom_field_column? | if has_custom_field_column? | ||||
scope = scope.preload(:custom_values) | scope = scope.preload(:custom_values) | ||||
end | end | ||||
if has_column?(:watcher_users) | |||||
scope = scope.preload(:watcher_users) | |||||
end | |||||
issues = scope.to_a | issues = scope.to_a | ||||
end | end | ||||
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 | class QueryAssociationColumn < QueryColumn | ||||
def initialize(association, attribute, options={}) | def initialize(association, attribute, options={}) | ||||
@association = association | @association = association |
value = " " * level + value | value = " " * level + value | ||||
when :attachments | when :attachments | ||||
value = value.to_a.map {|a| a.filename}.join("\n") | value = value.to_a.map {|a| a.filename}.join("\n") | ||||
when :watcher_users | |||||
value = value.to_a.join("\n") | |||||
end | end | ||||
if value.is_a?(Date) | if value.is_a?(Date) | ||||
format_date(value) | format_date(value) |
assert_include "\"source.rb\npicture.jpg\"", response.body | assert_include "\"source.rb\npicture.jpg\"", response.body | ||||
end | 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 | def test_index_with_estimated_hours_total | ||||
Issue.delete_all | Issue.delete_all | ||||
Issue.generate!(:estimated_hours => '5:30') | Issue.generate!(:estimated_hours => '5:30') |