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