Project

General

Profile

Feature #1675 » found-version.patch

v1.0.4 patch file for 'Found in version' feature - Brian Lindahl, 2011-01-06 01:37

View differences:

redmine/app/controllers/reports_controller.rb 2011-01-05 17:30:16.653641600 -0700
29 29
    @subprojects = @project.descendants.visible
30 30

  
31 31
    @issues_by_tracker = Issue.by_tracker(@project)
32
    @issues_by_version = Issue.by_version(@project)
32
    @issues_by_found_version = Issue.by_found_version(@project)
33
    @issues_by_fixed_version = Issue.by_fixed_version(@project)
33 34
    @issues_by_priority = Issue.by_priority(@project)
34 35
    @issues_by_category = Issue.by_category(@project)
35 36
    @issues_by_assigned_to = Issue.by_assigned_to(@project)
......
46 47
      @rows = @project.trackers
47 48
      @data = Issue.by_tracker(@project)
48 49
      @report_title = l(:field_tracker)
49
    when "version"
50
    when "found_version"
51
      @field = "found_version_id"
52
      @rows = @project.shared_versions.sort
53
      @data = Issue.by_found_version(@project)
54
      @report_title = l(:field_found_version)
55
    when "fixed_version"
50 56
      @field = "fixed_version_id"
51 57
      @rows = @project.shared_versions.sort
52
      @data = Issue.by_version(@project)
53
      @report_title = l(:field_version)
58
      @data = Issue.by_fixed_version(@project)
59
      @report_title = l(:field_fixed_version)
54 60
    when "priority"
55 61
      @field = "priority_id"
56 62
      @rows = IssuePriority.all
redmine/app/helpers/issues_helper.rb 2011-01-05 17:30:03.231680700 -0700
121 121
        value = format_date(detail.value.to_date) if detail.value
122 122
        old_value = format_date(detail.old_value.to_date) if detail.old_value
123 123

  
124
      when ['project_id', 'status_id', 'tracker_id', 'assigned_to_id', 'priority_id', 'category_id', 'fixed_version_id'].include?(detail.prop_key)
124
      when ['project_id', 'status_id', 'tracker_id', 'assigned_to_id', 'priority_id', 'category_id', 'found_version_id', 'fixed_version_id'].include?(detail.prop_key)
125 125
        value = find_name_by_reflection(field, detail.value)
126 126
        old_value = find_name_by_reflection(field, detail.old_value)
127 127

  
......
200 200
                  l(:field_subject),
201 201
                  l(:field_assigned_to),
202 202
                  l(:field_category),
203
                  l(:field_found_version),
203 204
                  l(:field_fixed_version),
204 205
                  l(:field_author),
205 206
                  l(:field_start_date),
......
227 228
                  issue.subject,
228 229
                  issue.assigned_to,
229 230
                  issue.category,
231
                  issue.found_version,
230 232
                  issue.fixed_version,
231 233
                  issue.author.name,
232 234
                  format_date(issue.start_date),
redmine/app/models/issue.rb 2011-01-05 17:28:50.199963300 -0700
21 21
  belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id'
22 22
  belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
23 23
  belongs_to :assigned_to, :class_name => 'User', :foreign_key => 'assigned_to_id'
24
  belongs_to :found_version, :class_name => 'Version', :foreign_key => 'found_version_id'
24 25
  belongs_to :fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id'
25 26
  belongs_to :priority, :class_name => 'IssuePriority', :foreign_key => 'priority_id'
26 27
  belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id'
......
121 122
      # reassign to the category with same name if any
122 123
      new_category = issue.category.nil? ? nil : new_project.issue_categories.find_by_name(issue.category.name)
123 124
      issue.category = new_category
124
      # Keep the fixed_version if it's still valid in the new_project
125
      # Keep the found version if it's still valid in the new_project
126
      unless new_project.shared_versions.include?(issue.found_version)
127
        issue.found_version = nil
128
      end
129
      # Keep the fixed version if it's still valid in the new_project
125 130
      unless new_project.shared_versions.include?(issue.fixed_version)
126 131
        issue.fixed_version = nil
127 132
      end
......
204 209
    category_id
205 210
    assigned_to_id
206 211
    priority_id
212
    found_version_id
207 213
    fixed_version_id
208 214
    subject
209 215
    description
......
273 279
      errors.add :start_date, :invalid
274 280
    end
275 281
    
282
    if found_version
283
      if !found_assignable_versions.include?(found_version)
284
        errors.add :found_version_id, :inclusion
285
      end
286
    end
287
	
276 288
    if fixed_version
277
      if !assignable_versions.include?(fixed_version)
289
      if !fixed_assignable_versions.include?(fixed_version)
278 290
        errors.add :fixed_version_id, :inclusion
279 291
      elsif reopened? && fixed_version.closed?
280 292
        errors.add_to_base I18n.t(:error_can_not_reopen_issue_on_closed_version)
......
370 382
    users.uniq.sort
371 383
  end
372 384
  
373
  # Versions that the issue can be assigned to
374
  def assignable_versions
375
    @assignable_versions ||= (project.shared_versions.open + [Version.find_by_id(fixed_version_id_was)]).compact.uniq.sort
385
  # Versions that the issue can be assigned as fixed to
386
  def found_assignable_versions
387
    @found_assignable_versions ||= (project.shared_versions.locked + [Version.find_by_id(found_version_id_was)]).compact.uniq.sort
388
  end
389
  
390
  # Versions that the issue can be assigned as fixed to
391
  def fixed_assignable_versions
392
    @fixed_assignable_versions ||= (project.shared_versions.open + [Version.find_by_id(fixed_version_id_was)]).compact.uniq.sort
376 393
  end
377 394
  
378 395
  # Returns true if this issue is blocked by another issue that is still open
......
527 544
  # Unassigns issues from +version+ if it's no longer shared with issue's project
528 545
  def self.update_versions_from_sharing_change(version)
529 546
    # Update issues assigned to the version
547
    update_versions(["#{Issue.table_name}.found_version_id = ?", version.id])
530 548
    update_versions(["#{Issue.table_name}.fixed_version_id = ?", version.id])
531 549
  end
532 550
  
......
563 581
                       :joins => Tracker.table_name)
564 582
  end
565 583

  
566
  def self.by_version(project)
584
  def self.by_found_version(project)
585
    count_and_group_by(:project => project,
586
                       :field => 'found_version_id',
587
                       :joins => Version.table_name)
588
  end
589
  
590
  def self.by_fixed_version(project)
567 591
    count_and_group_by(:project => project,
568 592
                       :field => 'fixed_version_id',
569 593
                       :joins => Version.table_name)
......
739 763
        issue.save
740 764
      end
741 765
    end
766
    # Only need to update issues with a found_version from
767
    # a different project and that is not systemwide shared
768
    Issue.all(:conditions => merge_conditions("#{Issue.table_name}.found_version_id IS NOT NULL" +
769
                                                " AND #{Issue.table_name}.project_id <> #{Version.table_name}.project_id" +
770
                                                " AND #{Version.table_name}.sharing <> 'system'",
771
                                                conditions),
772
              :include => [:project, :found_version]
773
              ).each do |issue|
774
      next if issue.project.nil? || issue.found_version.nil?
775
      unless issue.project.shared_versions.include?(issue.found_version)
776
        issue.init_journal(User.current)
777
        issue.found_version = nil
778
        issue.save
779
      end
780
    end
742 781
  end
743 782
  
744 783
  # Callback on attachment deletion
redmine/app/models/query.rb 2011-01-05 17:29:15.778252000 -0700
129 129
    QueryColumn.new(:assigned_to, :sortable => ["#{User.table_name}.lastname", "#{User.table_name}.firstname", "#{User.table_name}.id"], :groupable => true),
130 130
    QueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", :default_order => 'desc'),
131 131
    QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => true),
132
    QueryColumn.new(:found_version, :sortable => ["#{Version.table_name}.effective_date", "#{Version.table_name}.name"], :default_order => 'desc', :groupable => true),
132 133
    QueryColumn.new(:fixed_version, :sortable => ["#{Version.table_name}.effective_date", "#{Version.table_name}.name"], :default_order => 'desc', :groupable => true),
133 134
    QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"),
134 135
    QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"),
......
174 175
    @available_filters = { "status_id" => { :type => :list_status, :order => 1, :values => IssueStatus.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] } },       
175 176
                           "tracker_id" => { :type => :list, :order => 2, :values => trackers.collect{|s| [s.name, s.id.to_s] } },                                                                                                                
176 177
                           "priority_id" => { :type => :list, :order => 3, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] } },
177
                           "subject" => { :type => :text, :order => 8 },  
178
                           "created_on" => { :type => :date_past, :order => 9 },                        
179
                           "updated_on" => { :type => :date_past, :order => 10 },
180
                           "start_date" => { :type => :date, :order => 11 },
181
                           "due_date" => { :type => :date, :order => 12 },
182
                           "estimated_hours" => { :type => :integer, :order => 13 },
183
                           "done_ratio" =>  { :type => :integer, :order => 14 }}
178
                           "subject" => { :type => :text, :order => 9 },  
179
                           "created_on" => { :type => :date_past, :order => 10 },                        
180
                           "updated_on" => { :type => :date_past, :order => 11 },
181
                           "start_date" => { :type => :date, :order => 12 },
182
                           "due_date" => { :type => :date, :order => 13 },
183
                           "estimated_hours" => { :type => :integer, :order => 14 },
184
                           "done_ratio" =>  { :type => :integer, :order => 15 }}
184 185
    
185 186
    user_values = []
186 187
    user_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
......
197 198
    @available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } unless user_values.empty?
198 199
    
199 200
    if User.current.logged?
200
      @available_filters["watcher_id"] = { :type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]] }
201
      @available_filters["watcher_id"] = { :type => :list, :order => 16, :values => [["<< #{l(:label_me)} >>", "me"]] }
201 202
    end
202 203
  
203 204
    if project
......
206 207
        @available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => @project.issue_categories.collect{|s| [s.name, s.id.to_s] } }
207 208
      end
208 209
      unless @project.shared_versions.empty?
209
        @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => @project.shared_versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] } }
210
        @available_filters["found_version_id"] = { :type => :list_optional, :order => 7, :values => @project.shared_versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] } }
211
        @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 8, :values => @project.shared_versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] } }
210 212
      end
211 213
      unless @project.descendants.active.empty?
212
        @available_filters["subproject_id"] = { :type => :list_subprojects, :order => 13, :values => @project.descendants.visible.collect{|s| [s.name, s.id.to_s] } }
214
        @available_filters["subproject_id"] = { :type => :list_subprojects, :order => 14, :values => @project.descendants.visible.collect{|s| [s.name, s.id.to_s] } }
213 215
      end
214 216
      add_custom_fields_filters(@project.all_issue_custom_fields)
215 217
    else
216 218
      # global filters for cross project issue list
217 219
      system_shared_versions = Version.visible.find_all_by_sharing('system')
218 220
      unless system_shared_versions.empty?
219
        @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => system_shared_versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] } }
221
        @available_filters["found_version_id"] = { :type => :list_optional, :order => 7, :values => system_shared_versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] } }
222
        @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 8, :values => system_shared_versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] } }
220 223
      end
221 224
      add_custom_fields_filters(IssueCustomField.find(:all, :conditions => {:is_filter => true, :is_for_all => true}))
222 225
      # project filter
redmine/app/models/version.rb 2011-01-05 17:31:04.372697000 -0700
18 18
class Version < ActiveRecord::Base
19 19
  after_update :update_issues_from_sharing_change
20 20
  belongs_to :project
21
  has_many :found_issues, :class_name => 'Issue', :foreign_key => 'found_version_id', :dependent => :nullify
21 22
  has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id', :dependent => :nullify
22 23
  acts_as_customizable
23 24
  acts_as_attachable :view_permission => :view_files,
......
34 35
  validates_inclusion_of :sharing, :in => VERSION_SHARINGS
35 36

  
36 37
  named_scope :open, :conditions => {:status => 'open'}
38
  named_scope :locked, :conditions => {:status => 'locked'}
39
  named_scope :closed, :conditions => {:status => 'closed'}
37 40
  named_scope :visible, lambda {|*args| { :include => :project,
38 41
                                          :conditions => Project.allowed_to_condition(args.first || User.current, :view_issues) } }
39 42

  
redmine/app/views/issues/_attributes.rhtml 2011-01-05 17:30:55.325764100 -0700
18 18
                     :title => l(:label_issue_category_new), 
19 19
                     :tabindex => 199) if authorize_for('issue_categories', 'new') %></p>
20 20
<% end %>
21
<% unless @issue.assignable_versions.empty? %>
22
<p><%= f.select :fixed_version_id, version_options_for_select(@issue.assignable_versions, @issue.fixed_version), :include_blank => true %>
21
<% unless @issue.found_assignable_versions.empty? %>
22
<p><%= f.select :found_version_id, version_options_for_select(@issue.found_assignable_versions, @issue.found_version), :include_blank => true %></p>
23
<% end %>
24
<% unless @issue.fixed_assignable_versions.empty? %>
25
<p><%= f.select :fixed_version_id, version_options_for_select(@issue.fixed_assignable_versions, @issue.fixed_version), :include_blank => true %>
23 26
<%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
24 27
                     l(:label_version_new),
25 28
                     'version[name]', 
redmine/app/views/issues/show.rhtml 2011-01-05 17:30:39.966290800 -0700
36 36
    <% end %>
37 37
</tr>
38 38
<tr>
39
    <th class="fixed-version"><%=l(:field_fixed_version)%>:</th><td class="fixed-version"><%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %></td>
39
    <th class="found-version"><%=l(:field_found_version)%>:</th><td class="found-version"><%= @issue.found_version ? link_to_version(@issue.found_version) : "-" %></td>
40 40
    <% if @issue.estimated_hours %>
41 41
    <th class="estimated-hours"><%=l(:field_estimated_hours)%>:</th><td class="estimated-hours"><%= l_hours(@issue.estimated_hours) %></td>
42 42
    <% end %>
43 43
</tr>
44
<tr>
45
    <th class="fixed-version"><%=l(:field_fixed_version)%>:</th><td class="fixed-version"><%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %></td>
46
</tr>
44 47
<%= render_custom_fields_rows(@issue) %>
45 48
<%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
46 49
</table>
redmine/app/views/reports/issue_report.rhtml 2011-01-05 17:30:27.638086900 -0700
17 17
</div>
18 18

  
19 19
<div class="splitcontentright">
20
<h3><%=l(:field_version)%>&nbsp;&nbsp;<%= link_to image_tag('zoom_in.png'), :action => 'issue_report_details', :detail => 'version' %></h3>
21
<%= render :partial => 'simple', :locals => { :data => @issues_by_version, :field_name => "fixed_version_id", :rows => @versions } %>
20
<h3><%=l(:field_found_version)%>&nbsp;&nbsp;<%= link_to image_tag('zoom_in.png'), :action => 'issue_report_details', :detail => 'found_version' %></h3>
21
<%= render :partial => 'simple', :locals => { :data => @issues_by_found_version, :field_name => "found_version_id", :rows => @versions } %>
22
<br />
23
<h3><%=l(:field_fixed_version)%>&nbsp;&nbsp;<%= link_to image_tag('zoom_in.png'), :action => 'issue_report_details', :detail => 'fixed_version' %></h3>
24
<%= render :partial => 'simple', :locals => { :data => @issues_by_fixed_version, :field_name => "fixed_version_id", :rows => @versions } %>
22 25
<br />
23 26
<% if @project.children.any? %>
24 27
<h3><%=l(:field_subproject)%>&nbsp;&nbsp;<%= link_to image_tag('zoom_in.png'), :action => 'issue_report_details', :detail => 'subproject' %></h3>
redmine/config/locales/en.yml 2011-01-05 17:31:50.372991400 -0700
233 233
  field_due_date: Due date
234 234
  field_assigned_to: Assigned to
235 235
  field_priority: Priority
236
  field_found_version: Found in version
236 237
  field_fixed_version: Target version
237 238
  field_user: User
238 239
  field_principal: Principal
(2-2/2)