manual_set_issue_position_in_versions_v3.patch

Marius BALTEANU, 2016-11-19 08:34

Download (15 KB)

View differences:

app/controllers/issues_controller.rb
192 192

  
193 193
      respond_to do |format|
194 194
        format.html { redirect_back_or_default issue_path(@issue, previous_and_next_issue_ids_params) }
195
        format.js { render :nothing => true }
195 196
        format.api  { render_api_ok }
196 197
      end
197 198
    else
198 199
      respond_to do |format|
199 200
        format.html { render :action => 'edit' }
201
        format.js { render :nothing => true, :status => 422 }
200 202
        format.api  { render_validation_errors(@issue) }
201 203
      end
202 204
    end
......
380 382
  # Overrides Redmine::MenuManager::MenuController::ClassMethods for
381 383
  # when the "New issue" tab is enabled
382 384
  def current_menu_item
383
    if Setting.new_item_menu_tab == '1' && [:new, :create].include?(action_name.to_sym) 
385
    if Setting.new_item_menu_tab == '1' && [:new, :create].include?(action_name.to_sym)
384 386
      :new_issue
385 387
    else
386 388
      super
app/controllers/versions_controller.rb
46 46

  
47 47
        @issues_by_version = {}
48 48
        if @selected_tracker_ids.any? && @versions.any?
49

  
49 50
          issues = Issue.visible.
50 51
            includes(:project, :tracker).
51 52
            preload(:status, :priority, :fixed_version).
52 53
            where(:tracker_id => @selected_tracker_ids, :project_id => project_ids, :fixed_version_id => @versions.map(&:id)).
53
            order("#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id")
54
            order(order_issues_by)
54 55
          @issues_by_version = issues.group_by(&:fixed_version)
55 56
        end
56 57
        @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
......
67 68
        @issues = @version.fixed_issues.visible.
68 69
          includes(:status, :tracker, :priority).
69 70
          preload(:project).
70
          reorder("#{Tracker.table_name}.position, #{Issue.table_name}.id").
71
          reorder(order_issues_by).
71 72
          to_a
72 73
      }
73 74
      format.api
......
180 181
      @selected_tracker_ids = (default_trackers || selectable_trackers).collect {|t| t.id.to_s }
181 182
    end
182 183
  end
184

  
185
  def order_issues_by
186
    if Setting.manual_issue_position_in_versions == '1'
187
      return "COALESCE(#{Issue.table_name}.position, 999999)"
188
   else
189
      return "#{Tracker.table_name}.position, #{Issue.table_name}.id"
190
   end
191
  end
183 192
end
app/models/issue.rb
56 56

  
57 57
  acts_as_activity_provider :scope => preload(:project, :author, :tracker, :status),
58 58
                            :author_key => :author_id
59

  
59
  acts_as_positioned :scope => [:fixed_version_id]
60 60
  DONE_RATIO_OPTIONS = %w(issue_field issue_status)
61 61

  
62 62
  attr_accessor :deleted_attachment_ids
......
468 468
    :if => lambda {|issue, user| (issue.new_record? || issue.attributes_editable?(user)) &&
469 469
      user.allowed_to?(:manage_subtasks, issue.project)}
470 470

  
471
  safe_attributes 'position',
472
    :if => lambda {|issue, user| user.allowed_to?(:change_issue_position_in_version, issue.project)}
473

  
471 474
  safe_attributes 'deleted_attachment_ids',
472 475
    :if => lambda {|issue, user| issue.attachments_deletable?(user)}
473 476

  
......
781 784

  
782 785
  # Returns the names of attributes that are journalized when updating the issue
783 786
  def journalized_attribute_names
784
    names = Issue.column_names - %w(id root_id lft rgt lock_version created_on updated_on closed_on)
787
    names = Issue.column_names - %w(id root_id lft rgt lock_version position created_on updated_on closed_on)
785 788
    if tracker
786 789
      names -= tracker.disabled_core_fields
787 790
    end
app/views/settings/_issues.html.erb
13 13

  
14 14
<p><%= setting_check_box :display_subprojects_issues %></p>
15 15

  
16
<p><%= setting_check_box :manual_issue_position_in_versions %></p>
17

  
16 18
<p><%= setting_select :issue_done_ratio, Issue::DONE_RATIO_OPTIONS.collect {|i| [l("setting_issue_done_ratio_#{i}"), i]} %></p>
17 19

  
18 20
<p><%= setting_multiselect :non_working_week_days, (1..7).map {|d| [day_name(d), d.to_s]}, :inline => true %></p>
......
20 22
<p><%= setting_text_field :issues_export_limit, :size => 6 %></p>
21 23

  
22 24
<p><%= setting_text_field :gantt_items_limit, :size => 6 %></p>
25

  
23 26
</div>
24 27

  
25 28
<fieldset class="box">
app/views/versions/index.html.erb
26 26
      <%= form_tag({}, :data => {:cm_url => issues_context_menu_path}) do -%>
27 27
        <table class="list related-issues">
28 28
        <caption><%= l(:label_related_issues) %></caption>
29
        <% issues.each do |issue| -%>
30
          <tr class="hascontextmenu">
31
            <td class="checkbox"><%= check_box_tag 'ids[]', issue.id, false, :id => nil %></td>
32
            <td class="subject"><%= link_to_issue(issue, :project => (@project != issue.project)) %></td>
33
          </tr>
34
        <% end -%>
29
          <tbody>
30
            <% issues.each do |issue| -%>
31
              <tr class="hascontextmenu">
32
                <td class="checkbox"><%= check_box_tag 'ids[]', issue.id, false, :id => nil %></td>
33
                <td class="subject"><%= link_to_issue(issue, :project => (@project != issue.project)) %></td>
34
                <% if Setting.manual_issue_position_in_versions == '1' && User.current.allowed_to?(:change_issue_position_in_version, version.project) %>
35
                  <td class="sortable"><%= reorder_handle(issue) %></td>
36
                <% end%>
37
              </tr>
38
            <% end -%>
39
          </tbody>
35 40
        </table>
36 41
      <% end %>
37 42
    <% end %>
......
100 105
<% html_title(l(:label_roadmap)) %>
101 106

  
102 107
<%= context_menu %>
108

  
109
<% if Setting.manual_issue_position_in_versions == '1' && User.current.allowed_to?(:change_issue_position_in_version, @project) %>
110
  <%= javascript_tag do %>
111
    $(function() { $("table.related-issues tbody").positionedItems(); });
112
  <% end %>
113
<% end %>
app/views/versions/show.html.erb
38 38
<% if @issues.present? %>
39 39
<%= form_tag({}, :data => {:cm_url => issues_context_menu_path}) do -%>
40 40
  <table class="list related-issues">
41
  <caption><%= l(:label_related_issues) %></caption>
42
  <%- @issues.each do |issue| -%>
43
    <tr class="issue hascontextmenu">
44
      <td class="checkbox"><%= check_box_tag 'ids[]', issue.id, false, :id => nil %></td>
45
      <td class="subject"><%= link_to_issue(issue, :project => (@project != issue.project)) %></td>
46
    </tr>
47
  <% end %>
41
    <caption><%= l(:label_related_issues) %></caption>
42
    <tbody>
43
    <%- @issues.each do |issue| -%>
44
      <tr class="issue hascontextmenu">
45
        <td class="checkbox"><%= check_box_tag 'ids[]', issue.id, false, :id => nil %></td>
46
        <td class="subject"><%= link_to_issue(issue, :project => (@project != issue.project)) %></td>
47
        <% if Setting.manual_issue_position_in_versions == '1' && !@version.closed? && User.current.allowed_to?(:change_issue_position_in_version, @version.project) %>
48
          <td class="sortable"><%= reorder_handle(issue) %></td>
49
        <% end%>
50
      </tr>
51
    <% end %>
52
    </tbody>
48 53
  </table>
49 54
<% end %>
50 55
<%= context_menu %>
......
54 59
<%= call_hook :view_versions_show_bottom, :version => @version %>
55 60

  
56 61
<% html_title @version.name %>
62

  
63
<% if Setting.manual_issue_position_in_versions == '1' && !@version.closed? && User.current.allowed_to?(:change_issue_position_in_version, @version.project) %>
64
  <%= javascript_tag do %>
65
    $(function() { $("table.related-issues tbody").positionedItems(); });
66
  <% end %>
67
<% end %>
config/locales/en.yml
448 448
  setting_attachment_extensions_allowed: Allowed extensions
449 449
  setting_attachment_extensions_denied: Disallowed extensions
450 450
  setting_new_item_menu_tab: Project menu tab for creating new objects
451
  setting_manual_issue_position_in_versions: Enable manually set issue position in versions
451 452

  
452 453
  permission_add_project: Create project
453 454
  permission_add_subprojects: Create subprojects
......
514 515
  permission_manage_subtasks: Manage subtasks
515 516
  permission_manage_related_issues: Manage related issues
516 517
  permission_import_issues: Import issues
518
  permission_change_issue_position_in_version: Change issue position in version
517 519

  
518 520
  project_module_issue_tracking: Issue tracking
519 521
  project_module_time_tracking: Time tracking
config/settings.yml
171 171
  default: 'derived'
172 172
link_copied_issue:
173 173
  default: 'ask'
174
manual_issue_position_in_versions:
175
  default: 0
174 176
issue_group_assignment:
175 177
  default: 0
176 178
default_issue_start_date_to_creation_date:
db/migrate/20160901154541_add_issue_position.rb
1
class AddIssuePosition < ActiveRecord::Migration
2
  def up
3
    add_column :issues, :position, :integer
4
  end
5

  
6
  def down
7
    remove_column :issues, :position
8
  end
9
end
lib/redmine.rb
110 110
    map.permission :view_private_notes, {}, :read => true, :require => :member
111 111
    map.permission :set_notes_private, {}, :require => :member
112 112
    map.permission :delete_issues, {:issues => :destroy}, :require => :member
113
    map.permission :change_issue_position_in_version, {}
113 114
    # Queries
114 115
    map.permission :manage_public_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :member
115 116
    map.permission :save_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :loggedin
public/stylesheets/application.css
442 442

  
443 443
div#roadmap .related-issues { margin-bottom: 1em; }
444 444
div#roadmap .related-issues td.checkbox { display: none; }
445
div#roadmap .related-issues td.sortable { text-align: right; }
445 446
div#roadmap .wiki h1:first-child { display: none; }
446 447
div#roadmap .wiki h1 { font-size: 120%; }
447 448
div#roadmap .wiki h2 { font-size: 110%; }
test/fixtures/issues.yml
40 40
  rgt: 2
41 41
  lock_version: 3
42 42
  done_ratio: 30
43
  position: 1
43 44
issues_003:
44 45
  created_on: 2006-07-19 21:07:27 +02:00
45 46
  project_id: 1
test/functional/versions_controller_test.rb
77 77
    assert_select 'h3', :text => /Subproject version/
78 78
  end
79 79

  
80
  def test_issues_order_when_manual_issue_position_in_versions_is_disabled
81
    with_settings :manual_issue_position_in_versions => '0' do
82
      get :index, :params => {:project_id => 1}
83
      assert_response :success
84

  
85
      assert_select '#roadmap article:first-child' do
86
        assert_select '.related-issues tr:nth-child(1) a[href=?]', '/issues/12', :text => 'Bug #12'
87
        assert_select '.related-issues tr:nth-child(2) a[href=?]', '/issues/2', :text => 'Feature request #2'
88
      end
89

  
90
      get :show, :params => { :id => 2 }
91
      assert_response :success
92

  
93
      assert_select '.related-issues' do
94
        assert_select 'tr:nth-child(1) a[href=?]', '/issues/12', :text => 'Bug #12'
95
        assert_select 'tr:nth-child(2) a[href=?]', '/issues/2', :text => 'Feature request #2'
96
      end
97
    end
98
  end
99

  
100
  def test_issues_order_when_manual_issue_position_in_versions_is_enabled
101
    with_settings :manual_issue_position_in_versions => '1' do
102
      get :index, :params => {:project_id => 1}
103
      assert_response :success
104

  
105
      assert_select '#roadmap article:first-child' do
106
        assert_select '.related-issues tr:nth-child(1) a[href=?]', '/issues/2', :text => 'Feature request #2'
107
        assert_select '.related-issues tr:nth-child(2) a[href=?]', '/issues/12', :text => 'Bug #12'
108
      end
109

  
110
      get :show, :params => { :id => 2 }
111
      assert_response :success
112

  
113
      assert_select '.related-issues' do
114
        assert_select 'tr:nth-child(1) a[href=?]', '/issues/2', :text => 'Feature request #2'
115
        assert_select 'tr:nth-child(2) a[href=?]', '/issues/12', :text => 'Bug #12'
116
      end
117
    end
118
  end
119

  
120
  def test_user_with_permission_can_change_issue_position
121
    role = Role.find(1)
122
    role.add_permission! :change_issue_position_in_version
123

  
124
    @request.session[:user_id] = 2
125
    with_settings :manual_issue_position_in_versions => '1' do
126
      get :index, :params => {:project_id => 1}
127
      assert_response :success
128

  
129
      assert_select '#roadmap article' do
130
        assert_select '.related-issues tr td.sortable', 2
131
      end
132

  
133
      get :show, :params => { :id => 2 }
134
      assert_response :success
135

  
136
      assert_select '.related-issues' do
137
        assert_select '.related-issues tr td.sortable', 2
138
      end
139
    end
140
  end
141

  
142
  def test_user_without_permission_cannot_change_issue_position
143
    @request.session[:user_id] = 2
144
    with_settings :manual_issue_position_in_versions => '1' do
145
      get :index, :params => {:project_id => 1}
146
      assert_response :success
147

  
148
      assert_select '#roadmap article' do
149
        assert_select '.related-issues tr td.sortable', 0
150
      end
151

  
152
      get :show, :params => { :id => 2 }
153
      assert_response :success
154

  
155
      assert_select '.related-issues' do
156
        assert_select '.related-issues tr td.sortable', 0
157
      end
158
    end
159
  end
160

  
80 161
  def test_index_should_prepend_shared_versions
81 162
    get :index, :params => {:project_id => 1}
82 163
    assert_response :success