Project

General

Profile

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

Go MAEDA, 2020-09-28 10:12

View differences:

app/helpers/workflows_helper.rb
42 42
    select_tag name, option_tags, {:multiple => multiple}.merge(options)
43 43
  end
44
  def field_required?(field)
45
    field.is_a?(CustomField) ? field.is_required? : %w(project_id tracker_id subject priority_id is_private).include?(field)
44
  def field_required?(field, trackers=[])
45
    if field == 'parent_issue_id'
46
      subtasks = trackers.select {|t| t.subtask_only?}
47
      subtasks.count == trackers.count
48
    else
49
      field.is_a?(CustomField) ? field.is_required? : %w(project_id tracker_id subject priority_id is_private).include?(field)
50
    end
46 51
  end
47
  def field_permission_tag(permissions, status, field, roles)
52
  def field_permission_tag(permissions, status, field, roles, trackers=[])
48 53
    name = field.is_a?(CustomField) ? field.id.to_s : field
49 54
    options = [["", ""], [l(:label_readonly), "readonly"]]
50
    options << [l(:label_required), "required"] unless field_required?(field)
55
    options << [l(:label_required), "required"] unless field_required?(field, trackers)
51 56
    html_options = {}
52 57
    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
  validates_length_of :subject, :maximum => 255
68 69
  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
<% 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 %>)">
config/locales/en.yml
403 403
  field_history_default_tab: Issue's history default tab
404 404
  field_unique_id: Unique ID
405 405
  field_toolbar_language_options: Code highlighting toolbar languages
406
  field_subtask_only: Subtask only
406 407
  setting_app_title: Application title
407 408
  setting_welcome_text: Welcome text
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
3207 3207
    get(:new, :params => {:project_id => 1})
3208 3208
    assert_response :success
3209 3209
    assert_select 'select[name=?]', 'issue[tracker_id]' do
3210
      assert_select 'option', 3
3210
      assert_select 'option', 4
3211 3211
      assert_select 'option[value="1"][selected=selected]'
3212 3212
    end
3213 3213
  end
......
3228 3228
    )
3229 3229
    assert_response :success
3230 3230
    assert_select 'select[name=?]', 'issue[tracker_id]' do
3231
      assert_select 'option', 2
3231
      assert_select 'option', 3
3232 3232
      assert_select 'option[value="2"][selected=selected]'
3233 3233
      assert_select 'option[value="1"]', 0
3234 3234
    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
  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
  def test_create_with_disabled_core_fields
test/functional/workflows_controller_test.rb
246 246
    end
247 247
  end
248
  def test_get_permissions_with_role_and_subtask_tracker_should_mark_parent_task_field_required
249
    get :permissions, :params => {:role_id => 1, :tracker_id => 4}
250
    assert_response :success
251

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

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

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

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

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

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

  
248 273
  def test_get_permissions_with_required_custom_field_should_not_show_required_option
249 274
    cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :tracker_ids => [1], :is_required => true)
test/integration/api_test/trackers_test.rb
30 30
    assert_select 'trackers[type=array] tracker id', :text => '2' do
31 31
      assert_select '~ name', :text => 'Feature request'
32
      assert_select '~ subtask_only', :text => 'false'
32 33
      assert_select '~ description', :text => 'Description for Feature request tracker'
33 34
    end
35

  
36
    assert_select 'trackers[type=array] tracker id', :text => '4' do
37
      assert_select '~ subtask_only', :text => 'true'
38
    end
34 39
  end
35 40
end
test/unit/issue_test.rb
214 214
    assert_include 'Parent task is invalid', issue.errors.full_messages
215 215
  end
216
  def test_create_with_subtask_tracker
217
    issue = Issue.new(:project_id => 1, :tracker_id => 4,
218
                      :author_id => 1, :subject => 'Group assignment',
219
                      :parent_issue_id => 1)
220

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

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

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

  
216 233
  def assert_visibility_match(user, issues)
217 234
    assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
218 235
  end
......
1706 1723
    role.save!
1707 1724
    User.add_to_project(user, Project.find(1), role)
1708
    assert_equal [1, 2, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1725
    assert_equal [1, 2, 3, 4], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1709 1726
  end
1710 1727
  def test_allowed_target_trackers_with_one_role_allowed_on_some_trackers
......
1747 1764
    role2.save!
1748 1765
    User.add_to_project(user, Project.find(1), [role1, role2])
1749
    assert_equal [1, 2, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1766
    assert_equal [1, 2, 3, 4], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1750 1767
  end
1751 1768
  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
    assert_equal 4, t.reload.position
28
    assert_equal 5, t.reload.position
29 29
  end
30 30
  def test_create_should_insert_at_given_position
......
35 35
    t.save!
36 36
    assert_equal 2, t.reload.position
37
    assert_equal [1, 3, 4, 2], Tracker.order(:id).pluck(:position)
37
    assert_equal [1, 3, 4, 5, 2], Tracker.order(:id).pluck(:position)
38 38
  end
39 39
  def test_destroy_should_remove_position
......
43 43
    Tracker.generate!
44 44
    t.destroy
45
    assert_equal [1, 2, 3, 4], Tracker.order(:id).pluck(:position)
45
    assert_equal [1, 2, 3, 4, 5], Tracker.order(:id).pluck(:position)
46 46
  end
47 47
  def test_update_should_update_positions
48 48
    t = Tracker.generate!
49
    assert_equal 4, t.position
49
    assert_equal 5, t.position
50 50
    t.position = 2
51 51
    t.save!
52
    assert_equal [1, 3, 4, 2], Tracker.order(:id).pluck(:position)
52
    assert_equal [1, 3, 4, 5, 2], Tracker.order(:id).pluck(:position)
53 53
  end
54 54
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
(5-5/5)