Feature #29894 » 29894v1.patch
app/assets/stylesheets/application.css | ||
---|---|---|
292 | 292 |
tr.project.closed a, tr.project.archived a { color: #aaa; } |
293 | 293 | |
294 | 294 |
tr.issue { text-align: center; white-space: nowrap; } |
295 |
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; } |
|
295 |
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; }
|
|
296 | 296 |
tr.issue td.relations { text-align: left; } |
297 | 297 |
tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;} |
298 |
tr.issue td.relations span {white-space: nowrap;} |
|
298 |
tr.issue td.relations span, tr.issue td.watcher_users a {white-space: nowrap;} |
|
299 |
tr.issue td.watcher_users ul {list-style: none; padding: 0; margin: 0} |
|
299 | 300 |
table.issues td.block_column {color:#777; font-size:90%; padding:4px 4px 4px 24px; text-align:left; white-space:normal;} |
300 | 301 |
table.issues td.block_column span {font-weight: bold; display: block; margin-bottom: 4px;} |
301 | 302 |
table.issues td.block_column pre {white-space:normal;} |
app/assets/stylesheets/rtl.css | ||
---|---|---|
61 | 61 |
tr.project.idnt-8 td.name {padding-left:0; padding-right:11em;} |
62 | 62 |
tr.project.idnt-9 td.name {padding-left:0; padding-right:12.5em;} |
63 | 63 | |
64 |
tr.issue td.subject, tr.issue td.relations { text-align:right; } |
|
64 |
tr.issue td.subject, tr.issue td.relations, tr.issue td.watcher_users { text-align:right; }
|
|
65 | 65 |
tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;} |
66 | 66 | |
67 | 67 |
table.issues td.description {padding:4px 24px 4px 4px; text-align:right;} |
app/helpers/queries_helper.rb | ||
---|---|---|
277 | 277 |
link_to_if(value > 0, format_hours(value), project_time_entries_path(item.project, :issue_id => "~#{item.id}")) |
278 | 278 |
when :attachments |
279 | 279 |
value.to_a.map {|a| format_object(a)}.join(" ").html_safe |
280 |
when :watcher_users |
|
281 |
content_tag('ul', value.to_a.map {|user| content_tag('li', format_object(user))}.join.html_safe) |
|
280 | 282 |
else |
281 | 283 |
format_object(value) |
282 | 284 |
end |
... | ... | |
300 | 302 |
case column.name |
301 | 303 |
when :attachments |
302 | 304 |
value.to_a.map {|a| a.filename}.join("\n") |
305 |
when :watcher_users |
|
306 |
value.to_a.join("\n") |
|
303 | 307 |
else |
304 | 308 |
format_object(value, false) do |value| |
305 | 309 |
case value.class.name |
app/models/issue_query.rb | ||
---|---|---|
40 | 40 |
QueryColumn.new(:assigned_to, |
41 | 41 |
:sortable => lambda {User.fields_for_order_statement}, |
42 | 42 |
:groupable => true), |
43 |
WatcherQueryColumn.new(:watcher_users, :caption => :label_issue_watchers), |
|
43 | 44 |
TimestampQueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", |
44 | 45 |
:default_order => 'desc', :groupable => true), |
45 | 46 |
QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => true), |
... | ... | |
404 | 405 |
if has_custom_field_column? |
405 | 406 |
scope = scope.preload(:custom_values) |
406 | 407 |
end |
408 |
if has_column?(:watcher_users) |
|
409 |
scope = scope.preload(:watcher_users) |
|
410 |
end |
|
407 | 411 | |
408 | 412 |
issues = scope.to_a |
409 | 413 |
app/models/query.rb | ||
---|---|---|
104 | 104 |
end |
105 | 105 |
end |
106 | 106 | |
107 |
class WatcherQueryColumn < QueryColumn |
|
108 |
def value_object(object) |
|
109 |
return nil unless User.current.allowed_to?(:"view_#{object.class.name.underscore}_watchers", object.try(:project)) |
|
110 | ||
111 |
super |
|
112 |
end |
|
113 |
end |
|
114 | ||
107 | 115 |
class QueryAssociationColumn < QueryColumn |
108 | 116 |
def initialize(association, attribute, options={}) |
109 | 117 |
@association = association |
lib/redmine/export/pdf/issues_pdf_helper.rb | ||
---|---|---|
402 | 402 |
value = " " * level + value |
403 | 403 |
when :attachments |
404 | 404 |
value = value.to_a.map {|a| a.filename}.join("\n") |
405 |
when :watcher_users |
|
406 |
value = value.to_a.join("\n") |
|
405 | 407 |
end |
406 | 408 |
if value.is_a?(Date) |
407 | 409 |
format_date(value) |
test/functional/issues_controller_test.rb | ||
---|---|---|
1923 | 1923 |
assert_include "\"source.rb\npicture.jpg\"", response.body |
1924 | 1924 |
end |
1925 | 1925 | |
1926 |
def test_index_with_watchers_column |
|
1927 |
@request.session[:user_id] = 2 |
|
1928 |
get( |
|
1929 |
:index, |
|
1930 |
:params => { |
|
1931 |
:c => %w(subject watcher_users), |
|
1932 |
:set_filter => '1', |
|
1933 |
:sort => 'id', |
|
1934 |
} |
|
1935 |
) |
|
1936 | ||
1937 |
assert_response :success |
|
1938 |
assert_select 'td.watcher_users' |
|
1939 |
assert_select 'tr#issue-2' do |
|
1940 |
assert_select 'td.watcher_users' do |
|
1941 |
assert_select 'a[href=?]', '/users/1', :text => User.find(1).name |
|
1942 |
assert_select 'a[href=?]', '/users/3', :text => User.find(3).name |
|
1943 |
end |
|
1944 |
end |
|
1945 |
end |
|
1946 | ||
1947 |
def test_index_with_watchers_column_only_visible_watchers |
|
1948 |
@request.session[:user_id] = 3 |
|
1949 |
User.find(3).roles.first.remove_permission! :view_issue_watchers |
|
1950 |
get( |
|
1951 |
:index, |
|
1952 |
:params => { |
|
1953 |
:c => %w(subject watcher_users), |
|
1954 |
:set_filter => '1', |
|
1955 |
:sort => 'id', |
|
1956 |
} |
|
1957 |
) |
|
1958 | ||
1959 |
assert_response :success |
|
1960 |
assert_select 'td.watcher_users' |
|
1961 |
assert_select 'tr#issue-2' do |
|
1962 |
assert_select 'td.watcher_users' do |
|
1963 |
assert_select 'a[href=?]', '/users/1', 0 |
|
1964 |
# Currently not implemented, see https://www.redmine.org/issues/29894#note-17 |
|
1965 |
# You can only know that you are a watcher yourself |
|
1966 |
# assert_select 'a[href=?]', '/users/3', :text => User.find(3).name |
|
1967 |
end |
|
1968 |
end |
|
1969 |
end |
|
1970 | ||
1971 |
def test_index_with_watchers_column_as_csv |
|
1972 |
@request.session[:user_id] = 2 |
|
1973 |
get( |
|
1974 |
:index, |
|
1975 |
:params => { |
|
1976 |
:c => %w(subject watcher_users), |
|
1977 |
:set_filter => '1', |
|
1978 |
:sort => 'id', |
|
1979 |
:format => 'csv', |
|
1980 |
} |
|
1981 |
) |
|
1982 | ||
1983 |
assert_response :success |
|
1984 |
assert_include "\"#{User.find(1).name}\n#{User.find(3).name}\"", response.body |
|
1985 |
end |
|
1986 | ||
1926 | 1987 |
def test_index_with_estimated_hours_total |
1927 | 1988 |
Issue.delete_all |
1928 | 1989 |
Issue.generate!(:estimated_hours => '5:30') |