Project

General

Profile

Feature #31859 » 0001-Per-role-visibility-settings-for-spent-time-custom-f.patch

Marius BĂLTEANU, 2019-08-07 08:03

View differences:

app/models/time_entry.rb
189 189
    editable_custom_field_values(user).map(&:custom_field).uniq
190 190
  end
191 191

  
192
  def visible_custom_field_values(user = nil)
193
    user ||= User.current
194
    custom_field_values.select do |value|
195
      value.custom_field.visible_by?(project, user)
196
    end
197
  end
198

  
192 199
  def assignable_users
193 200
    users = []
194 201
    if project
app/models/time_entry_custom_field.rb
21 21
  def type_name
22 22
    :label_spent_time
23 23
  end
24
end
25 24

  
25
  def visible_by?(project, user=User.current)
26
    super || (roles & user.roles_for_project(project)).present?
27
  end
28

  
29
  def validate_custom_field
30
    super
31
    errors.add(:base, l(:label_role_plural) + ' ' + l('activerecord.errors.messages.blank')) unless visible? || roles.present?
32
  end
33
end
app/views/custom_fields/_form.html.erb
31 31
</div>
32 32

  
33 33
<div class="splitcontentright">
34
<div class="box tabular">
35 34
<% case @custom_field.class.name
36 35
when "IssueCustomField" %>
37
    <p><%= f.check_box :is_required %></p>
38
    <% if @custom_field.format.is_filter_supported %>
39
    <p><%= f.check_box :is_filter %></p>
40
    <% end %>
41
    <% if @custom_field.format.searchable_supported %>
42
    <p><%= f.check_box :searchable %></p>
43
    <% end %>
36
    <div class="box tabular">
37
      <p><%= f.check_box :is_required %></p>
38
      <% if @custom_field.format.is_filter_supported %>
39
      <p><%= f.check_box :is_filter %></p>
40
      <% end %>
41
      <% if @custom_field.format.searchable_supported %>
42
      <p><%= f.check_box :searchable %></p>
43
      <% end %>
44
    </div>
45
    <%= render :partial => 'visibility_by_role_selector' %>
44 46

  
45 47
<% when "UserCustomField" %>
46
    <p><%= f.check_box :is_required %></p>
47
    <p><%= f.check_box :visible %></p>
48
    <p><%= f.check_box :editable %></p>
49
    <% if @custom_field.format.is_filter_supported %>
50
    <p><%= f.check_box :is_filter %></p>
51
    <% end %>
48
    <div class="box tabular">
49
      <p><%= f.check_box :is_required %></p>
50
      <p><%= f.check_box :visible %></p>
51
      <p><%= f.check_box :editable %></p>
52
      <% if @custom_field.format.is_filter_supported %>
53
      <p><%= f.check_box :is_filter %></p>
54
      <% end %>
55
    </div>
52 56

  
53 57
<% when "ProjectCustomField" %>
54
    <p><%= f.check_box :is_required %></p>
55
    <p><%= f.check_box :visible %></p>
56
    <% if @custom_field.format.searchable_supported %>
57
    <p><%= f.check_box :searchable %></p>
58
    <% end %>
59
    <% if @custom_field.format.is_filter_supported %>
60
    <p><%= f.check_box :is_filter %></p>
61
    <% end %>
58
    <div class="box tabular">
59
      <p><%= f.check_box :is_required %></p>
60
      <p><%= f.check_box :visible %></p>
61
      <% if @custom_field.format.searchable_supported %>
62
      <p><%= f.check_box :searchable %></p>
63
      <% end %>
64
      <% if @custom_field.format.is_filter_supported %>
65
      <p><%= f.check_box :is_filter %></p>
66
      <% end %>
67
    </div>
62 68

  
63 69
<% when "VersionCustomField" %>
64
    <p><%= f.check_box :is_required %></p>
65
    <% if @custom_field.format.is_filter_supported %>
66
    <p><%= f.check_box :is_filter %></p>
67
    <% end %>
70
    <div class="box tabular">
71
      <p><%= f.check_box :is_required %></p>
72
      <% if @custom_field.format.is_filter_supported %>
73
      <p><%= f.check_box :is_filter %></p>
74
      <% end %>
75
    </div>
68 76

  
69 77
<% when "GroupCustomField" %>
70
    <p><%= f.check_box :is_required %></p>
71
    <% if @custom_field.format.is_filter_supported %>
72
    <p><%= f.check_box :is_filter %></p>
73
    <% end %>
78
    <div class="box tabular">
79
      <p><%= f.check_box :is_required %></p>
80
      <% if @custom_field.format.is_filter_supported %>
81
      <p><%= f.check_box :is_filter %></p>
82
      <% end %>
83
    </div>
74 84

  
75 85
<% when "TimeEntryCustomField" %>
76
    <p><%= f.check_box :is_required %></p>
77
    <% if @custom_field.format.is_filter_supported %>
78
    <p><%= f.check_box :is_filter %></p>
79
    <% end %>
86
    <div class="box tabular">
87
      <p><%= f.check_box :is_required %></p>
88
      <% if @custom_field.format.is_filter_supported %>
89
      <p><%= f.check_box :is_filter %></p>
90
      <% end %>
91
    </div>
92
    <%= render :partial => 'visibility_by_role_selector' %>
80 93

  
81 94
<% else %>
95
  <div class="box tabular">
82 96
    <p><%= f.check_box :is_required %></p>
83

  
97
  </div>
84 98
<% end %>
85 99
<%= call_hook(:"view_custom_fields_form_#{@custom_field.type.to_s.underscore}", :custom_field => @custom_field, :form => f) %>
86
</div>
87 100

  
88 101
<% if @custom_field.is_a?(IssueCustomField) %>
89 102

  
90
  <fieldset class="box tabular"><legend><%= l(:field_visible) %></legend>
91
    <label class="block">
92
      <%= radio_button_tag 'custom_field[visible]', 1, @custom_field.visible?, :id => 'custom_field_visible_on',
93
            :data => {:disables => '.custom_field_role input'} %>
94
      <%= l(:label_visibility_public) %>
95
    </label>
96
    <label class="block">
97
      <%= radio_button_tag 'custom_field[visible]', 0, !@custom_field.visible?, :id => 'custom_field_visible_off',
98
            :data => {:enables => '.custom_field_role input'} %>
99
      <%= l(:label_visibility_roles) %>:
100
    </label>
101
    <% role_ids = @custom_field.role_ids %>
102
    <% Role.givable.sorted.each do |role| %>
103
      <label class="block custom_field_role" style="padding-left:2em;">
104
        <%= check_box_tag 'custom_field[role_ids][]', role.id, role_ids.include?(role.id), :id => nil %>
105
        <%= role.name %>
106
      </label>
107
    <% end %>
108
    <%= hidden_field_tag 'custom_field[role_ids][]', '' %>
109
  </fieldset>
110

  
111 103
  <fieldset class="box" id="custom_field_tracker_ids"><legend><%= toggle_checkboxes_link("#custom_field_tracker_ids input[type=checkbox]") %><%=l(:label_tracker_plural)%></legend>
112 104
  <% tracker_ids = @custom_field.tracker_ids %>
113 105
  <% Tracker.sorted.each do |tracker| %>
app/views/custom_fields/_visibility_by_role_selector.html.erb
1
<fieldset class="box tabular"><legend><%= l(:field_visible) %></legend>
2
  <label class="block">
3
    <%= radio_button_tag 'custom_field[visible]', 1, @custom_field.visible?, :id => 'custom_field_visible_on',
4
          :data => {:disables => '.custom_field_role input'} %>
5
    <%= l(:label_visibility_public) %>
6
  </label>
7
  <label class="block">
8
    <%= radio_button_tag 'custom_field[visible]', 0, !@custom_field.visible?, :id => 'custom_field_visible_off',
9
          :data => {:enables => '.custom_field_role input'} %>
10
    <%= l(:label_visibility_roles) %>:
11
  </label>
12
  <% role_ids = @custom_field.role_ids %>
13
  <% Role.givable.sorted.each do |role| %>
14
    <label class="block custom_field_role" style="padding-left:2em;">
15
      <%= check_box_tag 'custom_field[role_ids][]', role.id, role_ids.include?(role.id), :id => nil %>
16
      <%= role.name %>
17
    </label>
18
  <% end %>
19
  <%= hidden_field_tag 'custom_field[role_ids][]', '' %>
20
</fieldset>
app/views/timelog/_form.html.erb
23 23
  <p><%= f.hours_field :hours, :size => 6, :required => true %></p>
24 24
  <p><%= f.text_field :comments, :size => 100, :maxlength => 1024, :required => Setting.timelog_required_fields.include?('comments') %></p>
25 25
  <p><%= f.select :activity_id, activity_collection_for_select_options(@time_entry), :required => true %></p>
26
  <% @time_entry.custom_field_values.each do |value| %>
26
  <% @time_entry.editable_custom_field_values.each do |value| %>
27 27
    <p><%= custom_field_tag_with_label :time_entry, value %></p>
28 28
  <% end %>
29 29
  <%= call_hook(:view_timelog_edit_form_bottom, { :time_entry => @time_entry, :form => f }) %>
app/views/timelog/index.api.rsb
12 12
      api.created_on time_entry.created_on
13 13
      api.updated_on time_entry.updated_on
14 14

  
15
      render_api_custom_values time_entry.custom_field_values, api
15
      render_api_custom_values time_entry.visible_custom_field_values, api
16 16
    end
17 17
  end
18 18
end
test/functional/custom_fields_controller_test.rb
95 95
        assert_select 'option[value=user]', :text => 'User'
96 96
        assert_select 'option[value=version]', :text => 'Version'
97 97
      end
98

  
99
      # Visibility
100
      assert_select 'input[type=radio][name=?]', 'custom_field[visible]', 2
101
      assert_select 'input[type=checkbox][name=?]', 'custom_field[role_ids][]', 3
102

  
98 103
      assert_select 'input[type=checkbox][name=?]', 'custom_field[project_ids][]', Project.count
99 104
      assert_select 'input[type=hidden][name=?]', 'custom_field[project_ids][]', 1
100 105
      assert_select 'input[type=hidden][name=type][value=IssueCustomField]'
101 106
    end
102 107
  end
103 108

  
109
  def test_new_time_entry_custom_field
110
    get :new, :params => {
111
        :type => 'TimeEntryCustomField'
112
      }
113
    assert_response :success
114

  
115
    assert_select 'form#custom_field_form' do
116
      assert_select 'select#custom_field_field_format[name=?]', 'custom_field[field_format]' do
117
        assert_select 'option[value=user]', :text => 'User'
118
        assert_select 'option[value=version]', :text => 'Version'
119
      end
120

  
121
      # Visibility
122
      assert_select 'input[type=radio][name=?]', 'custom_field[visible]', 2
123
      assert_select 'input[type=checkbox][name=?]', 'custom_field[role_ids][]', 3
124

  
125
      assert_select 'input[type=hidden][name=type][value=TimeEntryCustomField]'
126
    end
127
  end
128

  
104 129
  def test_new_time_entry_custom_field_should_not_show_trackers_and_projects
105 130
    get :new, :params => {
106 131
        :type => 'TimeEntryCustomField'
test/functional/timelog_custom_fields_visibility_test.rb
34 34
           :workflows,
35 35
           :custom_fields, :custom_values, :custom_fields_trackers
36 36

  
37
  def setup
38
    field_attributes = {:field_format => 'string', :is_for_all => true, :is_filter => true, :trackers => Tracker.all}
39
    @fields = []
40
    @fields << (@field1 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 1', :visible => true)))
41
    @fields << (@field2 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 2', :visible => false, :role_ids => [1, 2])))
42
    @fields << (@field3 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 3', :visible => false, :role_ids => [1, 3])))
43
    @issue = Issue.generate!(
44
      :author_id => 1,
45
      :project_id => 1,
46
      :tracker_id => 1,
47
      :custom_field_values => {@field1.id => 'Value0', @field2.id => 'Value1', @field3.id => 'Value2'}
48
    )
49
    TimeEntry.generate!(:issue => @issue)
50

  
51
    @user_with_role_on_other_project = User.generate!
52
    User.add_to_project(@user_with_role_on_other_project, Project.find(2), Role.find(3))
53

  
54
    @users_to_test = {
55
      User.find(1) => [@field1, @field2, @field3],
56
      User.find(3) => [@field1, @field2],
57
      @user_with_role_on_other_project => [@field1], # should see field1 only on Project 1
58
      User.generate! => [@field1],
59
      User.anonymous => [@field1]
60
    }
61

  
62
    Member.where(:project_id => 1).each do |member|
63
      member.destroy unless @users_to_test.keys.include?(member.principal)
64
    end
65
  end
66

  
67 37
  def test_index_should_show_visible_custom_fields_only
38
    prepare_test_data
39

  
68 40
    @users_to_test.each do |user, fields|
69 41
      @request.session[:user_id] = user.id
70 42
      get :index, :params => {
......
83 55
  end
84 56

  
85 57
  def test_index_as_csv_should_show_visible_custom_fields_only
58
    prepare_test_data
59

  
86 60
    @users_to_test.each do |user, fields|
87 61
      @request.session[:user_id] = user.id
88 62
      get :index, :params => {
......
102 76
  end
103 77

  
104 78
  def test_index_with_partial_custom_field_visibility_should_show_visible_custom_fields_only
79
    prepare_test_data
80

  
105 81
    Issue.delete_all
106 82
    TimeEntry.delete_all
107 83
    CustomValue.delete_all
......
131 107
    assert_select 'td', :text => "ValueC"
132 108
    assert_select 'td', :text => "ValueB", :count => 0
133 109
  end
110

  
111
  def test_edit_should_not_show_custom_fields_not_visible_for_user
112
    time_entry_cf = TimeEntryCustomField.find(10)
113
    time_entry_cf.visible = false
114
    time_entry_cf.role_ids = [2]
115
    time_entry_cf.save!
116

  
117
    @request.session[:user_id] = 2
118

  
119
    get :edit, :params => {
120
      :id => 3,
121
      :project_id => 1
122
    }
123

  
124
    assert_response :success
125
    assert_select 'select#time_entry_custom_field_values_10', 0
126
  end
127

  
128
  private
129

  
130
  def prepare_test_data
131
    field_attributes = {:field_format => 'string', :is_for_all => true, :is_filter => true, :trackers => Tracker.all}
132
    @fields = []
133
    @fields << (@field1 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 1', :visible => true)))
134
    @fields << (@field2 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 2', :visible => false, :role_ids => [1, 2])))
135
    @fields << (@field3 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 3', :visible => false, :role_ids => [1, 3])))
136
    @issue = Issue.generate!(
137
      :author_id => 1,
138
      :project_id => 1,
139
      :tracker_id => 1,
140
      :custom_field_values => {@field1.id => 'Value0', @field2.id => 'Value1', @field3.id => 'Value2'}
141
    )
142
    TimeEntry.generate!(:issue => @issue)
143

  
144
    @user_with_role_on_other_project = User.generate!
145
    User.add_to_project(@user_with_role_on_other_project, Project.find(2), Role.find(3))
146

  
147
    @users_to_test = {
148
      User.find(1) => [@field1, @field2, @field3],
149
      User.find(3) => [@field1, @field2],
150
      @user_with_role_on_other_project => [@field1], # should see field1 only on Project 1
151
      User.generate! => [@field1],
152
      User.anonymous => [@field1]
153
    }
154

  
155
    Member.where(:project_id => 1).each do |member|
156
      member.destroy unless @users_to_test.keys.include?(member.principal)
157
    end
158
  end
134 159
end
test/functional/timelog_report_test.rb
138 138

  
139 139
  def test_hidden_custom_fields_should_not_be_proposed
140 140
    TimeEntryCustomField.create!(name: 'shown', field_format: 'list', possible_values: ['value1', 'value2'], visible: true)
141
    TimeEntryCustomField.create!(name: 'Hidden', field_format: 'list', possible_values: ['value1', 'value2'], visible: false)
141
    TimeEntryCustomField.create!(name: 'Hidden', field_format: 'list', possible_values: ['value1', 'value2'], visible: false, role_ids: [3])
142 142

  
143 143
    get :report, :params => {:project_id => 1}
144 144
    assert_response :success
(1-1/4)