@@ -19,6 +19,7 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, :destroy] |
|
21 | before_filter :find_time_entry, :only => [:show, :edit, :update, :destroy] | |
|
22 | before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update] | |||
22 | before_filter :authorize, :except => [:index] |
|
23 | before_filter :authorize, :except => [:index] | |
23 | before_filter :find_optional_project, :only => [:index] |
|
24 | before_filter :find_optional_project, :only => [:index] | |
24 | accept_key_auth :index, :show, :create, :update, :destroy |
|
25 | accept_key_auth :index, :show, :create, :update, :destroy | |
@@ -160,6 +161,28 class TimelogController < ApplicationController | |||||
160 | end |
|
161 | end | |
161 | end |
|
162 | end | |
162 |
|
163 | |||
|
164 | def bulk_edit | |||
|
165 | @available_activities = TimeEntryActivity.shared.active | |||
|
166 | @custom_fields = TimeEntry.first.available_custom_fields | |||
|
167 | end | |||
|
168 | ||||
|
169 | def bulk_update | |||
|
170 | attributes = parse_params_for_bulk_time_entry_attributes(params) | |||
|
171 | ||||
|
172 | unsaved_time_entry_ids = [] | |||
|
173 | @time_entries.each do |time_entry| | |||
|
174 | time_entry.reload | |||
|
175 | time_entry.attributes = attributes | |||
|
176 | call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry }) | |||
|
177 | unless time_entry.save | |||
|
178 | # Keep unsaved time_entry ids to display them in flash error | |||
|
179 | unsaved_time_entry_ids << time_entry.id | |||
|
180 | end | |||
|
181 | end | |||
|
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 => @project}) | |||
|
184 | end | |||
|
185 | ||||
163 | 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 } | |
164 | def destroy |
|
187 | def destroy | |
165 | if @time_entry.destroy && @time_entry.destroyed? |
|
188 | if @time_entry.destroy && @time_entry.destroyed? | |
@@ -195,6 +218,26 private | |||||
195 | render_404 |
|
218 | render_404 | |
196 | end |
|
219 | end | |
197 |
|
220 | |||
|
221 | def find_time_entries | |||
|
222 | @time_entries = TimeEntry.find_all_by_id(params[:id] || params[:ids]) | |||
|
223 | raise ActiveRecord::RecordNotFound if @time_entries.empty? | |||
|
224 | @projects = @time_entries.collect(&:project).compact.uniq | |||
|
225 | @project = @projects.first if @projects.size == 1 | |||
|
226 | rescue ActiveRecord::RecordNotFound | |||
|
227 | render_404 | |||
|
228 | end | |||
|
229 | ||||
|
230 | def set_flash_from_bulk_time_entry_save(time_entries, unsaved_time_entry_ids) | |||
|
231 | if unsaved_time_entry_ids.empty? | |||
|
232 | flash[:notice] = l(:notice_successful_update) unless time_entries.empty? | |||
|
233 | else | |||
|
234 | flash[:error] = l(:notice_failed_to_save_time_entries, | |||
|
235 | :count => unsaved_time_entry_ids.size, | |||
|
236 | :total => time_entries.size, | |||
|
237 | :ids => '#' + unsaved_time_entry_ids.join(', #')) | |||
|
238 | end | |||
|
239 | end | |||
|
240 | ||||
198 | def find_project |
|
241 | def find_project | |
199 | if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present? |
|
242 | if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present? | |
200 | @issue = Issue.find(issue_id) |
|
243 | @issue = Issue.find(issue_id) | |
@@ -265,4 +308,10 private | |||||
265 | @to ||= (TimeEntry.latest_date_for_project(@project) || Date.today) |
|
308 | @to ||= (TimeEntry.latest_date_for_project(@project) || Date.today) | |
266 | end |
|
309 | end | |
267 |
|
310 | |||
|
311 | def parse_params_for_bulk_time_entry_attributes(params) | |||
|
312 | attributes = (params[:time_entry] || {}).reject {|k,v| v.blank?} | |||
|
313 | attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'} | |||
|
314 | attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values] | |||
|
315 | attributes | |||
|
316 | end | |||
268 | end |
|
317 | end |
@@ -431,6 +431,12 class Project < ActiveRecord::Base | |||||
431 | def all_issue_custom_fields |
|
431 | def all_issue_custom_fields | |
432 | @all_issue_custom_fields ||= (IssueCustomField.for_all + issue_custom_fields).uniq.sort |
|
432 | @all_issue_custom_fields ||= (IssueCustomField.for_all + issue_custom_fields).uniq.sort | |
433 | end |
|
433 | end | |
|
434 | ||||
|
435 | # Returns an array of all custom fields enabled for project time entries | |||
|
436 | # (explictly associated custom fields and custom fields enabled for all projects) | |||
|
437 | def all_time_entry_custom_fields | |||
|
438 | @all_time_entry_custom_fields ||= (TimeEntryCustomField.for_all + time_entry_custom_fields).uniq.sort | |||
|
439 | end | |||
434 |
|
440 | |||
435 | def project |
|
441 | def project | |
436 | self |
|
442 | self |
@@ -23,6 +23,13 ActionController::Routing::Routes.draw do |map| | |||||
23 | time_report.connect 'time_entries/report.:format' |
|
23 | time_report.connect 'time_entries/report.:format' | |
24 | end |
|
24 | end | |
25 |
|
25 | |||
|
26 | map.bulk_edit_time_entry 'time_entries/bulk_edit', | |||
|
27 | :controller => 'timelog', :action => 'bulk_edit', :conditions => { :method => :get } | |||
|
28 | map.bulk_update_time_entry 'time_entries/bulk_edit', | |||
|
29 | :controller => 'timelog', :action => 'bulk_update', :conditions => { :method => :post } | |||
|
30 | map.time_entries_context_menu '/time_entries/context_menu', | |||
|
31 | :controller => 'context_menus', :action => 'time_entries' | |||
|
32 | # TODO: wasteful since this is also nested under issues, projects, and projects/issues | |||
26 | map.resources :time_entries, :controller => 'timelog' |
|
33 | map.resources :time_entries, :controller => 'timelog' | |
27 |
|
34 | |||
28 | map.connect 'projects/:id/wiki', :controller => 'wikis', :action => 'edit', :conditions => {:method => :post} |
|
35 | map.connect 'projects/:id/wiki', :controller => 'wikis', :action => 'edit', :conditions => {:method => :post} |
@@ -86,10 +86,10 Redmine::AccessControl.map do |map| | |||||
86 | end |
|
86 | end | |
87 |
|
87 | |||
88 | map.project_module :time_tracking do |map| |
|
88 | map.project_module :time_tracking do |map| | |
89 | map.permission :log_time, {:timelog => [:new, :create, :edit, :update]}, :require => :loggedin |
|
89 | map.permission :log_time, {:timelog => [:new, :create, :edit, :update, :bulk_edit, :bulk_update]}, :require => :loggedin | |
90 | map.permission :view_time_entries, :timelog => [:index, :show], :time_entry_reports => [:report] |
|
90 | map.permission :view_time_entries, :timelog => [:index, :show], :time_entry_reports => [:report] | |
91 | map.permission :edit_time_entries, {:timelog => [:new, :create, :edit, :update, :destroy]}, :require => :member |
|
91 | map.permission :edit_time_entries, {:timelog => [:new, :create, :edit, :update, :destroy, :bulk_edit, :bulk_update]}, :require => :member | |
92 | map.permission :edit_own_time_entries, {:timelog => [:new, :create, :edit, :update, :destroy]}, :require => :loggedin |
|
92 | map.permission :edit_own_time_entries, {:timelog => [:new, :create, :edit, :update, :destroy,:bulk_edit, :bulk_update]}, :require => :loggedin | |
93 | map.permission :manage_project_activities, {:project_enumerations => [:update, :destroy]}, :require => :member |
|
93 | map.permission :manage_project_activities, {:project_enumerations => [:update, :destroy]}, :require => :member | |
94 | end |
|
94 | end | |
95 |
|
95 |
General Comments 0
You need to be logged in to leave comments.
Login now