##// END OF EJS Templates
Fixed: DoubleRenderError introduced by #7996....
Jean-Philippe Lang -
r5198:efccc61d79d3
parent child
Show More
@@ -1,321 +1,323
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2010 Jean-Philippe Lang
2 # Copyright (C) 2006-2010 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class TimelogController < ApplicationController
18 class TimelogController < ApplicationController
19 menu_item :issues
19 menu_item :issues
20 before_filter :find_project, :only => [:new, :create]
20 before_filter :find_project, :only => [:new, :create]
21 before_filter :find_time_entry, :only => [:show, :edit, :update]
21 before_filter :find_time_entry, :only => [:show, :edit, :update]
22 before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
22 before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
23 before_filter :authorize, :except => [:index]
23 before_filter :authorize, :except => [:index]
24 before_filter :find_optional_project, :only => [:index]
24 before_filter :find_optional_project, :only => [:index]
25 accept_key_auth :index, :show, :create, :update, :destroy
25 accept_key_auth :index, :show, :create, :update, :destroy
26
26
27 helper :sort
27 helper :sort
28 include SortHelper
28 include SortHelper
29 helper :issues
29 helper :issues
30 include TimelogHelper
30 include TimelogHelper
31 helper :custom_fields
31 helper :custom_fields
32 include CustomFieldsHelper
32 include CustomFieldsHelper
33
33
34 def index
34 def index
35 sort_init 'spent_on', 'desc'
35 sort_init 'spent_on', 'desc'
36 sort_update 'spent_on' => 'spent_on',
36 sort_update 'spent_on' => 'spent_on',
37 'user' => 'user_id',
37 'user' => 'user_id',
38 'activity' => 'activity_id',
38 'activity' => 'activity_id',
39 'project' => "#{Project.table_name}.name",
39 'project' => "#{Project.table_name}.name",
40 'issue' => 'issue_id',
40 'issue' => 'issue_id',
41 'hours' => 'hours'
41 'hours' => 'hours'
42
42
43 cond = ARCondition.new
43 cond = ARCondition.new
44 if @issue
44 if @issue
45 cond << "#{Issue.table_name}.root_id = #{@issue.root_id} AND #{Issue.table_name}.lft >= #{@issue.lft} AND #{Issue.table_name}.rgt <= #{@issue.rgt}"
45 cond << "#{Issue.table_name}.root_id = #{@issue.root_id} AND #{Issue.table_name}.lft >= #{@issue.lft} AND #{Issue.table_name}.rgt <= #{@issue.rgt}"
46 elsif @project
46 elsif @project
47 cond << @project.project_condition(Setting.display_subprojects_issues?)
47 cond << @project.project_condition(Setting.display_subprojects_issues?)
48 end
48 end
49
49
50 retrieve_date_range
50 retrieve_date_range
51 cond << ['spent_on BETWEEN ? AND ?', @from, @to]
51 cond << ['spent_on BETWEEN ? AND ?', @from, @to]
52
52
53 respond_to do |format|
53 respond_to do |format|
54 format.html {
54 format.html {
55 # Paginate results
55 # Paginate results
56 @entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions)
56 @entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions)
57 @entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
57 @entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
58 @entries = TimeEntry.visible.find(:all,
58 @entries = TimeEntry.visible.find(:all,
59 :include => [:project, :activity, :user, {:issue => :tracker}],
59 :include => [:project, :activity, :user, {:issue => :tracker}],
60 :conditions => cond.conditions,
60 :conditions => cond.conditions,
61 :order => sort_clause,
61 :order => sort_clause,
62 :limit => @entry_pages.items_per_page,
62 :limit => @entry_pages.items_per_page,
63 :offset => @entry_pages.current.offset)
63 :offset => @entry_pages.current.offset)
64 @total_hours = TimeEntry.visible.sum(:hours, :include => [:project, :issue], :conditions => cond.conditions).to_f
64 @total_hours = TimeEntry.visible.sum(:hours, :include => [:project, :issue], :conditions => cond.conditions).to_f
65
65
66 render :layout => !request.xhr?
66 render :layout => !request.xhr?
67 }
67 }
68 format.api {
68 format.api {
69 @entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions)
69 @entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions)
70 @entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
70 @entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
71 @entries = TimeEntry.visible.find(:all,
71 @entries = TimeEntry.visible.find(:all,
72 :include => [:project, :activity, :user, {:issue => :tracker}],
72 :include => [:project, :activity, :user, {:issue => :tracker}],
73 :conditions => cond.conditions,
73 :conditions => cond.conditions,
74 :order => sort_clause,
74 :order => sort_clause,
75 :limit => @entry_pages.items_per_page,
75 :limit => @entry_pages.items_per_page,
76 :offset => @entry_pages.current.offset)
76 :offset => @entry_pages.current.offset)
77 }
77 }
78 format.atom {
78 format.atom {
79 entries = TimeEntry.visible.find(:all,
79 entries = TimeEntry.visible.find(:all,
80 :include => [:project, :activity, :user, {:issue => :tracker}],
80 :include => [:project, :activity, :user, {:issue => :tracker}],
81 :conditions => cond.conditions,
81 :conditions => cond.conditions,
82 :order => "#{TimeEntry.table_name}.created_on DESC",
82 :order => "#{TimeEntry.table_name}.created_on DESC",
83 :limit => Setting.feeds_limit.to_i)
83 :limit => Setting.feeds_limit.to_i)
84 render_feed(entries, :title => l(:label_spent_time))
84 render_feed(entries, :title => l(:label_spent_time))
85 }
85 }
86 format.csv {
86 format.csv {
87 # Export all entries
87 # Export all entries
88 @entries = TimeEntry.visible.find(:all,
88 @entries = TimeEntry.visible.find(:all,
89 :include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
89 :include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
90 :conditions => cond.conditions,
90 :conditions => cond.conditions,
91 :order => sort_clause)
91 :order => sort_clause)
92 send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv')
92 send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv')
93 }
93 }
94 end
94 end
95 end
95 end
96
96
97 def show
97 def show
98 respond_to do |format|
98 respond_to do |format|
99 # TODO: Implement html response
99 # TODO: Implement html response
100 format.html { render :nothing => true, :status => 406 }
100 format.html { render :nothing => true, :status => 406 }
101 format.api
101 format.api
102 end
102 end
103 end
103 end
104
104
105 def new
105 def new
106 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
106 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
107 @time_entry.attributes = params[:time_entry]
107 @time_entry.attributes = params[:time_entry]
108
108
109 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
109 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
110 render :action => 'edit'
110 render :action => 'edit'
111 end
111 end
112
112
113 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
113 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
114 def create
114 def create
115 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
115 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
116 @time_entry.attributes = params[:time_entry]
116 @time_entry.attributes = params[:time_entry]
117
117
118 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
118 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
119
119
120 if @time_entry.save
120 if @time_entry.save
121 respond_to do |format|
121 respond_to do |format|
122 format.html {
122 format.html {
123 flash[:notice] = l(:notice_successful_update)
123 flash[:notice] = l(:notice_successful_update)
124 redirect_back_or_default :action => 'index', :project_id => @time_entry.project
124 redirect_back_or_default :action => 'index', :project_id => @time_entry.project
125 }
125 }
126 format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
126 format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
127 end
127 end
128 else
128 else
129 respond_to do |format|
129 respond_to do |format|
130 format.html { render :action => 'edit' }
130 format.html { render :action => 'edit' }
131 format.api { render_validation_errors(@time_entry) }
131 format.api { render_validation_errors(@time_entry) }
132 end
132 end
133 end
133 end
134 end
134 end
135
135
136 def edit
136 def edit
137 @time_entry.attributes = params[:time_entry]
137 @time_entry.attributes = params[:time_entry]
138
138
139 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
139 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
140 end
140 end
141
141
142 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
142 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
143 def update
143 def update
144 @time_entry.attributes = params[:time_entry]
144 @time_entry.attributes = params[:time_entry]
145
145
146 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
146 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
147
147
148 if @time_entry.save
148 if @time_entry.save
149 respond_to do |format|
149 respond_to do |format|
150 format.html {
150 format.html {
151 flash[:notice] = l(:notice_successful_update)
151 flash[:notice] = l(:notice_successful_update)
152 redirect_back_or_default :action => 'index', :project_id => @time_entry.project
152 redirect_back_or_default :action => 'index', :project_id => @time_entry.project
153 }
153 }
154 format.api { head :ok }
154 format.api { head :ok }
155 end
155 end
156 else
156 else
157 respond_to do |format|
157 respond_to do |format|
158 format.html { render :action => 'edit' }
158 format.html { render :action => 'edit' }
159 format.api { render_validation_errors(@time_entry) }
159 format.api { render_validation_errors(@time_entry) }
160 end
160 end
161 end
161 end
162 end
162 end
163
163
164 def bulk_edit
164 def bulk_edit
165 @available_activities = TimeEntryActivity.shared.active
165 @available_activities = TimeEntryActivity.shared.active
166 @custom_fields = TimeEntry.first.available_custom_fields
166 @custom_fields = TimeEntry.first.available_custom_fields
167 end
167 end
168
168
169 def bulk_update
169 def bulk_update
170 attributes = parse_params_for_bulk_time_entry_attributes(params)
170 attributes = parse_params_for_bulk_time_entry_attributes(params)
171
171
172 unsaved_time_entry_ids = []
172 unsaved_time_entry_ids = []
173 @time_entries.each do |time_entry|
173 @time_entries.each do |time_entry|
174 time_entry.reload
174 time_entry.reload
175 time_entry.attributes = attributes
175 time_entry.attributes = attributes
176 call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry })
176 call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry })
177 unless time_entry.save
177 unless time_entry.save
178 # Keep unsaved time_entry ids to display them in flash error
178 # Keep unsaved time_entry ids to display them in flash error
179 unsaved_time_entry_ids << time_entry.id
179 unsaved_time_entry_ids << time_entry.id
180 end
180 end
181 end
181 end
182 set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids)
182 set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids)
183 redirect_back_or_default({:controller => 'timelog', :action => 'index', :project_id => @projects.first})
183 redirect_back_or_default({:controller => 'timelog', :action => 'index', :project_id => @projects.first})
184 end
184 end
185
185
186 verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
186 verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
187 def destroy
187 def destroy
188 @time_entries.each do |t|
188 @time_entries.each do |t|
189 begin
189 begin
190 unless t.destroy && t.destroyed?
190 unless t.destroy && t.destroyed?
191 respond_to do |format|
191 respond_to do |format|
192 format.html {
192 format.html {
193 flash[:error] = l(:notice_unable_delete_time_entry)
193 flash[:error] = l(:notice_unable_delete_time_entry)
194 redirect_to :back
194 redirect_to :back
195 }
195 }
196 format.api { render_validation_errors(t) }
196 format.api { render_validation_errors(t) }
197 end
197 end
198 return
198 end
199 end
199 rescue ::ActionController::RedirectBackError
200 rescue ::ActionController::RedirectBackError
200 redirect_to :action => 'index', :project_id => @projects.first
201 redirect_to :action => 'index', :project_id => @projects.first
202 return
201 end
203 end
202 end
204 end
203
205
204 respond_to do |format|
206 respond_to do |format|
205 format.html {
207 format.html {
206 flash[:notice] = l(:notice_successful_delete)
208 flash[:notice] = l(:notice_successful_delete)
207 redirect_back_or_default(:action => 'index', :project_id => @projects.first)
209 redirect_back_or_default(:action => 'index', :project_id => @projects.first)
208 }
210 }
209 format.api { head :ok }
211 format.api { head :ok }
210 end
212 end
211 end
213 end
212
214
213 private
215 private
214 def find_time_entry
216 def find_time_entry
215 @time_entry = TimeEntry.find(params[:id])
217 @time_entry = TimeEntry.find(params[:id])
216 unless @time_entry.editable_by?(User.current)
218 unless @time_entry.editable_by?(User.current)
217 render_403
219 render_403
218 return false
220 return false
219 end
221 end
220 @project = @time_entry.project
222 @project = @time_entry.project
221 rescue ActiveRecord::RecordNotFound
223 rescue ActiveRecord::RecordNotFound
222 render_404
224 render_404
223 end
225 end
224
226
225 def find_time_entries
227 def find_time_entries
226 @time_entries = TimeEntry.find_all_by_id(params[:id] || params[:ids])
228 @time_entries = TimeEntry.find_all_by_id(params[:id] || params[:ids])
227 raise ActiveRecord::RecordNotFound if @time_entries.empty?
229 raise ActiveRecord::RecordNotFound if @time_entries.empty?
228 @projects = @time_entries.collect(&:project).compact.uniq
230 @projects = @time_entries.collect(&:project).compact.uniq
229 @project = @projects.first if @projects.size == 1
231 @project = @projects.first if @projects.size == 1
230 rescue ActiveRecord::RecordNotFound
232 rescue ActiveRecord::RecordNotFound
231 render_404
233 render_404
232 end
234 end
233
235
234 def set_flash_from_bulk_time_entry_save(time_entries, unsaved_time_entry_ids)
236 def set_flash_from_bulk_time_entry_save(time_entries, unsaved_time_entry_ids)
235 if unsaved_time_entry_ids.empty?
237 if unsaved_time_entry_ids.empty?
236 flash[:notice] = l(:notice_successful_update) unless time_entries.empty?
238 flash[:notice] = l(:notice_successful_update) unless time_entries.empty?
237 else
239 else
238 flash[:error] = l(:notice_failed_to_save_time_entries,
240 flash[:error] = l(:notice_failed_to_save_time_entries,
239 :count => unsaved_time_entry_ids.size,
241 :count => unsaved_time_entry_ids.size,
240 :total => time_entries.size,
242 :total => time_entries.size,
241 :ids => '#' + unsaved_time_entry_ids.join(', #'))
243 :ids => '#' + unsaved_time_entry_ids.join(', #'))
242 end
244 end
243 end
245 end
244
246
245 def find_project
247 def find_project
246 if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present?
248 if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present?
247 @issue = Issue.find(issue_id)
249 @issue = Issue.find(issue_id)
248 @project = @issue.project
250 @project = @issue.project
249 elsif (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
251 elsif (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
250 @project = Project.find(project_id)
252 @project = Project.find(project_id)
251 else
253 else
252 render_404
254 render_404
253 return false
255 return false
254 end
256 end
255 rescue ActiveRecord::RecordNotFound
257 rescue ActiveRecord::RecordNotFound
256 render_404
258 render_404
257 end
259 end
258
260
259 def find_optional_project
261 def find_optional_project
260 if !params[:issue_id].blank?
262 if !params[:issue_id].blank?
261 @issue = Issue.find(params[:issue_id])
263 @issue = Issue.find(params[:issue_id])
262 @project = @issue.project
264 @project = @issue.project
263 elsif !params[:project_id].blank?
265 elsif !params[:project_id].blank?
264 @project = Project.find(params[:project_id])
266 @project = Project.find(params[:project_id])
265 end
267 end
266 deny_access unless User.current.allowed_to?(:view_time_entries, @project, :global => true)
268 deny_access unless User.current.allowed_to?(:view_time_entries, @project, :global => true)
267 end
269 end
268
270
269 # Retrieves the date range based on predefined ranges or specific from/to param dates
271 # Retrieves the date range based on predefined ranges or specific from/to param dates
270 def retrieve_date_range
272 def retrieve_date_range
271 @free_period = false
273 @free_period = false
272 @from, @to = nil, nil
274 @from, @to = nil, nil
273
275
274 if params[:period_type] == '1' || (params[:period_type].nil? && !params[:period].nil?)
276 if params[:period_type] == '1' || (params[:period_type].nil? && !params[:period].nil?)
275 case params[:period].to_s
277 case params[:period].to_s
276 when 'today'
278 when 'today'
277 @from = @to = Date.today
279 @from = @to = Date.today
278 when 'yesterday'
280 when 'yesterday'
279 @from = @to = Date.today - 1
281 @from = @to = Date.today - 1
280 when 'current_week'
282 when 'current_week'
281 @from = Date.today - (Date.today.cwday - 1)%7
283 @from = Date.today - (Date.today.cwday - 1)%7
282 @to = @from + 6
284 @to = @from + 6
283 when 'last_week'
285 when 'last_week'
284 @from = Date.today - 7 - (Date.today.cwday - 1)%7
286 @from = Date.today - 7 - (Date.today.cwday - 1)%7
285 @to = @from + 6
287 @to = @from + 6
286 when '7_days'
288 when '7_days'
287 @from = Date.today - 7
289 @from = Date.today - 7
288 @to = Date.today
290 @to = Date.today
289 when 'current_month'
291 when 'current_month'
290 @from = Date.civil(Date.today.year, Date.today.month, 1)
292 @from = Date.civil(Date.today.year, Date.today.month, 1)
291 @to = (@from >> 1) - 1
293 @to = (@from >> 1) - 1
292 when 'last_month'
294 when 'last_month'
293 @from = Date.civil(Date.today.year, Date.today.month, 1) << 1
295 @from = Date.civil(Date.today.year, Date.today.month, 1) << 1
294 @to = (@from >> 1) - 1
296 @to = (@from >> 1) - 1
295 when '30_days'
297 when '30_days'
296 @from = Date.today - 30
298 @from = Date.today - 30
297 @to = Date.today
299 @to = Date.today
298 when 'current_year'
300 when 'current_year'
299 @from = Date.civil(Date.today.year, 1, 1)
301 @from = Date.civil(Date.today.year, 1, 1)
300 @to = Date.civil(Date.today.year, 12, 31)
302 @to = Date.civil(Date.today.year, 12, 31)
301 end
303 end
302 elsif params[:period_type] == '2' || (params[:period_type].nil? && (!params[:from].nil? || !params[:to].nil?))
304 elsif params[:period_type] == '2' || (params[:period_type].nil? && (!params[:from].nil? || !params[:to].nil?))
303 begin; @from = params[:from].to_s.to_date unless params[:from].blank?; rescue; end
305 begin; @from = params[:from].to_s.to_date unless params[:from].blank?; rescue; end
304 begin; @to = params[:to].to_s.to_date unless params[:to].blank?; rescue; end
306 begin; @to = params[:to].to_s.to_date unless params[:to].blank?; rescue; end
305 @free_period = true
307 @free_period = true
306 else
308 else
307 # default
309 # default
308 end
310 end
309
311
310 @from, @to = @to, @from if @from && @to && @from > @to
312 @from, @to = @to, @from if @from && @to && @from > @to
311 @from ||= (TimeEntry.earilest_date_for_project(@project) || Date.today)
313 @from ||= (TimeEntry.earilest_date_for_project(@project) || Date.today)
312 @to ||= (TimeEntry.latest_date_for_project(@project) || Date.today)
314 @to ||= (TimeEntry.latest_date_for_project(@project) || Date.today)
313 end
315 end
314
316
315 def parse_params_for_bulk_time_entry_attributes(params)
317 def parse_params_for_bulk_time_entry_attributes(params)
316 attributes = (params[:time_entry] || {}).reject {|k,v| v.blank?}
318 attributes = (params[:time_entry] || {}).reject {|k,v| v.blank?}
317 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
319 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
318 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
320 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
319 attributes
321 attributes
320 end
322 end
321 end
323 end
General Comments 0
You need to be logged in to leave comments. Login now