##// END OF EJS Templates
Let the new time_entry form be submitted without project (#17954)....
Jean-Philippe Lang -
r13058:15d5c331ebc1
parent child
Show More
@@ -18,14 +18,12
18 class TimelogController < ApplicationController
18 class TimelogController < ApplicationController
19 menu_item :issues
19 menu_item :issues
20
20
21 before_filter :find_project_for_new_time_entry, :only => [:create]
22 before_filter :find_time_entry, :only => [:show, :edit, :update]
21 before_filter :find_time_entry, :only => [:show, :edit, :update]
23 before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
22 before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
24 before_filter :authorize, :except => [:new, :index, :report]
23 before_filter :authorize, :only => [:show, :edit, :update, :bulk_edit, :bulk_update, :destroy]
25
24
26 before_filter :find_optional_project, :only => [:index, :report]
25 before_filter :find_optional_project, :only => [:new, :create, :index, :report]
27 before_filter :find_optional_project_for_new_time_entry, :only => [:new]
26 before_filter :authorize_global, :only => [:new, :create, :index, :report]
28 before_filter :authorize_global, :only => [:new, :index, :report]
29
27
30 accept_rss_auth :index
28 accept_rss_auth :index
31 accept_api_auth :index, :show, :create, :update, :destroy
29 accept_api_auth :index, :show, :create, :update, :destroy
@@ -104,6 +102,10 class TimelogController < ApplicationController
104 def create
102 def create
105 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
103 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
106 @time_entry.safe_attributes = params[:time_entry]
104 @time_entry.safe_attributes = params[:time_entry]
105 if @time_entry.project && !User.current.allowed_to?(:log_time, @time_entry.project)
106 render_403
107 return
108 end
107
109
108 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
110 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
109
111
@@ -112,21 +114,19 class TimelogController < ApplicationController
112 format.html {
114 format.html {
113 flash[:notice] = l(:notice_successful_create)
115 flash[:notice] = l(:notice_successful_create)
114 if params[:continue]
116 if params[:continue]
115 if params[:project_id]
117 options = {
116 options = {
118 :time_entry => {
117 :time_entry => {:issue_id => @time_entry.issue_id, :activity_id => @time_entry.activity_id},
119 :project_id => params[:time_entry][:project_id],
118 :back_url => params[:back_url]
120 :issue_id => @time_entry.issue_id,
119 }
121 :activity_id => @time_entry.activity_id
120 if @time_entry.issue
122 },
121 redirect_to new_project_issue_time_entry_path(@time_entry.project, @time_entry.issue, options)
123 :back_url => params[:back_url]
122 else
124 }
123 redirect_to new_project_time_entry_path(@time_entry.project, options)
125 if params[:project_id] && @time_entry.project
124 end
126 redirect_to new_project_time_entry_path(@time_entry.project, options)
127 elsif params[:issue_id] && @time_entry.issue
128 redirect_to new_issue_time_entry_path(@time_entry.issue, options)
125 else
129 else
126 options = {
127 :time_entry => {:project_id => @time_entry.project_id, :issue_id => @time_entry.issue_id, :activity_id => @time_entry.activity_id},
128 :back_url => params[:back_url]
129 }
130 redirect_to new_time_entry_path(options)
130 redirect_to new_time_entry_path(options)
131 end
131 end
132 else
132 else
@@ -251,32 +251,15 private
251 end
251 end
252 end
252 end
253
253
254 def find_optional_project_for_new_time_entry
255 if (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
256 @project = Project.find(project_id)
257 end
258 if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present?
259 @issue = Issue.find(issue_id)
260 @project ||= @issue.project
261 end
262 rescue ActiveRecord::RecordNotFound
263 render_404
264 end
265
266 def find_project_for_new_time_entry
267 find_optional_project_for_new_time_entry
268 if @project.nil?
269 render_404
270 end
271 end
272
273 def find_optional_project
254 def find_optional_project
274 if !params[:issue_id].blank?
255 if params[:issue_id].present?
275 @issue = Issue.find(params[:issue_id])
256 @issue = Issue.find(params[:issue_id])
276 @project = @issue.project
257 @project = @issue.project
277 elsif !params[:project_id].blank?
258 elsif params[:project_id].present?
278 @project = Project.find(params[:project_id])
259 @project = Project.find(params[:project_id])
279 end
260 end
261 rescue ActiveRecord::RecordNotFound
262 render_404
280 end
263 end
281
264
282 # Returns the TimeEntry scope for index and report actions
265 # Returns the TimeEntry scope for index and report actions
@@ -349,7 +349,10 module ApplicationHelper
349 end
349 end
350
350
351 def project_tree_options_for_select(projects, options = {})
351 def project_tree_options_for_select(projects, options = {})
352 s = ''
352 s = ''.html_safe
353 if options[:include_blank]
354 s << content_tag('option', '&nbsp;'.html_safe, :value => '')
355 end
353 project_tree(projects) do |project, level|
356 project_tree(projects) do |project, level|
354 name_prefix = (level > 0 ? '&nbsp;' * 2 * level + '&#187; ' : '').html_safe
357 name_prefix = (level > 0 ? '&nbsp;' * 2 * level + '&#187; ' : '').html_safe
355 tag_options = {:value => project.id}
358 tag_options = {:value => project.id}
@@ -24,7 +24,7 class TimeEntry < ActiveRecord::Base
24 belongs_to :user
24 belongs_to :user
25 belongs_to :activity, :class_name => 'TimeEntryActivity', :foreign_key => 'activity_id'
25 belongs_to :activity, :class_name => 'TimeEntryActivity', :foreign_key => 'activity_id'
26
26
27 attr_protected :project_id, :user_id, :tyear, :tmonth, :tweek
27 attr_protected :user_id, :tyear, :tmonth, :tweek
28
28
29 acts_as_customizable
29 acts_as_customizable
30 acts_as_event :title => Proc.new {|o| "#{l_hours(o.hours)} (#{(o.issue || o.project).event_title})"},
30 acts_as_event :title => Proc.new {|o| "#{l_hours(o.hours)} (#{(o.issue || o.project).event_title})"},
@@ -65,7 +65,7 class TimeEntry < ActiveRecord::Base
65 end
65 end
66 }
66 }
67
67
68 safe_attributes 'hours', 'comments', 'issue_id', 'activity_id', 'spent_on', 'custom_field_values', 'custom_fields'
68 safe_attributes 'hours', 'comments', 'project_id', 'issue_id', 'activity_id', 'spent_on', 'custom_field_values', 'custom_fields'
69
69
70 def initialize(attributes=nil, *args)
70 def initialize(attributes=nil, *args)
71 super
71 super
@@ -78,10 +78,12 class TimeEntry < ActiveRecord::Base
78 end
78 end
79
79
80 def safe_attributes=(attrs, user=User.current)
80 def safe_attributes=(attrs, user=User.current)
81 attrs = super
81 if attrs
82 if !new_record? && issue && issue.project_id != project_id
82 attrs = super(attrs)
83 if user.allowed_to?(:log_time, issue.project)
83 if issue_id_changed? && attrs[:project_id].blank? && issue && issue.project_id != project_id
84 self.project_id = issue.project_id
84 if user.allowed_to?(:log_time, issue.project)
85 self.project_id = issue.project_id
86 end
85 end
87 end
86 end
88 end
87 attrs
89 attrs
@@ -61,7 +61,7
61 end
61 end
62 end
62 end
63 if User.current.allowed_to?(:view_time_entries, @project)
63 if User.current.allowed_to?(:view_time_entries, @project)
64 rows.right l(:label_spent_time), (@issue.total_spent_hours > 0 ? link_to(l_hours(@issue.total_spent_hours), project_issue_time_entries_path(@project, @issue)) : "-"), :class => 'spent-time'
64 rows.right l(:label_spent_time), (@issue.total_spent_hours > 0 ? link_to(l_hours(@issue.total_spent_hours), issue_time_entries_path(@issue)) : "-"), :class => 'spent-time'
65 end
65 end
66 end %>
66 end %>
67 <%= render_custom_fields_rows(@issue) %>
67 <%= render_custom_fields_rows(@issue) %>
@@ -3,10 +3,12
3
3
4 <div class="box tabular">
4 <div class="box tabular">
5 <% if @time_entry.new_record? %>
5 <% if @time_entry.new_record? %>
6 <% if params[:project_id] || @time_entry.issue %>
6 <% if params[:project_id] %>
7 <%= f.hidden_field :project_id %>
7 <%= hidden_field_tag 'project_id', params[:project_id] %>
8 <% elsif params[:issue_id] %>
9 <%= hidden_field_tag 'issue_id', params[:issue_id] %>
8 <% else %>
10 <% else %>
9 <p><%= f.select :project_id, project_tree_options_for_select(Project.allowed_to(:log_time).all, :selected => @time_entry.project), :required => true %></p>
11 <p><%= f.select :project_id, project_tree_options_for_select(Project.allowed_to(:log_time).all, :selected => @time_entry.project, :include_blank => true) %></p>
10 <% end %>
12 <% end %>
11 <% end %>
13 <% end %>
12 <p>
14 <p>
@@ -1,7 +1,6
1 <h2><%= l(:label_spent_time) %></h2>
1 <h2><%= l(:label_spent_time) %></h2>
2
2
3 <%= labelled_form_for @time_entry, :url => time_entries_path do |f| %>
3 <%= labelled_form_for @time_entry, :url => time_entries_path do |f| %>
4 <%= hidden_field_tag 'project_id', params[:project_id] if params[:project_id] %>
5 <%= render :partial => 'form', :locals => {:f => f} %>
4 <%= render :partial => 'form', :locals => {:f => f} %>
6 <%= submit_tag l(:button_create) %>
5 <%= submit_tag l(:button_create) %>
7 <%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
6 <%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
@@ -28,31 +28,37 class TimelogControllerTest < ActionController::TestCase
28
28
29 include Redmine::I18n
29 include Redmine::I18n
30
30
31 def test_new_with_project_id
31 def test_new
32 @request.session[:user_id] = 3
32 @request.session[:user_id] = 3
33 get :new, :project_id => 1
33 get :new
34 assert_response :success
34 assert_response :success
35 assert_template 'new'
35 assert_template 'new'
36 assert_select 'select[name=?]', 'time_entry[project_id]', 0
36 assert_select 'input[name=?][type=hidden]', 'project_id', 0
37 assert_select 'input[name=?][value=1][type=hidden]', 'time_entry[project_id]'
37 assert_select 'input[name=?][type=hidden]', 'issue_id', 0
38 assert_select 'select[name=?]', 'time_entry[project_id]' do
39 # blank option for project
40 assert_select 'option[value=]'
41 end
38 end
42 end
39
43
40 def test_new_with_issue_id
44 def test_new_with_project_id
41 @request.session[:user_id] = 3
45 @request.session[:user_id] = 3
42 get :new, :issue_id => 2
46 get :new, :project_id => 1
43 assert_response :success
47 assert_response :success
44 assert_template 'new'
48 assert_template 'new'
49 assert_select 'input[name=?][type=hidden]', 'project_id'
50 assert_select 'input[name=?][type=hidden]', 'issue_id', 0
45 assert_select 'select[name=?]', 'time_entry[project_id]', 0
51 assert_select 'select[name=?]', 'time_entry[project_id]', 0
46 assert_select 'input[name=?][value=1][type=hidden]', 'time_entry[project_id]'
47 end
52 end
48
53
49 def test_new_without_project
54 def test_new_with_issue_id
50 @request.session[:user_id] = 3
55 @request.session[:user_id] = 3
51 get :new
56 get :new, :issue_id => 2
52 assert_response :success
57 assert_response :success
53 assert_template 'new'
58 assert_template 'new'
54 assert_select 'select[name=?]', 'time_entry[project_id]'
59 assert_select 'input[name=?][type=hidden]', 'project_id', 0
55 assert_select 'input[name=?]', 'time_entry[project_id]', 0
60 assert_select 'input[name=?][type=hidden]', 'issue_id'
61 assert_select 'select[name=?]', 'time_entry[project_id]', 0
56 end
62 end
57
63
58 def test_new_without_project_should_prefill_the_form
64 def test_new_without_project_should_prefill_the_form
@@ -63,7 +69,6 class TimelogControllerTest < ActionController::TestCase
63 assert_select 'select[name=?]', 'time_entry[project_id]' do
69 assert_select 'select[name=?]', 'time_entry[project_id]' do
64 assert_select 'option[value=1][selected=selected]'
70 assert_select 'option[value=1][selected=selected]'
65 end
71 end
66 assert_select 'input[name=?]', 'time_entry[project_id]', 0
67 end
72 end
68
73
69 def test_new_without_project_should_deny_without_permission
74 def test_new_without_project_should_deny_without_permission
@@ -113,80 +118,101 class TimelogControllerTest < ActionController::TestCase
113 end
118 end
114
119
115 def test_post_create
120 def test_post_create
116 # TODO: should POST to issues’ time log instead of project. change form
117 # and routing
118 @request.session[:user_id] = 3
121 @request.session[:user_id] = 3
119 post :create, :project_id => 1,
122 assert_difference 'TimeEntry.count' do
123 post :create, :project_id => 1,
120 :time_entry => {:comments => 'Some work on TimelogControllerTest',
124 :time_entry => {:comments => 'Some work on TimelogControllerTest',
121 # Not the default activity
125 # Not the default activity
122 :activity_id => '11',
126 :activity_id => '11',
123 :spent_on => '2008-03-14',
127 :spent_on => '2008-03-14',
124 :issue_id => '1',
128 :issue_id => '1',
125 :hours => '7.3'}
129 :hours => '7.3'}
126 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
130 assert_redirected_to '/projects/ecookbook/time_entries'
131 end
127
132
128 i = Issue.find(1)
133 t = TimeEntry.order('id DESC').first
129 t = TimeEntry.find_by_comments('Some work on TimelogControllerTest')
130 assert_not_nil t
134 assert_not_nil t
135 assert_equal 'Some work on TimelogControllerTest', t.comments
136 assert_equal 1, t.project_id
137 assert_equal 1, t.issue_id
131 assert_equal 11, t.activity_id
138 assert_equal 11, t.activity_id
132 assert_equal 7.3, t.hours
139 assert_equal 7.3, t.hours
133 assert_equal 3, t.user_id
140 assert_equal 3, t.user_id
134 assert_equal i, t.issue
135 assert_equal i.project, t.project
136 end
141 end
137
142
138 def test_post_create_with_blank_issue
143 def test_post_create_with_blank_issue
139 # TODO: should POST to issues’ time log instead of project. change form
140 # and routing
141 @request.session[:user_id] = 3
144 @request.session[:user_id] = 3
142 post :create, :project_id => 1,
145 assert_difference 'TimeEntry.count' do
146 post :create, :project_id => 1,
143 :time_entry => {:comments => 'Some work on TimelogControllerTest',
147 :time_entry => {:comments => 'Some work on TimelogControllerTest',
144 # Not the default activity
148 # Not the default activity
145 :activity_id => '11',
149 :activity_id => '11',
146 :issue_id => '',
150 :issue_id => '',
147 :spent_on => '2008-03-14',
151 :spent_on => '2008-03-14',
148 :hours => '7.3'}
152 :hours => '7.3'}
149 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
153 assert_redirected_to '/projects/ecookbook/time_entries'
154 end
150
155
151 t = TimeEntry.find_by_comments('Some work on TimelogControllerTest')
156 t = TimeEntry.order('id DESC').first
152 assert_not_nil t
157 assert_not_nil t
158 assert_equal 'Some work on TimelogControllerTest', t.comments
159 assert_equal 1, t.project_id
160 assert_nil t.issue_id
153 assert_equal 11, t.activity_id
161 assert_equal 11, t.activity_id
154 assert_equal 7.3, t.hours
162 assert_equal 7.3, t.hours
155 assert_equal 3, t.user_id
163 assert_equal 3, t.user_id
156 end
164 end
157
165
158 def test_create_and_continue
166 def test_create_and_continue_at_project_level
159 @request.session[:user_id] = 2
167 @request.session[:user_id] = 2
160 post :create, :project_id => 1,
168 assert_difference 'TimeEntry.count' do
161 :time_entry => {:activity_id => '11',
169 post :create, :time_entry => {:project_id => '1',
162 :issue_id => '',
170 :activity_id => '11',
163 :spent_on => '2008-03-14',
171 :issue_id => '',
164 :hours => '7.3'},
172 :spent_on => '2008-03-14',
165 :continue => '1'
173 :hours => '7.3'},
166 assert_redirected_to '/projects/ecookbook/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D='
174 :continue => '1'
175 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D=1'
176 end
167 end
177 end
168
178
169 def test_create_and_continue_with_issue_id
179 def test_create_and_continue_at_issue_level
170 @request.session[:user_id] = 2
180 @request.session[:user_id] = 2
171 post :create, :project_id => 1,
181 assert_difference 'TimeEntry.count' do
172 :time_entry => {:activity_id => '11',
182 post :create, :time_entry => {:project_id => '',
173 :issue_id => '1',
183 :activity_id => '11',
174 :spent_on => '2008-03-14',
184 :issue_id => '1',
175 :hours => '7.3'},
185 :spent_on => '2008-03-14',
176 :continue => '1'
186 :hours => '7.3'},
177 assert_redirected_to '/projects/ecookbook/issues/1/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=1'
187 :continue => '1'
188 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=1&time_entry%5Bproject_id%5D='
189 end
178 end
190 end
179
191
180 def test_create_and_continue_without_project
192 def test_create_and_continue_with_project_id
181 @request.session[:user_id] = 2
193 @request.session[:user_id] = 2
182 post :create, :time_entry => {:project_id => '1',
194 assert_difference 'TimeEntry.count' do
183 :activity_id => '11',
195 post :create, :project_id => 1,
184 :issue_id => '',
196 :time_entry => {:activity_id => '11',
185 :spent_on => '2008-03-14',
197 :issue_id => '',
186 :hours => '7.3'},
198 :spent_on => '2008-03-14',
187 :continue => '1'
199 :hours => '7.3'},
200 :continue => '1'
201 assert_redirected_to '/projects/ecookbook/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D='
202 end
203 end
188
204
189 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D=1'
205 def test_create_and_continue_with_issue_id
206 @request.session[:user_id] = 2
207 assert_difference 'TimeEntry.count' do
208 post :create, :issue_id => 1,
209 :time_entry => {:activity_id => '11',
210 :issue_id => '1',
211 :spent_on => '2008-03-14',
212 :hours => '7.3'},
213 :continue => '1'
214 assert_redirected_to '/issues/1/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=1&time_entry%5Bproject_id%5D='
215 end
190 end
216 end
191
217
192 def test_create_without_log_time_permission_should_be_denied
218 def test_create_without_log_time_permission_should_be_denied
@@ -201,6 +227,14 class TimelogControllerTest < ActionController::TestCase
201 assert_response 403
227 assert_response 403
202 end
228 end
203
229
230 def test_create_without_project_and_issue_should_fail
231 @request.session[:user_id] = 2
232 post :create, :time_entry => {:issue_id => ''}
233
234 assert_response :success
235 assert_template 'new'
236 end
237
204 def test_create_with_failure
238 def test_create_with_failure
205 @request.session[:user_id] = 2
239 @request.session[:user_id] = 2
206 post :create, :project_id => 1,
240 post :create, :project_id => 1,
@@ -546,8 +580,6 class TimelogControllerTest < ActionController::TestCase
546 # display all time
580 # display all time
547 assert_nil assigns(:from)
581 assert_nil assigns(:from)
548 assert_nil assigns(:to)
582 assert_nil assigns(:to)
549 # TODO: remove /projects/:project_id/issues/:issue_id/time_entries routes
550 # to use /issues/:issue_id/time_entries
551 assert_tag :form,
583 assert_tag :form,
552 :attributes => {:action => "/projects/ecookbook/issues/1/time_entries", :id => 'query_form'}
584 :attributes => {:action => "/projects/ecookbook/issues/1/time_entries", :id => 'query_form'}
553 end
585 end
General Comments 0
You need to be logged in to leave comments. Login now