Mercurial > hg > soundsoftware-site
comparison app/controllers/timelog_controller.rb @ 1115:433d4f72a19b redmine-2.2
Update to Redmine SVN revision 11137 on 2.2-stable branch
author | Chris Cannam |
---|---|
date | Mon, 07 Jan 2013 12:01:42 +0000 |
parents | 5f33065ddc4b |
children | 622f24f53b42 261b3d9a4903 |
comparison
equal
deleted
inserted
replaced
929:5f33065ddc4b | 1115:433d4f72a19b |
---|---|
1 # Redmine - project management software | 1 # Redmine - project management software |
2 # Copyright (C) 2006-2011 Jean-Philippe Lang | 2 # Copyright (C) 2006-2012 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. |
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 |
21 before_filter :find_project_for_new_time_entry, :only => [:create] | |
21 before_filter :find_time_entry, :only => [:show, :edit, :update] | 22 before_filter :find_time_entry, :only => [:show, :edit, :update] |
22 before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy] | 23 before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy] |
23 before_filter :authorize, :except => [:index] | 24 before_filter :authorize, :except => [:new, :index, :report] |
24 before_filter :find_optional_project, :only => [:index] | 25 |
26 before_filter :find_optional_project, :only => [:index, :report] | |
27 before_filter :find_optional_project_for_new_time_entry, :only => [:new] | |
28 before_filter :authorize_global, :only => [:new, :index, :report] | |
29 | |
25 accept_rss_auth :index | 30 accept_rss_auth :index |
26 accept_api_auth :index, :show, :create, :update, :destroy | 31 accept_api_auth :index, :show, :create, :update, :destroy |
27 | 32 |
28 helper :sort | 33 helper :sort |
29 include SortHelper | 34 include SortHelper |
32 helper :custom_fields | 37 helper :custom_fields |
33 include CustomFieldsHelper | 38 include CustomFieldsHelper |
34 | 39 |
35 def index | 40 def index |
36 sort_init 'spent_on', 'desc' | 41 sort_init 'spent_on', 'desc' |
37 sort_update 'spent_on' => 'spent_on', | 42 sort_update 'spent_on' => ['spent_on', "#{TimeEntry.table_name}.created_on"], |
38 'user' => 'user_id', | 43 'user' => 'user_id', |
39 'activity' => 'activity_id', | 44 'activity' => 'activity_id', |
40 'project' => "#{Project.table_name}.name", | 45 'project' => "#{Project.table_name}.name", |
41 'issue' => 'issue_id', | 46 'issue' => 'issue_id', |
42 'hours' => 'hours' | 47 'hours' => 'hours' |
43 | 48 |
44 cond = ARCondition.new | 49 retrieve_date_range |
50 | |
51 scope = TimeEntry.visible.spent_between(@from, @to) | |
45 if @issue | 52 if @issue |
46 cond << "#{Issue.table_name}.root_id = #{@issue.root_id} AND #{Issue.table_name}.lft >= #{@issue.lft} AND #{Issue.table_name}.rgt <= #{@issue.rgt}" | 53 scope = scope.on_issue(@issue) |
47 elsif @project | 54 elsif @project |
48 cond << @project.project_condition(Setting.display_subprojects_issues?) | 55 scope = scope.on_project(@project, Setting.display_subprojects_issues?) |
49 end | 56 end |
50 | |
51 retrieve_date_range | |
52 cond << ['spent_on BETWEEN ? AND ?', @from, @to] | |
53 | 57 |
54 respond_to do |format| | 58 respond_to do |format| |
55 format.html { | 59 format.html { |
56 # Paginate results | 60 # Paginate results |
57 @entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions) | 61 @entry_count = scope.count |
58 @entry_pages = Paginator.new self, @entry_count, per_page_option, params['page'] | 62 @entry_pages = Paginator.new self, @entry_count, per_page_option, params['page'] |
59 @entries = TimeEntry.visible.find(:all, | 63 @entries = scope.all( |
60 :include => [:project, :activity, :user, {:issue => :tracker}], | 64 :include => [:project, :activity, :user, {:issue => :tracker}], |
61 :conditions => cond.conditions, | 65 :order => sort_clause, |
62 :order => sort_clause, | 66 :limit => @entry_pages.items_per_page, |
63 :limit => @entry_pages.items_per_page, | 67 :offset => @entry_pages.current.offset |
64 :offset => @entry_pages.current.offset) | 68 ) |
65 @total_hours = TimeEntry.visible.sum(:hours, :include => [:project, :issue], :conditions => cond.conditions).to_f | 69 @total_hours = scope.sum(:hours).to_f |
66 | 70 |
67 render :layout => !request.xhr? | 71 render :layout => !request.xhr? |
68 } | 72 } |
69 format.api { | 73 format.api { |
70 @entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions) | 74 @entry_count = scope.count |
71 @offset, @limit = api_offset_and_limit | 75 @offset, @limit = api_offset_and_limit |
72 @entries = TimeEntry.visible.find(:all, | 76 @entries = scope.all( |
73 :include => [:project, :activity, :user, {:issue => :tracker}], | 77 :include => [:project, :activity, :user, {:issue => :tracker}], |
74 :conditions => cond.conditions, | 78 :order => sort_clause, |
75 :order => sort_clause, | 79 :limit => @limit, |
76 :limit => @limit, | 80 :offset => @offset |
77 :offset => @offset) | 81 ) |
78 } | 82 } |
79 format.atom { | 83 format.atom { |
80 entries = TimeEntry.visible.find(:all, | 84 entries = scope.all( |
81 :include => [:project, :activity, :user, {:issue => :tracker}], | 85 :include => [:project, :activity, :user, {:issue => :tracker}], |
82 :conditions => cond.conditions, | 86 :order => "#{TimeEntry.table_name}.created_on DESC", |
83 :order => "#{TimeEntry.table_name}.created_on DESC", | 87 :limit => Setting.feeds_limit.to_i |
84 :limit => Setting.feeds_limit.to_i) | 88 ) |
85 render_feed(entries, :title => l(:label_spent_time)) | 89 render_feed(entries, :title => l(:label_spent_time)) |
86 } | 90 } |
87 format.csv { | 91 format.csv { |
88 # Export all entries | 92 # Export all entries |
89 @entries = TimeEntry.visible.find(:all, | 93 @entries = scope.all( |
90 :include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}], | 94 :include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}], |
91 :conditions => cond.conditions, | 95 :order => sort_clause |
92 :order => sort_clause) | 96 ) |
93 send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv') | 97 send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv') |
94 } | 98 } |
99 end | |
100 end | |
101 | |
102 def report | |
103 retrieve_date_range | |
104 @report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], @from, @to) | |
105 | |
106 respond_to do |format| | |
107 format.html { render :layout => !request.xhr? } | |
108 format.csv { send_data(report_to_csv(@report), :type => 'text/csv; header=present', :filename => 'timelog.csv') } | |
95 end | 109 end |
96 end | 110 end |
97 | 111 |
98 def show | 112 def show |
99 respond_to do |format| | 113 respond_to do |format| |
104 end | 118 end |
105 | 119 |
106 def new | 120 def new |
107 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today) | 121 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today) |
108 @time_entry.safe_attributes = params[:time_entry] | 122 @time_entry.safe_attributes = params[:time_entry] |
109 | 123 end |
110 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry }) | 124 |
111 render :action => 'edit' | |
112 end | |
113 | |
114 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed } | |
115 def create | 125 def create |
116 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today) | 126 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today) |
127 @time_entry.safe_attributes = params[:time_entry] | |
128 | |
129 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry }) | |
130 | |
131 if @time_entry.save | |
132 respond_to do |format| | |
133 format.html { | |
134 flash[:notice] = l(:notice_successful_create) | |
135 if params[:continue] | |
136 if params[:project_id] | |
137 redirect_to :action => 'new', :project_id => @time_entry.project, :issue_id => @time_entry.issue, | |
138 :time_entry => {:issue_id => @time_entry.issue_id, :activity_id => @time_entry.activity_id}, | |
139 :back_url => params[:back_url] | |
140 else | |
141 redirect_to :action => 'new', | |
142 :time_entry => {:project_id => @time_entry.project_id, :issue_id => @time_entry.issue_id, :activity_id => @time_entry.activity_id}, | |
143 :back_url => params[:back_url] | |
144 end | |
145 else | |
146 redirect_back_or_default :action => 'index', :project_id => @time_entry.project | |
147 end | |
148 } | |
149 format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) } | |
150 end | |
151 else | |
152 respond_to do |format| | |
153 format.html { render :action => 'new' } | |
154 format.api { render_validation_errors(@time_entry) } | |
155 end | |
156 end | |
157 end | |
158 | |
159 def edit | |
160 @time_entry.safe_attributes = params[:time_entry] | |
161 end | |
162 | |
163 def update | |
117 @time_entry.safe_attributes = params[:time_entry] | 164 @time_entry.safe_attributes = params[:time_entry] |
118 | 165 |
119 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry }) | 166 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry }) |
120 | 167 |
121 if @time_entry.save | 168 if @time_entry.save |
122 respond_to do |format| | 169 respond_to do |format| |
123 format.html { | 170 format.html { |
124 flash[:notice] = l(:notice_successful_update) | 171 flash[:notice] = l(:notice_successful_update) |
125 redirect_back_or_default :action => 'index', :project_id => @time_entry.project | 172 redirect_back_or_default :action => 'index', :project_id => @time_entry.project |
126 } | 173 } |
127 format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) } | 174 format.api { render_api_ok } |
128 end | |
129 else | |
130 respond_to do |format| | |
131 format.html { render :action => 'edit' } | |
132 format.api { render_validation_errors(@time_entry) } | |
133 end | |
134 end | |
135 end | |
136 | |
137 def edit | |
138 @time_entry.safe_attributes = params[:time_entry] | |
139 | |
140 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry }) | |
141 end | |
142 | |
143 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed } | |
144 def update | |
145 @time_entry.safe_attributes = params[:time_entry] | |
146 | |
147 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry }) | |
148 | |
149 if @time_entry.save | |
150 respond_to do |format| | |
151 format.html { | |
152 flash[:notice] = l(:notice_successful_update) | |
153 redirect_back_or_default :action => 'index', :project_id => @time_entry.project | |
154 } | |
155 format.api { head :ok } | |
156 end | 175 end |
157 else | 176 else |
158 respond_to do |format| | 177 respond_to do |format| |
159 format.html { render :action => 'edit' } | 178 format.html { render :action => 'edit' } |
160 format.api { render_validation_errors(@time_entry) } | 179 format.api { render_validation_errors(@time_entry) } |
182 end | 201 end |
183 set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids) | 202 set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids) |
184 redirect_back_or_default({:controller => 'timelog', :action => 'index', :project_id => @projects.first}) | 203 redirect_back_or_default({:controller => 'timelog', :action => 'index', :project_id => @projects.first}) |
185 end | 204 end |
186 | 205 |
187 verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed } | |
188 def destroy | 206 def destroy |
189 @time_entries.each do |t| | 207 destroyed = TimeEntry.transaction do |
190 begin | 208 @time_entries.each do |t| |
191 unless t.destroy && t.destroyed? | 209 unless t.destroy && t.destroyed? |
192 respond_to do |format| | 210 raise ActiveRecord::Rollback |
193 format.html { | |
194 flash[:error] = l(:notice_unable_delete_time_entry) | |
195 redirect_to :back | |
196 } | |
197 format.api { render_validation_errors(t) } | |
198 end | |
199 return | |
200 end | 211 end |
201 rescue ::ActionController::RedirectBackError | |
202 redirect_to :action => 'index', :project_id => @projects.first | |
203 return | |
204 end | 212 end |
205 end | 213 end |
206 | 214 |
207 respond_to do |format| | 215 respond_to do |format| |
208 format.html { | 216 format.html { |
209 flash[:notice] = l(:notice_successful_delete) | 217 if destroyed |
218 flash[:notice] = l(:notice_successful_delete) | |
219 else | |
220 flash[:error] = l(:notice_unable_delete_time_entry) | |
221 end | |
210 redirect_back_or_default(:action => 'index', :project_id => @projects.first) | 222 redirect_back_or_default(:action => 'index', :project_id => @projects.first) |
211 } | 223 } |
212 format.api { head :ok } | 224 format.api { |
225 if destroyed | |
226 render_api_ok | |
227 else | |
228 render_validation_errors(@time_entries) | |
229 end | |
230 } | |
213 end | 231 end |
214 end | 232 end |
215 | 233 |
216 private | 234 private |
217 def find_time_entry | 235 def find_time_entry |
243 :total => time_entries.size, | 261 :total => time_entries.size, |
244 :ids => '#' + unsaved_time_entry_ids.join(', #')) | 262 :ids => '#' + unsaved_time_entry_ids.join(', #')) |
245 end | 263 end |
246 end | 264 end |
247 | 265 |
248 def find_project | 266 def find_optional_project_for_new_time_entry |
267 if (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present? | |
268 @project = Project.find(project_id) | |
269 end | |
249 if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present? | 270 if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present? |
250 @issue = Issue.find(issue_id) | 271 @issue = Issue.find(issue_id) |
251 @project = @issue.project | 272 @project ||= @issue.project |
252 elsif (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present? | |
253 @project = Project.find(project_id) | |
254 else | |
255 render_404 | |
256 return false | |
257 end | 273 end |
258 rescue ActiveRecord::RecordNotFound | 274 rescue ActiveRecord::RecordNotFound |
259 render_404 | 275 render_404 |
276 end | |
277 | |
278 def find_project_for_new_time_entry | |
279 find_optional_project_for_new_time_entry | |
280 if @project.nil? | |
281 render_404 | |
282 end | |
260 end | 283 end |
261 | 284 |
262 def find_optional_project | 285 def find_optional_project |
263 if !params[:issue_id].blank? | 286 if !params[:issue_id].blank? |
264 @issue = Issue.find(params[:issue_id]) | 287 @issue = Issue.find(params[:issue_id]) |
265 @project = @issue.project | 288 @project = @issue.project |
266 elsif !params[:project_id].blank? | 289 elsif !params[:project_id].blank? |
267 @project = Project.find(params[:project_id]) | 290 @project = Project.find(params[:project_id]) |
268 end | 291 end |
269 deny_access unless User.current.allowed_to?(:view_time_entries, @project, :global => true) | |
270 end | 292 end |
271 | 293 |
272 # Retrieves the date range based on predefined ranges or specific from/to param dates | 294 # Retrieves the date range based on predefined ranges or specific from/to param dates |
273 def retrieve_date_range | 295 def retrieve_date_range |
274 @free_period = false | 296 @free_period = false |
284 @from = Date.today - (Date.today.cwday - 1)%7 | 306 @from = Date.today - (Date.today.cwday - 1)%7 |
285 @to = @from + 6 | 307 @to = @from + 6 |
286 when 'last_week' | 308 when 'last_week' |
287 @from = Date.today - 7 - (Date.today.cwday - 1)%7 | 309 @from = Date.today - 7 - (Date.today.cwday - 1)%7 |
288 @to = @from + 6 | 310 @to = @from + 6 |
311 when 'last_2_weeks' | |
312 @from = Date.today - 14 - (Date.today.cwday - 1)%7 | |
313 @to = @from + 13 | |
289 when '7_days' | 314 when '7_days' |
290 @from = Date.today - 7 | 315 @from = Date.today - 7 |
291 @to = Date.today | 316 @to = Date.today |
292 when 'current_month' | 317 when 'current_month' |
293 @from = Date.civil(Date.today.year, Date.today.month, 1) | 318 @from = Date.civil(Date.today.year, Date.today.month, 1) |
309 else | 334 else |
310 # default | 335 # default |
311 end | 336 end |
312 | 337 |
313 @from, @to = @to, @from if @from && @to && @from > @to | 338 @from, @to = @to, @from if @from && @to && @from > @to |
314 @from ||= (TimeEntry.earilest_date_for_project(@project) || Date.today) | |
315 @to ||= (TimeEntry.latest_date_for_project(@project) || Date.today) | |
316 end | 339 end |
317 | 340 |
318 def parse_params_for_bulk_time_entry_attributes(params) | 341 def parse_params_for_bulk_time_entry_attributes(params) |
319 attributes = (params[:time_entry] || {}).reject {|k,v| v.blank?} | 342 attributes = (params[:time_entry] || {}).reject {|k,v| v.blank?} |
320 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'} | 343 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'} |