##// END OF EJS Templates
Preload some associations....
Jean-Philippe Lang -
r11814:ef8dd0f64b70
parent child
Show More
@@ -1,311 +1,308
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2013 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class TimelogController < ApplicationController
19 19 menu_item :issues
20 20
21 21 before_filter :find_project_for_new_time_entry, :only => [:create]
22 22 before_filter :find_time_entry, :only => [:show, :edit, :update]
23 23 before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
24 24 before_filter :authorize, :except => [:new, :index, :report]
25 25
26 26 before_filter :find_optional_project, :only => [:index, :report]
27 27 before_filter :find_optional_project_for_new_time_entry, :only => [:new]
28 28 before_filter :authorize_global, :only => [:new, :index, :report]
29 29
30 30 accept_rss_auth :index
31 31 accept_api_auth :index, :show, :create, :update, :destroy
32 32
33 33 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
34 34
35 35 helper :sort
36 36 include SortHelper
37 37 helper :issues
38 38 include TimelogHelper
39 39 helper :custom_fields
40 40 include CustomFieldsHelper
41 41 helper :queries
42 42 include QueriesHelper
43 43
44 44 def index
45 45 @query = TimeEntryQuery.build_from_params(params, :project => @project, :name => '_')
46 46
47 47 sort_init(@query.sort_criteria.empty? ? [['spent_on', 'desc']] : @query.sort_criteria)
48 48 sort_update(@query.sortable_columns)
49 scope = time_entry_scope(:order => sort_clause)
49 scope = time_entry_scope(:order => sort_clause).
50 includes(:project, :activity, :user, :issue).
51 preload(:issue => [:project, :tracker, :status, :assigned_to, :priority])
50 52
51 53 respond_to do |format|
52 54 format.html {
53 55 # Paginate results
54 56 @entry_count = scope.count
55 57 @entry_pages = Paginator.new @entry_count, per_page_option, params['page']
56 58 @entries = scope.all(
57 :include => [:project, :activity, :user, {:issue => :tracker}],
58 59 :limit => @entry_pages.per_page,
59 60 :offset => @entry_pages.offset
60 61 )
61 62 @total_hours = scope.sum(:hours).to_f
62 63
63 64 render :layout => !request.xhr?
64 65 }
65 66 format.api {
66 67 @entry_count = scope.count
67 68 @offset, @limit = api_offset_and_limit
68 @entries = scope.all(
69 :include => [:project, :activity, :user, {:issue => :tracker}],
69 @entries = scope.preload(:custom_values => :custom_field).all(
70 70 :limit => @limit,
71 71 :offset => @offset
72 72 )
73 73 }
74 74 format.atom {
75 75 entries = scope.reorder("#{TimeEntry.table_name}.created_on DESC").all(
76 :include => [:project, :activity, :user, {:issue => :tracker}],
77 76 :limit => Setting.feeds_limit.to_i
78 77 )
79 78 render_feed(entries, :title => l(:label_spent_time))
80 79 }
81 80 format.csv {
82 81 # Export all entries
83 @entries = scope.all(
84 :include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}]
85 )
82 @entries = scope.all
86 83 send_data(query_to_csv(@entries, @query, params), :type => 'text/csv; header=present', :filename => 'timelog.csv')
87 84 }
88 85 end
89 86 end
90 87
91 88 def report
92 89 @query = TimeEntryQuery.build_from_params(params, :project => @project, :name => '_')
93 90 scope = time_entry_scope
94 91
95 92 @report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], scope)
96 93
97 94 respond_to do |format|
98 95 format.html { render :layout => !request.xhr? }
99 96 format.csv { send_data(report_to_csv(@report), :type => 'text/csv; header=present', :filename => 'timelog.csv') }
100 97 end
101 98 end
102 99
103 100 def show
104 101 respond_to do |format|
105 102 # TODO: Implement html response
106 103 format.html { render :nothing => true, :status => 406 }
107 104 format.api
108 105 end
109 106 end
110 107
111 108 def new
112 109 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
113 110 @time_entry.safe_attributes = params[:time_entry]
114 111 end
115 112
116 113 def create
117 114 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
118 115 @time_entry.safe_attributes = params[:time_entry]
119 116
120 117 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
121 118
122 119 if @time_entry.save
123 120 respond_to do |format|
124 121 format.html {
125 122 flash[:notice] = l(:notice_successful_create)
126 123 if params[:continue]
127 124 if params[:project_id]
128 125 options = {
129 126 :time_entry => {:issue_id => @time_entry.issue_id, :activity_id => @time_entry.activity_id},
130 127 :back_url => params[:back_url]
131 128 }
132 129 if @time_entry.issue
133 130 redirect_to new_project_issue_time_entry_path(@time_entry.project, @time_entry.issue, options)
134 131 else
135 132 redirect_to new_project_time_entry_path(@time_entry.project, options)
136 133 end
137 134 else
138 135 options = {
139 136 :time_entry => {:project_id => @time_entry.project_id, :issue_id => @time_entry.issue_id, :activity_id => @time_entry.activity_id},
140 137 :back_url => params[:back_url]
141 138 }
142 139 redirect_to new_time_entry_path(options)
143 140 end
144 141 else
145 142 redirect_back_or_default project_time_entries_path(@time_entry.project)
146 143 end
147 144 }
148 145 format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
149 146 end
150 147 else
151 148 respond_to do |format|
152 149 format.html { render :action => 'new' }
153 150 format.api { render_validation_errors(@time_entry) }
154 151 end
155 152 end
156 153 end
157 154
158 155 def edit
159 156 @time_entry.safe_attributes = params[:time_entry]
160 157 end
161 158
162 159 def update
163 160 @time_entry.safe_attributes = params[:time_entry]
164 161
165 162 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
166 163
167 164 if @time_entry.save
168 165 respond_to do |format|
169 166 format.html {
170 167 flash[:notice] = l(:notice_successful_update)
171 168 redirect_back_or_default project_time_entries_path(@time_entry.project)
172 169 }
173 170 format.api { render_api_ok }
174 171 end
175 172 else
176 173 respond_to do |format|
177 174 format.html { render :action => 'edit' }
178 175 format.api { render_validation_errors(@time_entry) }
179 176 end
180 177 end
181 178 end
182 179
183 180 def bulk_edit
184 181 @available_activities = TimeEntryActivity.shared.active
185 182 @custom_fields = TimeEntry.first.available_custom_fields
186 183 end
187 184
188 185 def bulk_update
189 186 attributes = parse_params_for_bulk_time_entry_attributes(params)
190 187
191 188 unsaved_time_entry_ids = []
192 189 @time_entries.each do |time_entry|
193 190 time_entry.reload
194 191 time_entry.safe_attributes = attributes
195 192 call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry })
196 193 unless time_entry.save
197 194 logger.info "time entry could not be updated: #{time_entry.errors.full_messages}" if logger && logger.info
198 195 # Keep unsaved time_entry ids to display them in flash error
199 196 unsaved_time_entry_ids << time_entry.id
200 197 end
201 198 end
202 199 set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids)
203 200 redirect_back_or_default project_time_entries_path(@projects.first)
204 201 end
205 202
206 203 def destroy
207 204 destroyed = TimeEntry.transaction do
208 205 @time_entries.each do |t|
209 206 unless t.destroy && t.destroyed?
210 207 raise ActiveRecord::Rollback
211 208 end
212 209 end
213 210 end
214 211
215 212 respond_to do |format|
216 213 format.html {
217 214 if destroyed
218 215 flash[:notice] = l(:notice_successful_delete)
219 216 else
220 217 flash[:error] = l(:notice_unable_delete_time_entry)
221 218 end
222 219 redirect_back_or_default project_time_entries_path(@projects.first)
223 220 }
224 221 format.api {
225 222 if destroyed
226 223 render_api_ok
227 224 else
228 225 render_validation_errors(@time_entries)
229 226 end
230 227 }
231 228 end
232 229 end
233 230
234 231 private
235 232 def find_time_entry
236 233 @time_entry = TimeEntry.find(params[:id])
237 234 unless @time_entry.editable_by?(User.current)
238 235 render_403
239 236 return false
240 237 end
241 238 @project = @time_entry.project
242 239 rescue ActiveRecord::RecordNotFound
243 240 render_404
244 241 end
245 242
246 243 def find_time_entries
247 244 @time_entries = TimeEntry.find_all_by_id(params[:id] || params[:ids])
248 245 raise ActiveRecord::RecordNotFound if @time_entries.empty?
249 246 @projects = @time_entries.collect(&:project).compact.uniq
250 247 @project = @projects.first if @projects.size == 1
251 248 rescue ActiveRecord::RecordNotFound
252 249 render_404
253 250 end
254 251
255 252 def set_flash_from_bulk_time_entry_save(time_entries, unsaved_time_entry_ids)
256 253 if unsaved_time_entry_ids.empty?
257 254 flash[:notice] = l(:notice_successful_update) unless time_entries.empty?
258 255 else
259 256 flash[:error] = l(:notice_failed_to_save_time_entries,
260 257 :count => unsaved_time_entry_ids.size,
261 258 :total => time_entries.size,
262 259 :ids => '#' + unsaved_time_entry_ids.join(', #'))
263 260 end
264 261 end
265 262
266 263 def find_optional_project_for_new_time_entry
267 264 if (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
268 265 @project = Project.find(project_id)
269 266 end
270 267 if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present?
271 268 @issue = Issue.find(issue_id)
272 269 @project ||= @issue.project
273 270 end
274 271 rescue ActiveRecord::RecordNotFound
275 272 render_404
276 273 end
277 274
278 275 def find_project_for_new_time_entry
279 276 find_optional_project_for_new_time_entry
280 277 if @project.nil?
281 278 render_404
282 279 end
283 280 end
284 281
285 282 def find_optional_project
286 283 if !params[:issue_id].blank?
287 284 @issue = Issue.find(params[:issue_id])
288 285 @project = @issue.project
289 286 elsif !params[:project_id].blank?
290 287 @project = Project.find(params[:project_id])
291 288 end
292 289 end
293 290
294 291 # Returns the TimeEntry scope for index and report actions
295 292 def time_entry_scope(options={})
296 293 scope = @query.results_scope(options)
297 294 if @issue
298 295 scope = scope.on_issue(@issue)
299 296 elsif @project
300 297 scope = scope.on_project(@project, Setting.display_subprojects_issues?)
301 298 end
302 299 scope
303 300 end
304 301
305 302 def parse_params_for_bulk_time_entry_attributes(params)
306 303 attributes = (params[:time_entry] || {}).reject {|k,v| v.blank?}
307 304 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
308 305 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
309 306 attributes
310 307 end
311 308 end
General Comments 0
You need to be logged in to leave comments. Login now