1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 class VersionsController < ApplicationController
19 menu_item :roadmap
20 model_object Version
21 before_filter :find_model_object, :except => [:index, :new, :create, :close_completed]
22 before_filter :find_project_from_association, :except => [:index, :new, :create, :close_completed]
23 before_filter :find_project, :only => [:index, :new, :create, :close_completed]
24 before_filter :authorize
25
26 accept_api_auth :index, :show, :create, :update, :destroy
27
28 helper :custom_fields
29 helper :projects
30
31 def index
32 respond_to do |format|
33 format.html {
34 @trackers = @project.trackers.find(:all, :order => 'position')
35 retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
36 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
37 project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
38
39 @versions = @project.shared_versions || []
40 @versions += @project.rolled_up_versions.visible if @with_subprojects
41 @versions = @versions.uniq.sort
42 unless params[:completed]
43 @completed_versions = @versions.select {|version| version.closed? || version.completed? }
44 @versions -= @completed_versions
45 end
46
47 @issues_by_version = {}
48 if @selected_tracker_ids.any? && @versions.any?
49 issues = Issue.visible.all(
50 :include => [:project, :status, :tracker, :priority, :fixed_version],
51 :conditions => {:tracker_id => @selected_tracker_ids, :project_id => project_ids, :fixed_version_id => @versions.map(&:id)},
52 :order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id"
53 )
54 @issues_by_version = issues.group_by(&:fixed_version)
55 end
56 @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
57 }
58 format.api {
59 @versions = @project.shared_versions.all
60 }
61 end
62 end
63
64 def show
65 respond_to do |format|
66 format.html {
67 @issues = @version.fixed_issues.visible.find(:all,
68 :include => [:status, :tracker, :priority],
69 :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
70 }
71 format.api
72 end
73 end
74
75 def new
76 @version = @project.versions.build
77 @version.safe_attributes = params[:version]
78
79 respond_to do |format|
80 format.html
81 format.js do
82 render :update do |page|
83 page.replace_html 'ajax-modal', :partial => 'versions/new_modal'
84 page << "showModal('ajax-modal', '600px');"
85 page << "Form.Element.focus('version_name');"
86 end
87 end
88 end
89 end
90
91 def create
92 @version = @project.versions.build
93 if params[:version]
94 attributes = params[:version].dup
95 attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
96 @version.safe_attributes = attributes
97 end
98
99 if request.post?
100 if @version.save
101 respond_to do |format|
102 format.html do
103 flash[:notice] = l(:notice_successful_create)
104 redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
105 end
106 format.js do
107 render(:update) {|page|
108 page << 'hideModal();'
109 # IE doesn't support the replace_html rjs method for select box options
110 page.replace "issue_fixed_version_id",
111 content_tag('select', content_tag('option') + version_options_for_select(@project.shared_versions.open, @version), :id => 'issue_fixed_version_id', :name => 'issue[fixed_version_id]')
112 }
113 end
114 format.api do
115 render :action => 'show', :status => :created, :location => version_url(@version)
116 end
117 end
118 else
119 respond_to do |format|
120 format.html { render :action => 'new' }
121 format.js do
122 render :update do |page|
123 page.replace_html 'ajax-modal', :partial => 'versions/new_modal'
124 page << "Form.Element.focus('version_name');"
125 end
126 end
127 format.api { render_validation_errors(@version) }
128 end
129 end
130 end
131 end
132
133 def edit
134 end
135
136 def update
137 if request.put? && params[:version]
138 attributes = params[:version].dup
139 attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing'])
140 @version.safe_attributes = attributes
141 if @version.save
142 respond_to do |format|
143 format.html {
144 flash[:notice] = l(:notice_successful_update)
145 redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
146 }
147 format.api { head :ok }
148 end
149 else
150 respond_to do |format|
151 format.html { render :action => 'edit' }
152 format.api { render_validation_errors(@version) }
153 end
154 end
155 end
156 end
157
158 def close_completed
159 if request.put?
160 @project.close_completed_versions
161 end
162 redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
163 end
164
165 def destroy
166 if @version.fixed_issues.empty?
167 @version.destroy
168 respond_to do |format|
169 format.html { redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project }
170 format.api { head :ok }
171 end
172 else
173 respond_to do |format|
174 format.html {
175 flash[:error] = l(:notice_unable_delete_version)
176 redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
177 }
178 format.api { head :unprocessable_entity }
179 end
180 end
181 end
182
183 def status_by
184 respond_to do |format|
185 format.html { render :action => 'show' }
186 format.js { render(:update) {|page| page.replace_html 'status_by', render_issue_status_by(@version, params[:status_by])} }
187 end
188 end
189
190 private
191 def find_project
192 @project = Project.find(params[:project_id])
193 rescue ActiveRecord::RecordNotFound
194 render_404
195 end
196
197 def retrieve_selected_tracker_ids(selectable_trackers, default_trackers=nil)
198 if ids = params[:tracker_ids]
199 @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
200 else
201 @selected_tracker_ids = (default_trackers || selectable_trackers).collect {|t| t.id.to_s }
202 end
203 end
204
205 end