Project

General

Profile

Feature #29894 » 29894v1.patch

Felix Schäfer, 2024-03-15 19:34

View differences:

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')
(3-3/4)