Project

General

Profile

Feature #29470 » 0001-Option-to-set-a-tracker-only-as-subtask.patch

Marius BĂLTEANU, 2020-04-05 13:26

View differences:

app/helpers/workflows_helper.rb
42 42
    select_tag name, option_tags, {:multiple => multiple}.merge(options)
43 43
  end
44 44

  
45
  def field_required?(field)
46
    field.is_a?(CustomField) ? field.is_required? : %w(project_id tracker_id subject priority_id is_private).include?(field)
45
  def field_required?(field, trackers=[])
46
    if field == 'parent_issue_id'
47
      subtasks = trackers.select {|t| t.subtask_only?}
48
      subtasks.count == trackers.count
49
    else
50
      field.is_a?(CustomField) ? field.is_required? : %w(project_id tracker_id subject priority_id is_private).include?(field)
51
    end
47 52
  end
48 53

  
49
  def field_permission_tag(permissions, status, field, roles)
54
  def field_permission_tag(permissions, status, field, roles, trackers=[])
50 55
    name = field.is_a?(CustomField) ? field.id.to_s : field
51 56
    options = [["", ""], [l(:label_readonly), "readonly"]]
52
    options << [l(:label_required), "required"] unless field_required?(field)
57
    options << [l(:label_required), "required"] unless field_required?(field, trackers)
53 58
    html_options = {}
54 59

  
55 60
    if perm = permissions[status.id][name]
app/models/issue.rb
64 64
  validates_presence_of :priority, :if => Proc.new {|issue| issue.new_record? || issue.priority_id_changed?}
65 65
  validates_presence_of :status, :if => Proc.new {|issue| issue.new_record? || issue.status_id_changed?}
66 66
  validates_presence_of :author, :if => Proc.new {|issue| issue.new_record? || issue.author_id_changed?}
67
  validates_presence_of :parent_issue_id, :if => Proc.new {|issue| issue.tracker && issue.tracker.subtask_only? && issue.tracker_id_changed?}
67 68

  
68 69
  validates_length_of :subject, :maximum => 255
69 70
  validates_inclusion_of :done_ratio, :in => 0..100
......
623 624
  # Returns the names of attributes that are read-only for user or the current user
624 625
  # For users with multiple roles, the read-only fields are the intersection of
625 626
  # read-only fields of each role
626
  # The result is an array of strings where sustom fields are represented with their ids
627
  # The result is an array of strings where custom fields are represented with their ids
627 628
  #
628 629
  # Examples:
629 630
  #   issue.read_only_attribute_names # => ['due_date', '2']
......
635 636
  # Returns the names of required attributes for user or the current user
636 637
  # For users with multiple roles, the required fields are the intersection of
637 638
  # required fields of each role
638
  # The result is an array of strings where sustom fields are represented with their ids
639
  # The result is an array of strings where custom fields are represented with their ids
639 640
  #
640 641
  # Examples:
641 642
  #   issue.required_attribute_names # => ['due_date', '2']
app/models/tracker.rb
70 70
    'name',
71 71
    'default_status_id',
72 72
    'is_in_roadmap',
73
    'subtask_only',
73 74
    'core_fields',
74 75
    'position',
75 76
    'custom_field_ids',
app/views/issues/_attributes.html.erb
61 61
<div class="splitcontentright">
62 62
<% if @issue.safe_attribute? 'parent_issue_id' %>
63 63
<p id="parent_issue"><%= f.text_field :parent_issue_id, :size => 10,
64
                                      :required => @issue.required_attribute?('parent_issue_id') %></p>
65
<%= javascript_tag "observeAutocompleteField('issue_parent_issue_id', '#{escape_javascript(auto_complete_issues_path(:project_id => @issue.project, :scope => Setting.cross_project_subtasks, :status => @issue.closed? ? 'c' : 'o', :issue_id => @issue.id))}')" %>
64
                                      :required => @issue.required_attribute?('parent_issue_id') || @issue.tracker.subtask_only? %></p>
65
<%= javascript_tag "observeAutocompleteField('issue_parent_issue_id', '#{escape_javascript auto_complete_issues_path(:project_id => @issue.project, :scope => Setting.cross_project_subtasks, :status => @issue.closed? ? 'c' : 'o', :issue_id => @issue.id)}')" %>
66 66
<% end %>
67 67

  
68 68
<% if @issue.safe_attribute? 'start_date' %>
app/views/trackers/_form.html.erb
10 10
        :include_blank => @tracker.default_status.nil?,
11 11
        :required => true %>
12 12
</p>
13
<p><%= f.check_box :subtask_only %></p>
13 14
<p><%= f.check_box :is_in_roadmap %></p>
14 15
<p><%= f.text_area :description, :rows => 4 %></p>
15 16
<p>
app/views/trackers/index.api.rsb
4 4
      api.id tracker.id
5 5
      api.name tracker.name
6 6
      api.default_status(:id => tracker.default_status.id, :name => tracker.default_status.name) unless tracker.default_status.nil?
7
      api.subtask_only tracker.subtask_only?
7 8
      api.description tracker.description
8 9
    end
9 10
  end
app/views/trackers/index.html.erb
9 9
  <thead><tr>
10 10
  <th><%=l(:label_tracker)%></th>
11 11
  <th><%=l(:field_default_status)%></th>
12
  <th><%=l(:field_subtask_only)%></th>
12 13
  <th><%=l(:field_description)%></th>
13 14
  <th></th>
14 15
  <th></th>
......
18 19
  <tr>
19 20
  <td class="name"><%= link_to tracker.name, edit_tracker_path(tracker) %></td>
20 21
  <td><%= tracker.default_status.name %></td>
22
  <td class="subtask"><%= checked_image tracker.subtask_only? %></td>
21 23
  <td class="description"><%= tracker.description %></td>
22 24
  <td>
23 25
    <% unless tracker.workflow_rules.exists? %>
app/views/workflows/permissions.html.erb
61 61
      <% @fields.each do |field, name| %>
62 62
      <tr>
63 63
        <td class="name">
64
          <%= name %> <%= content_tag('span', '*', :class => 'required') if field_required?(field) %>
64
          <%= name %> <%= content_tag('span', '*', :class => 'required') if field_required?(field, @trackers) %>
65 65
        </td>
66 66
        <% for status in @statuses -%>
67 67
        <td class="<%= @permissions[status.id][field].try(:join, ' ') %>" title="<%= name %> (<%= status.name %>)">
68
          <%= field_permission_tag(@permissions, status, field, @roles) %>
68
          <%= field_permission_tag(@permissions, status, field, @roles, @trackers) %>
69 69
          <% unless status == @statuses.last %><a href="#" class="repeat-value">&#187;</a><% end %>
70 70
        </td>
71 71
        <% end -%>
config/locales/en.yml
395 395
  field_history_default_tab: Issue's history default tab
396 396
  field_unique_id: Unique ID
397 397
  field_toolbar_language_options: Code highlighting toolbar languages
398
  field_subtask_only: Subtask only
398 399

  
399 400
  setting_app_title: Application title
400 401
  setting_welcome_text: Welcome text
db/migrate/20190625215748_add_subtask_only_to_trackers.rb
1
class AddSubtaskOnlyToTrackers < ActiveRecord::Migration[5.2]
2
  def up
3
    add_column :trackers, :subtask_only, :boolean, :default => false
4
  end
5

  
6
  def down
7
    remove_column :trackers, :subtask_only
8
  end
9
end
test/fixtures/projects_trackers.yml
1
--- 
2
projects_trackers_001: 
1
---
2
projects_trackers_001:
3 3
  project_id: 4
4 4
  tracker_id: 3
5
projects_trackers_002: 
5
projects_trackers_002:
6 6
  project_id: 1
7 7
  tracker_id: 1
8
projects_trackers_003: 
8
projects_trackers_003:
9 9
  project_id: 5
10 10
  tracker_id: 1
11
projects_trackers_004: 
11
projects_trackers_004:
12 12
  project_id: 1
13 13
  tracker_id: 2
14
projects_trackers_005: 
14
projects_trackers_005:
15 15
  project_id: 5
16 16
  tracker_id: 2
17
projects_trackers_006: 
17
projects_trackers_006:
18 18
  project_id: 5
19 19
  tracker_id: 3
20
projects_trackers_007: 
20
projects_trackers_007:
21 21
  project_id: 2
22 22
  tracker_id: 1
23
projects_trackers_008: 
23
projects_trackers_008:
24 24
  project_id: 2
25 25
  tracker_id: 2
26
projects_trackers_009: 
26
projects_trackers_009:
27 27
  project_id: 2
28 28
  tracker_id: 3
29
projects_trackers_010: 
29
projects_trackers_010:
30 30
  project_id: 3
31 31
  tracker_id: 2
32
projects_trackers_011: 
32
projects_trackers_011:
33 33
  project_id: 3
34 34
  tracker_id: 3
35
projects_trackers_012: 
35
projects_trackers_012:
36 36
  project_id: 4
37 37
  tracker_id: 1
38
projects_trackers_013: 
38
projects_trackers_013:
39 39
  project_id: 4
40 40
  tracker_id: 2
41
projects_trackers_014: 
41
projects_trackers_014:
42 42
  project_id: 1
43 43
  tracker_id: 3
44
projects_trackers_015: 
44
projects_trackers_015:
45 45
  project_id: 6
46 46
  tracker_id: 1
47 47
projects_trackers_016:
48 48
  project_id: 3
49 49
  tracker_id: 1
50
projects_trackers_017:
51
  project_id: 1
52
  tracker_id: 4
test/fixtures/trackers.yml
1
--- 
2
trackers_001: 
1
---
2
trackers_001:
3 3
  name: Bug
4 4
  id: 1
5 5
  is_in_chlog: true
6 6
  default_status_id: 1
7 7
  position: 1
8 8
  description: Description for Bug tracker
9
trackers_002: 
9
  subtask_only: false
10
trackers_002:
10 11
  name: Feature request
11 12
  id: 2
12 13
  is_in_chlog: true
13 14
  default_status_id: 1
14 15
  position: 2
15 16
  description: Description for Feature request tracker
16
trackers_003: 
17
  subtask_only: false
18
trackers_003:
17 19
  name: Support request
18 20
  id: 3
19 21
  is_in_chlog: false
20 22
  default_status_id: 1
21 23
  position: 3
24
  subtask_only: false
25
trackers_004:
26
  name: Subtask
27
  id: 4
28
  is_in_chlog: false
29
  default_status_id: 1
30
  position: 4
31
  subtask_only: true
test/functional/issues_controller_test.rb
3169 3169
    get(:new, :params => {:project_id => 1})
3170 3170
    assert_response :success
3171 3171
    assert_select 'select[name=?]', 'issue[tracker_id]' do
3172
      assert_select 'option', 3
3172
      assert_select 'option', 4
3173 3173
      assert_select 'option[value="1"][selected=selected]'
3174 3174
    end
3175 3175
  end
......
3190 3190
    )
3191 3191
    assert_response :success
3192 3192
    assert_select 'select[name=?]', 'issue[tracker_id]' do
3193
      assert_select 'option', 2
3193
      assert_select 'option', 3
3194 3194
      assert_select 'option[value="2"][selected=selected]'
3195 3195
      assert_select 'option[value="1"]', 0
3196 3196
    end
test/functional/trackers_controller_test.rb
31 31
    get :index
32 32
    assert_response :success
33 33
    assert_select 'table.trackers'
34

  
35
    # assert subtask column
36
    assert_select 'td.subtask .icon-checked', 1
34 37
  end
35 38

  
36 39
  def test_index_by_anonymous_should_redirect_to_login_form
......
49 52
    get :new
50 53
    assert_response :success
51 54
    assert_select 'input[name=?]', 'tracker[name]'
55
    assert_select 'input[name=?]', 'tracker[subtask_only]'
52 56
    assert_select 'select[name=?]', 'tracker[default_status_id]' do
53 57
      assert_select 'option[value=?][selected=selected]', IssueStatus.sorted.first.id.to_s
54 58
    end
......
72 76
    assert_equal Tracker::CORE_FIELDS, tracker.core_fields
73 77
    assert_equal [1, 6], tracker.custom_field_ids.sort
74 78
    assert_equal 0, tracker.workflow_rules.count
79
    assert !tracker.subtask_only
75 80
  end
76 81

  
77 82
  def test_create_with_disabled_core_fields
test/functional/workflows_controller_test.rb
246 246
    end
247 247
  end
248 248

  
249
  def test_get_permissions_with_role_and_subtask_tracker_should_mark_parent_task_field_required
250
    get :permissions, :params => {:role_id => 1, :tracker_id => 4}
251
    assert_response :success
252

  
253
    assert_select 'td.name', :text => 'Parent task *'
254
  end
255

  
256
  def test_get_permissions_with_role_and_multiple_subtask_trackers_should_mark_parent_task_field_required
257
    subtask = Tracker.find(1)
258
    subtask.subtask_only = true
259
    subtask.save!
260

  
261
    get :permissions, :params => {:role_id => 1, :tracker_id => [1, 4]}
262
    assert_response :success
263

  
264
    assert_select 'td.name', :text => 'Parent task *'
265
  end
266

  
267
  def test_get_permissions_with_role_and_mixed_trackers_should_not_mark_parent_task_field_required
268
    get :permissions, :params => {:role_id => 1, :tracker_id => [1, 4]}
269
    assert_response :success
270

  
271
    assert_select 'td.name', :text => 'Parent task'
272
  end
273

  
249 274
  def test_get_permissions_with_required_custom_field_should_not_show_required_option
250 275
    cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :tracker_ids => [1], :is_required => true)
251 276

  
test/integration/api_test/trackers_test.rb
30 30

  
31 31
    assert_select 'trackers[type=array] tracker id', :text => '2' do
32 32
      assert_select '~ name', :text => 'Feature request'
33
      assert_select '~ subtask_only', :text => 'false'
33 34
      assert_select '~ description', :text => 'Description for Feature request tracker'
34 35
    end
36

  
37
    assert_select 'trackers[type=array] tracker id', :text => '4' do
38
      assert_select '~ subtask_only', :text => 'true'
39
    end
35 40
  end
36 41
end
test/unit/issue_test.rb
214 214
    assert_include 'Parent task is invalid', issue.errors.full_messages
215 215
  end
216 216

  
217
  def test_create_with_subtask_tracker
218
    issue = Issue.new(:project_id => 1, :tracker_id => 4,
219
                      :author_id => 1, :subject => 'Group assignment',
220
                      :parent_issue_id => 1)
221

  
222
    assert issue.save
223
    assert_equal 1, issue.parent_issue_id
224
  end
225

  
226
  def test_create_with_subtask_tracker_should_require_parent_task
227
    issue = Issue.new(:project_id => 1, :tracker_id => 4,
228
                      :author_id => 1, :subject => 'Group assignment')
229

  
230
    assert !issue.save
231
    assert_include 'Parent task cannot be blank', issue.errors.full_messages
232
  end
233

  
217 234
  def assert_visibility_match(user, issues)
218 235
    assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
219 236
  end
......
1689 1706
    role.save!
1690 1707
    User.add_to_project(user, Project.find(1), role)
1691 1708

  
1692
    assert_equal [1, 2, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1709
    assert_equal [1, 2, 3, 4], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1693 1710
  end
1694 1711

  
1695 1712
  def test_allowed_target_trackers_with_one_role_allowed_on_some_trackers
......
1730 1747
    role2.save!
1731 1748
    User.add_to_project(user, Project.find(1), [role1, role2])
1732 1749

  
1733
    assert_equal [1, 2, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1750
    assert_equal [1, 2, 3, 4], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1734 1751
  end
1735 1752

  
1736 1753
  def test_allowed_target_trackers_should_not_consider_roles_without_add_issues_permission
test/unit/lib/redmine/acts/positioned_without_scope_test.rb
26 26
    t = Tracker.generate
27 27
    t.save!
28 28

  
29
    assert_equal 4, t.reload.position
29
    assert_equal 5, t.reload.position
30 30
  end
31 31

  
32 32
  def test_create_should_insert_at_given_position
......
35 35
    t.save!
36 36

  
37 37
    assert_equal 2, t.reload.position
38
    assert_equal [1, 3, 4, 2], Tracker.order(:id).pluck(:position)
38
    assert_equal [1, 3, 4, 5, 2], Tracker.order(:id).pluck(:position)
39 39
  end
40 40

  
41 41
  def test_destroy_should_remove_position
......
43 43
    Tracker.generate!
44 44
    t.destroy
45 45

  
46
    assert_equal [1, 2, 3, 4], Tracker.order(:id).pluck(:position)
46
    assert_equal [1, 2, 3, 4, 5], Tracker.order(:id).pluck(:position)
47 47
  end
48 48

  
49 49
  def test_update_should_update_positions
50 50
    t = Tracker.generate!
51
    assert_equal 4, t.position
51
    assert_equal 5, t.position
52 52

  
53 53
    t.position = 2
54 54
    t.save!
55
    assert_equal [1, 3, 4, 2], Tracker.order(:id).pluck(:position)
55
    assert_equal [1, 3, 4, 5, 2], Tracker.order(:id).pluck(:position)
56 56
  end
57 57
end
test/unit/tracker_test.rb
140 140
    assert tracker.respond_to?(:description)
141 141
    assert_equal tracker.description, "Description for Bug tracker"
142 142
  end
143

  
144
  def test_set_tracker_subtask_only
145
    tracker = Tracker.find(1)
146
    tracker.subtask_only = true
147

  
148
    assert tracker.save!
149
    assert tracker.subtask_only?
150
  end
143 151
end
(4-4/5)