Patch #219 ยป multiple_fixed_versions.patch
app/models/issue.rb (working copy) | ||
---|---|---|
22 | 22 |
belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id' |
23 | 23 |
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' |
24 | 24 |
belongs_to :assigned_to, :class_name => 'User', :foreign_key => 'assigned_to_id' |
25 |
belongs_to :fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id' |
|
26 | 25 |
belongs_to :priority, :class_name => 'Enumeration', :foreign_key => 'priority_id' |
27 | 26 |
belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id' |
28 | 27 | |
... | ... | |
32 | 31 |
has_many :custom_values, :dependent => :delete_all, :as => :customized |
33 | 32 |
has_many :custom_fields, :through => :custom_values |
34 | 33 |
has_and_belongs_to_many :changesets, :order => "revision ASC" |
34 |
has_and_belongs_to_many :fixed_versions, :class_name => 'Version' |
|
35 | 35 |
|
36 | 36 |
acts_as_watchable |
37 | 37 |
|
app/models/version.rb (working copy) | ||
---|---|---|
18 | 18 |
class Version < ActiveRecord::Base |
19 | 19 |
before_destroy :check_integrity |
20 | 20 |
belongs_to :project |
21 |
has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id'
|
|
21 |
has_and_belongs_to_many :fixed_issues, :class_name => 'Issue'
|
|
22 | 22 |
has_many :attachments, :as => :container, :dependent => :destroy |
23 | 23 | |
24 | 24 |
validates_presence_of :name |
app/controllers/issues_controller.rb (working copy) | ||
---|---|---|
46 | 46 |
@priorities = Enumeration::get_values('IPRI') |
47 | 47 |
if request.get? |
48 | 48 |
@custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x, :customized => @issue) } |
49 |
setup_versions() |
|
49 | 50 |
else |
50 | 51 |
begin |
51 | 52 |
@issue.init_journal(self.logged_in_user) |
... | ... | |
53 | 54 |
@custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) } |
54 | 55 |
@issue.custom_values = @custom_values |
55 | 56 |
@issue.attributes = params[:issue] |
57 |
ensure_versions_saved() |
|
56 | 58 |
if @issue.save |
57 | 59 |
flash[:notice] = l(:notice_successful_update) |
58 | 60 |
redirect_to :action => 'show', :id => @issue |
... | ... | |
81 | 83 |
def change_status |
82 | 84 |
@status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user |
83 | 85 |
@new_status = IssueStatus.find(params[:new_status_id]) |
86 |
setup_versions() |
|
84 | 87 |
if params[:confirm] |
85 | 88 |
begin |
86 | 89 |
journal = @issue.init_journal(self.logged_in_user, params[:notes]) |
87 | 90 |
@issue.status = @new_status |
88 | 91 |
if @issue.update_attributes(params[:issue]) |
92 |
ensure_versions_saved() |
|
89 | 93 |
# Save attachments |
90 | 94 |
params[:attachments].each { |file| |
91 | 95 |
next unless file.size > 0 |
... | ... | |
156 | 160 |
rescue ActiveRecord::RecordNotFound |
157 | 161 |
render_404 |
158 | 162 |
end |
163 | ||
164 |
def setup_versions |
|
165 |
@released_versions = [] |
|
166 |
@unreleased_versions = [] |
|
167 |
@project.versions.find(:all, :order => "#{Version.table_name}.effective_date ASC").each { |v| (v.effective_date < Date.today ? @released_versions : @unreleased_versions) << v } |
|
168 |
end |
|
169 | ||
170 |
def ensure_versions_saved |
|
171 |
@issue.fixed_versions = [] unless params[:issue][:fixed_version_ids] |
|
172 |
end |
|
159 | 173 |
end |
app/controllers/projects_controller.rb (working copy) | ||
---|---|---|
434 | 434 |
def changelog |
435 | 435 |
@trackers = Tracker.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position') |
436 | 436 |
retrieve_selected_tracker_ids(@trackers) |
437 |
|
|
438 |
@fixed_issues = @project.issues.find(:all, |
|
439 |
:include => [ :fixed_version, :status, :tracker ], |
|
440 |
:conditions => [ "#{IssueStatus.table_name}.is_closed=? and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')}) and #{Issue.table_name}.fixed_version_id is not null", true], |
|
441 |
:order => "#{Version.table_name}.effective_date DESC, #{Issue.table_name}.id DESC" |
|
437 | ||
438 |
@versions = @project.versions.find(:all, |
|
439 |
:conditions => [ "#{Version.table_name}.effective_date<?", Date.today], |
|
440 |
:order => "#{Version.table_name}.effective_date DESC" |
|
442 | 441 |
) unless @selected_tracker_ids.empty? |
443 |
@fixed_issues ||= [] |
|
444 | 442 |
end |
445 | 443 | |
446 | 444 |
def roadmap |
app/views/projects/changelog.rhtml (working copy) | ||
---|---|---|
13 | 13 |
<% end %> |
14 | 14 |
</div> |
15 | 15 | |
16 |
<% if @fixed_issues.empty? %><p><i><%= l(:label_no_data) %></i></p><% end %>
|
|
16 |
<% if @versions.empty? %><p><i><%= l(:label_no_data) %></i></p><% end %>
|
|
17 | 17 | |
18 |
<% ver_id = nil |
|
19 |
@fixed_issues.each do |issue| %> |
|
20 |
<% unless ver_id == issue.fixed_version_id %> |
|
21 |
<% if ver_id %></ul><% end %> |
|
22 |
<h3 class="icon22 icon22-package"><%= issue.fixed_version.name %></h3> |
|
23 |
<p><%= format_date(issue.fixed_version.effective_date) %><br /> |
|
24 |
<%=h issue.fixed_version.description %></p> |
|
18 |
<% @versions.each do |version| %> |
|
19 |
<h3 class="icon22 icon22-package"><%= version.name %></h3> |
|
20 |
<p><%= format_date(version.effective_date) %><br /> |
|
21 |
<p><%=h version.description %></p> |
|
22 | ||
23 |
<% |
|
24 |
issues = version.fixed_issues.find(:all, |
|
25 |
:include => :status, |
|
26 |
:conditions => ["tracker_id in (#{@selected_tracker_ids.join(',')}) and is_closed is true"], |
|
27 |
:order => "position") |
|
28 |
%> |
|
29 | ||
25 | 30 |
<ul> |
26 |
<% ver_id = issue.fixed_version_id |
|
27 |
end %> |
|
28 |
<li><%= link_to_issue issue %>: <%=h issue.subject %></li> |
|
31 |
<% if issues.size == 0 %> |
|
32 |
<li><%=l(:label_change_log_no_issues)%></li> |
|
33 |
<% else %> |
|
34 |
<% issues.each do |issue| %> |
|
35 |
<li> |
|
36 |
<%= link_to_issue(issue) %>: <%=h issue.subject %> |
|
37 |
</li> |
|
38 |
<% end %> |
|
39 |
<% end %> |
|
40 |
</ul> |
|
29 | 41 |
<% end %> |
30 |
</div> |
|
42 |
</div> |
app/views/issues/show.rhtml (working copy) | ||
---|---|---|
27 | 27 |
<td><b><%=l(:field_done_ratio)%> :</b></td><td><%= @issue.done_ratio %> %</td> |
28 | 28 |
</tr> |
29 | 29 |
<tr> |
30 |
<td><b><%=l(:field_fixed_version)%> :</b></td><td><%= @issue.fixed_version ? @issue.fixed_version.name : "-" %></td>
|
|
30 |
<td><b><%=l(:field_fixed_version_plural)%> :</b></td><td><%= @issue.fixed_versions.map{|v| v.name}.join(", ") %></td>
|
|
31 | 31 |
<td><b><%=l(:label_spent_time)%> :</b></td> |
32 | 32 |
<td><%= @issue.spent_hours > 0 ? (link_to lwr(:label_f_hour, @issue.spent_hours), {:controller => 'timelog', :action => 'details', :issue_id => @issue}, :class => 'icon icon-time') : "-" %></td> |
33 | 33 |
</tr> |
app/views/issues/edit.rhtml (working copy) | ||
---|---|---|
25 | 25 |
<p><%= custom_field_tag_with_label @custom_value %></p> |
26 | 26 |
<% end %> |
27 | 27 | |
28 |
<p><%= f.select :fixed_version_id, (@project.versions.collect {|v| [v.name, v.id]}), { :include_blank => true } %></p> |
|
28 |
<p><%= render(:partial => 'fixed_versions') %></p> |
|
29 | ||
29 | 30 |
</div> |
30 | 31 |
<!--[eoform:issue]--> |
31 | 32 |
</div> |
... | ... | |
52 | 53 |
<%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %> |
53 | 54 |
<%= javascript_include_tag 'calendar/calendar-setup' %> |
54 | 55 |
<%= stylesheet_link_tag 'calendar' %> |
55 |
<% end %> |
|
56 |
<% end %> |
app/views/issues/_fixed_versions.rhtml (revision 0) | ||
---|---|---|
1 |
<% selected_fixed_versions = @issue.fixed_versions.collect { |v| v.id.to_i } %> |
|
2 |
<select name="issue[fixed_version_ids][]" multiple="multiple" size="5"> |
|
3 |
<optgroup label="<%=l(:field_fixed_versions_unreleased)%>"> |
|
4 |
<%= options_from_collection_for_select @unreleased_versions, "id", "name", selected_fixed_versions %> |
|
5 |
</optgroup> |
|
6 |
<optgroup label="<%=l(:field_fixed_versions_released)%>"> |
|
7 |
<%= options_from_collection_for_select @released_versions, "id", "name", selected_fixed_versions %> |
|
8 |
</optgroup> |
|
9 |
</select> |
app/views/issues/change_status.rhtml (working copy) | ||
---|---|---|
11 | 11 |
<p><label><%=l(:label_issue_status_new)%></label> <%= @new_status.name %></p> |
12 | 12 |
<p><%= f.select :assigned_to_id, (@issue.project.members.collect {|m| [m.name, m.user_id]}), :include_blank => true %></p> |
13 | 13 |
<p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p> |
14 |
<p><%= f.select :fixed_version_id, (@project.versions.collect {|v| [v.name, v.id]}), { :include_blank => true } %></p>
|
|
14 |
<p><%= render(:partial => 'fixed_versions') %></p>
|
|
15 | 15 | |
16 | 16 |
<p><label for="notes"><%= l(:field_notes) %></label> |
17 | 17 |
<%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %></p> |
lang/en.yml (working copy) | ||
---|---|---|
109 | 109 |
field_assigned_to: Assigned to |
110 | 110 |
field_priority: Priority |
111 | 111 |
field_fixed_version: Fixed version |
112 |
field_fixed_version_plural: Fixed versions |
|
113 |
field_fixed_versions_released: Released versions |
|
114 |
field_fixed_versions_unreleased: Unreleased versions |
|
112 | 115 |
field_user: User |
113 | 116 |
field_role: Role |
114 | 117 |
field_homepage: Homepage |
... | ... | |
258 | 261 |
label_news_latest: Latest news |
259 | 262 |
label_news_view_all: View all news |
260 | 263 |
label_change_log: Change log |
264 |
label_change_log_no_issues: No issues for this version |
|
261 | 265 |
label_settings: Settings |
262 | 266 |
label_overview: Overview |
263 | 267 |
label_version: Version |
db/migrate/042_issue_version_relationship.rb (revision 0) | ||
---|---|---|
1 |
class IssueVersionRelationship < ActiveRecord::Migration |
|
2 |
class Issue < ActiveRecord::Base |
|
3 |
belongs_to :old_fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id' |
|
4 |
has_and_belongs_to_many :versions |
|
5 |
end |
|
6 | ||
7 |
class Version < ActiveRecord::Base |
|
8 |
has_and_belongs_to_many :issues |
|
9 |
end |
|
10 | ||
11 |
def self.up |
|
12 |
create_table :issues_versions, :id => false do |t| |
|
13 |
t.column :issue_id, :integer |
|
14 |
t.column :version_id, :integer |
|
15 |
end |
|
16 | ||
17 |
Issue.find(:all).each do |issue| |
|
18 |
if issue.old_fixed_version |
|
19 |
issue.versions << issue.old_fixed_version |
|
20 |
end |
|
21 |
end |
|
22 | ||
23 |
remove_column :issues, :fixed_version_id |
|
24 |
end |
|
25 | ||
26 |
def self.down |
|
27 |
add_column :issues, :fixed_version_id, :integer |
|
28 | ||
29 |
Issue.find(:all).each do |issue| |
|
30 | ||
31 |
if issue.versions.size > 1 |
|
32 |
# We cannot rollback if any issues point to multiple versions |
|
33 |
remove_column :issues, :fixed_version_id |
|
34 |
raise IrreversibleMigration |
|
35 |
end |
|
36 | ||
37 |
issue.old_fixed_version = issue.versions[0] if issue.versions.size == 1 |
|
38 |
end |
|
39 | ||
40 |
drop_table :issues_versions |
|
41 |
end |
|
42 |
end |
public/stylesheets/application.css (working copy) | ||
---|---|---|
665 | 665 |
padding-left: 26px; |
666 | 666 |
vertical-align: bottom; |
667 | 667 |
} |
668 | ||
669 |
optgroup { |
|
670 |
background-color: #EEE; |
|
671 |
font-style: normal; |
|
672 |
} |
|
673 | ||
674 |
option { |
|
675 |
background-color: white; |
|
676 |
} |