##// END OF EJS Templates
Removed deep nested time entries routes....
Jean-Philippe Lang -
r13227:043c2c92da7d
parent child
Show More
@@ -1,6 +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 => project_time_entry_path(@time_entry.project, @time_entry) do |f| %>
3 <%= labelled_form_for @time_entry, :url => time_entry_path(@time_entry) do |f| %>
4 <%= render :partial => 'form', :locals => {:f => f} %>
4 <%= render :partial => 'form', :locals => {:f => f} %>
5 <%= submit_tag l(:button_save) %>
5 <%= submit_tag l(:button_save) %>
6 <% end %>
6 <% end %>
@@ -1,348 +1,348
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 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.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
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 Rails.application.routes.draw do
18 Rails.application.routes.draw do
19 root :to => 'welcome#index', :as => 'home'
19 root :to => 'welcome#index', :as => 'home'
20
20
21 match 'login', :to => 'account#login', :as => 'signin', :via => [:get, :post]
21 match 'login', :to => 'account#login', :as => 'signin', :via => [:get, :post]
22 match 'logout', :to => 'account#logout', :as => 'signout', :via => [:get, :post]
22 match 'logout', :to => 'account#logout', :as => 'signout', :via => [:get, :post]
23 match 'account/register', :to => 'account#register', :via => [:get, :post], :as => 'register'
23 match 'account/register', :to => 'account#register', :via => [:get, :post], :as => 'register'
24 match 'account/lost_password', :to => 'account#lost_password', :via => [:get, :post], :as => 'lost_password'
24 match 'account/lost_password', :to => 'account#lost_password', :via => [:get, :post], :as => 'lost_password'
25 match 'account/activate', :to => 'account#activate', :via => :get
25 match 'account/activate', :to => 'account#activate', :via => :get
26 get 'account/activation_email', :to => 'account#activation_email', :as => 'activation_email'
26 get 'account/activation_email', :to => 'account#activation_email', :as => 'activation_email'
27
27
28 match '/news/preview', :controller => 'previews', :action => 'news', :as => 'preview_news', :via => [:get, :post, :put, :patch]
28 match '/news/preview', :controller => 'previews', :action => 'news', :as => 'preview_news', :via => [:get, :post, :put, :patch]
29 match '/issues/preview/new/:project_id', :to => 'previews#issue', :as => 'preview_new_issue', :via => [:get, :post, :put, :patch]
29 match '/issues/preview/new/:project_id', :to => 'previews#issue', :as => 'preview_new_issue', :via => [:get, :post, :put, :patch]
30 match '/issues/preview/edit/:id', :to => 'previews#issue', :as => 'preview_edit_issue', :via => [:get, :post, :put, :patch]
30 match '/issues/preview/edit/:id', :to => 'previews#issue', :as => 'preview_edit_issue', :via => [:get, :post, :put, :patch]
31 match '/issues/preview', :to => 'previews#issue', :as => 'preview_issue', :via => [:get, :post, :put, :patch]
31 match '/issues/preview', :to => 'previews#issue', :as => 'preview_issue', :via => [:get, :post, :put, :patch]
32
32
33 match 'projects/:id/wiki', :to => 'wikis#edit', :via => :post
33 match 'projects/:id/wiki', :to => 'wikis#edit', :via => :post
34 match 'projects/:id/wiki/destroy', :to => 'wikis#destroy', :via => [:get, :post]
34 match 'projects/:id/wiki/destroy', :to => 'wikis#destroy', :via => [:get, :post]
35
35
36 match 'boards/:board_id/topics/new', :to => 'messages#new', :via => [:get, :post], :as => 'new_board_message'
36 match 'boards/:board_id/topics/new', :to => 'messages#new', :via => [:get, :post], :as => 'new_board_message'
37 get 'boards/:board_id/topics/:id', :to => 'messages#show', :as => 'board_message'
37 get 'boards/:board_id/topics/:id', :to => 'messages#show', :as => 'board_message'
38 match 'boards/:board_id/topics/quote/:id', :to => 'messages#quote', :via => [:get, :post]
38 match 'boards/:board_id/topics/quote/:id', :to => 'messages#quote', :via => [:get, :post]
39 get 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
39 get 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
40
40
41 post 'boards/:board_id/topics/preview', :to => 'messages#preview', :as => 'preview_board_message'
41 post 'boards/:board_id/topics/preview', :to => 'messages#preview', :as => 'preview_board_message'
42 post 'boards/:board_id/topics/:id/replies', :to => 'messages#reply'
42 post 'boards/:board_id/topics/:id/replies', :to => 'messages#reply'
43 post 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
43 post 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
44 post 'boards/:board_id/topics/:id/destroy', :to => 'messages#destroy'
44 post 'boards/:board_id/topics/:id/destroy', :to => 'messages#destroy'
45
45
46 # Misc issue routes. TODO: move into resources
46 # Misc issue routes. TODO: move into resources
47 match '/issues/auto_complete', :to => 'auto_completes#issues', :via => :get, :as => 'auto_complete_issues'
47 match '/issues/auto_complete', :to => 'auto_completes#issues', :via => :get, :as => 'auto_complete_issues'
48 match '/issues/context_menu', :to => 'context_menus#issues', :as => 'issues_context_menu', :via => [:get, :post]
48 match '/issues/context_menu', :to => 'context_menus#issues', :as => 'issues_context_menu', :via => [:get, :post]
49 match '/issues/changes', :to => 'journals#index', :as => 'issue_changes', :via => :get
49 match '/issues/changes', :to => 'journals#index', :as => 'issue_changes', :via => :get
50 match '/issues/:id/quoted', :to => 'journals#new', :id => /\d+/, :via => :post, :as => 'quoted_issue'
50 match '/issues/:id/quoted', :to => 'journals#new', :id => /\d+/, :via => :post, :as => 'quoted_issue'
51
51
52 match '/journals/diff/:id', :to => 'journals#diff', :id => /\d+/, :via => :get
52 match '/journals/diff/:id', :to => 'journals#diff', :id => /\d+/, :via => :get
53 match '/journals/edit/:id', :to => 'journals#edit', :id => /\d+/, :via => [:get, :post]
53 match '/journals/edit/:id', :to => 'journals#edit', :id => /\d+/, :via => [:get, :post]
54
54
55 get '/projects/:project_id/issues/gantt', :to => 'gantts#show', :as => 'project_gantt'
55 get '/projects/:project_id/issues/gantt', :to => 'gantts#show', :as => 'project_gantt'
56 get '/issues/gantt', :to => 'gantts#show'
56 get '/issues/gantt', :to => 'gantts#show'
57
57
58 get '/projects/:project_id/issues/calendar', :to => 'calendars#show', :as => 'project_calendar'
58 get '/projects/:project_id/issues/calendar', :to => 'calendars#show', :as => 'project_calendar'
59 get '/issues/calendar', :to => 'calendars#show'
59 get '/issues/calendar', :to => 'calendars#show'
60
60
61 get 'projects/:id/issues/report', :to => 'reports#issue_report', :as => 'project_issues_report'
61 get 'projects/:id/issues/report', :to => 'reports#issue_report', :as => 'project_issues_report'
62 get 'projects/:id/issues/report/:detail', :to => 'reports#issue_report_details', :as => 'project_issues_report_details'
62 get 'projects/:id/issues/report/:detail', :to => 'reports#issue_report_details', :as => 'project_issues_report_details'
63
63
64 match 'my/account', :controller => 'my', :action => 'account', :via => [:get, :post]
64 match 'my/account', :controller => 'my', :action => 'account', :via => [:get, :post]
65 match 'my/account/destroy', :controller => 'my', :action => 'destroy', :via => [:get, :post]
65 match 'my/account/destroy', :controller => 'my', :action => 'destroy', :via => [:get, :post]
66 match 'my/page', :controller => 'my', :action => 'page', :via => :get
66 match 'my/page', :controller => 'my', :action => 'page', :via => :get
67 match 'my', :controller => 'my', :action => 'index', :via => :get # Redirects to my/page
67 match 'my', :controller => 'my', :action => 'index', :via => :get # Redirects to my/page
68 match 'my/reset_rss_key', :controller => 'my', :action => 'reset_rss_key', :via => :post
68 match 'my/reset_rss_key', :controller => 'my', :action => 'reset_rss_key', :via => :post
69 match 'my/reset_api_key', :controller => 'my', :action => 'reset_api_key', :via => :post
69 match 'my/reset_api_key', :controller => 'my', :action => 'reset_api_key', :via => :post
70 match 'my/password', :controller => 'my', :action => 'password', :via => [:get, :post]
70 match 'my/password', :controller => 'my', :action => 'password', :via => [:get, :post]
71 match 'my/page_layout', :controller => 'my', :action => 'page_layout', :via => :get
71 match 'my/page_layout', :controller => 'my', :action => 'page_layout', :via => :get
72 match 'my/add_block', :controller => 'my', :action => 'add_block', :via => :post
72 match 'my/add_block', :controller => 'my', :action => 'add_block', :via => :post
73 match 'my/remove_block', :controller => 'my', :action => 'remove_block', :via => :post
73 match 'my/remove_block', :controller => 'my', :action => 'remove_block', :via => :post
74 match 'my/order_blocks', :controller => 'my', :action => 'order_blocks', :via => :post
74 match 'my/order_blocks', :controller => 'my', :action => 'order_blocks', :via => :post
75
75
76 resources :users do
76 resources :users do
77 resources :memberships, :controller => 'principal_memberships'
77 resources :memberships, :controller => 'principal_memberships'
78 end
78 end
79
79
80 post 'watchers/watch', :to => 'watchers#watch', :as => 'watch'
80 post 'watchers/watch', :to => 'watchers#watch', :as => 'watch'
81 delete 'watchers/watch', :to => 'watchers#unwatch'
81 delete 'watchers/watch', :to => 'watchers#unwatch'
82 get 'watchers/new', :to => 'watchers#new'
82 get 'watchers/new', :to => 'watchers#new'
83 post 'watchers', :to => 'watchers#create'
83 post 'watchers', :to => 'watchers#create'
84 post 'watchers/append', :to => 'watchers#append'
84 post 'watchers/append', :to => 'watchers#append'
85 delete 'watchers', :to => 'watchers#destroy'
85 delete 'watchers', :to => 'watchers#destroy'
86 get 'watchers/autocomplete_for_user', :to => 'watchers#autocomplete_for_user'
86 get 'watchers/autocomplete_for_user', :to => 'watchers#autocomplete_for_user'
87 # Specific routes for issue watchers API
87 # Specific routes for issue watchers API
88 post 'issues/:object_id/watchers', :to => 'watchers#create', :object_type => 'issue'
88 post 'issues/:object_id/watchers', :to => 'watchers#create', :object_type => 'issue'
89 delete 'issues/:object_id/watchers/:user_id' => 'watchers#destroy', :object_type => 'issue'
89 delete 'issues/:object_id/watchers/:user_id' => 'watchers#destroy', :object_type => 'issue'
90
90
91 resources :projects do
91 resources :projects do
92 member do
92 member do
93 get 'settings(/:tab)', :action => 'settings', :as => 'settings'
93 get 'settings(/:tab)', :action => 'settings', :as => 'settings'
94 post 'modules'
94 post 'modules'
95 post 'archive'
95 post 'archive'
96 post 'unarchive'
96 post 'unarchive'
97 post 'close'
97 post 'close'
98 post 'reopen'
98 post 'reopen'
99 match 'copy', :via => [:get, :post]
99 match 'copy', :via => [:get, :post]
100 end
100 end
101
101
102 shallow do
102 shallow do
103 resources :memberships, :controller => 'members', :only => [:index, :show, :new, :create, :update, :destroy] do
103 resources :memberships, :controller => 'members', :only => [:index, :show, :new, :create, :update, :destroy] do
104 collection do
104 collection do
105 get 'autocomplete'
105 get 'autocomplete'
106 end
106 end
107 end
107 end
108 end
108 end
109
109
110 resource :enumerations, :controller => 'project_enumerations', :only => [:update, :destroy]
110 resource :enumerations, :controller => 'project_enumerations', :only => [:update, :destroy]
111
111
112 get 'issues/:copy_from/copy', :to => 'issues#new', :as => 'copy_issue'
112 get 'issues/:copy_from/copy', :to => 'issues#new', :as => 'copy_issue'
113 resources :issues, :only => [:index, :new, :create]
113 resources :issues, :only => [:index, :new, :create]
114 # issue form update
114 # issue form update
115 match 'issues/update_form', :controller => 'issues', :action => 'update_form', :via => [:put, :patch, :post], :as => 'issue_form'
115 match 'issues/update_form', :controller => 'issues', :action => 'update_form', :via => [:put, :patch, :post], :as => 'issue_form'
116
116
117 resources :files, :only => [:index, :new, :create]
117 resources :files, :only => [:index, :new, :create]
118
118
119 resources :versions, :except => [:index, :show, :edit, :update, :destroy] do
119 resources :versions, :except => [:index, :show, :edit, :update, :destroy] do
120 collection do
120 collection do
121 put 'close_completed'
121 put 'close_completed'
122 end
122 end
123 end
123 end
124 get 'versions.:format', :to => 'versions#index'
124 get 'versions.:format', :to => 'versions#index'
125 get 'roadmap', :to => 'versions#index', :format => false
125 get 'roadmap', :to => 'versions#index', :format => false
126 get 'versions', :to => 'versions#index'
126 get 'versions', :to => 'versions#index'
127
127
128 resources :news, :except => [:show, :edit, :update, :destroy]
128 resources :news, :except => [:show, :edit, :update, :destroy]
129 resources :time_entries, :controller => 'timelog' do
129 resources :time_entries, :controller => 'timelog', :except => [:show, :edit, :update, :destroy] do
130 get 'report', :on => :collection
130 get 'report', :on => :collection
131 end
131 end
132 resources :queries, :only => [:new, :create]
132 resources :queries, :only => [:new, :create]
133 shallow do
133 shallow do
134 resources :issue_categories
134 resources :issue_categories
135 end
135 end
136 resources :documents, :except => [:show, :edit, :update, :destroy]
136 resources :documents, :except => [:show, :edit, :update, :destroy]
137 resources :boards
137 resources :boards
138 shallow do
138 shallow do
139 resources :repositories, :except => [:index, :show] do
139 resources :repositories, :except => [:index, :show] do
140 member do
140 member do
141 match 'committers', :via => [:get, :post]
141 match 'committers', :via => [:get, :post]
142 end
142 end
143 end
143 end
144 end
144 end
145
145
146 match 'wiki/index', :controller => 'wiki', :action => 'index', :via => :get
146 match 'wiki/index', :controller => 'wiki', :action => 'index', :via => :get
147 resources :wiki, :except => [:index, :new, :create], :as => 'wiki_page' do
147 resources :wiki, :except => [:index, :new, :create], :as => 'wiki_page' do
148 member do
148 member do
149 get 'rename'
149 get 'rename'
150 post 'rename'
150 post 'rename'
151 get 'history'
151 get 'history'
152 get 'diff'
152 get 'diff'
153 match 'preview', :via => [:post, :put, :patch]
153 match 'preview', :via => [:post, :put, :patch]
154 post 'protect'
154 post 'protect'
155 post 'add_attachment'
155 post 'add_attachment'
156 end
156 end
157 collection do
157 collection do
158 get 'export'
158 get 'export'
159 get 'date_index'
159 get 'date_index'
160 end
160 end
161 end
161 end
162 match 'wiki', :controller => 'wiki', :action => 'show', :via => :get
162 match 'wiki', :controller => 'wiki', :action => 'show', :via => :get
163 get 'wiki/:id/:version', :to => 'wiki#show', :constraints => {:version => /\d+/}
163 get 'wiki/:id/:version', :to => 'wiki#show', :constraints => {:version => /\d+/}
164 delete 'wiki/:id/:version', :to => 'wiki#destroy_version'
164 delete 'wiki/:id/:version', :to => 'wiki#destroy_version'
165 get 'wiki/:id/:version/annotate', :to => 'wiki#annotate'
165 get 'wiki/:id/:version/annotate', :to => 'wiki#annotate'
166 get 'wiki/:id/:version/diff', :to => 'wiki#diff'
166 get 'wiki/:id/:version/diff', :to => 'wiki#diff'
167 end
167 end
168
168
169 resources :issues do
169 resources :issues do
170 collection do
170 collection do
171 match 'bulk_edit', :via => [:get, :post]
171 match 'bulk_edit', :via => [:get, :post]
172 post 'bulk_update'
172 post 'bulk_update'
173 end
173 end
174 resources :time_entries, :controller => 'timelog' do
174 resources :time_entries, :controller => 'timelog', :except => [:show, :edit, :update, :destroy] do
175 collection do
175 collection do
176 get 'report'
176 get 'report'
177 end
177 end
178 end
178 end
179 shallow do
179 shallow do
180 resources :relations, :controller => 'issue_relations', :only => [:index, :show, :create, :destroy]
180 resources :relations, :controller => 'issue_relations', :only => [:index, :show, :create, :destroy]
181 end
181 end
182 end
182 end
183 match '/issues', :controller => 'issues', :action => 'destroy', :via => :delete
183 match '/issues', :controller => 'issues', :action => 'destroy', :via => :delete
184
184
185 resources :queries, :except => [:show]
185 resources :queries, :except => [:show]
186
186
187 resources :news, :only => [:index, :show, :edit, :update, :destroy]
187 resources :news, :only => [:index, :show, :edit, :update, :destroy]
188 match '/news/:id/comments', :to => 'comments#create', :via => :post
188 match '/news/:id/comments', :to => 'comments#create', :via => :post
189 match '/news/:id/comments/:comment_id', :to => 'comments#destroy', :via => :delete
189 match '/news/:id/comments/:comment_id', :to => 'comments#destroy', :via => :delete
190
190
191 resources :versions, :only => [:show, :edit, :update, :destroy] do
191 resources :versions, :only => [:show, :edit, :update, :destroy] do
192 post 'status_by', :on => :member
192 post 'status_by', :on => :member
193 end
193 end
194
194
195 resources :documents, :only => [:show, :edit, :update, :destroy] do
195 resources :documents, :only => [:show, :edit, :update, :destroy] do
196 post 'add_attachment', :on => :member
196 post 'add_attachment', :on => :member
197 end
197 end
198
198
199 match '/time_entries/context_menu', :to => 'context_menus#time_entries', :as => :time_entries_context_menu, :via => [:get, :post]
199 match '/time_entries/context_menu', :to => 'context_menus#time_entries', :as => :time_entries_context_menu, :via => [:get, :post]
200
200
201 resources :time_entries, :controller => 'timelog', :except => :destroy do
201 resources :time_entries, :controller => 'timelog', :except => :destroy do
202 collection do
202 collection do
203 get 'report'
203 get 'report'
204 get 'bulk_edit'
204 get 'bulk_edit'
205 post 'bulk_update'
205 post 'bulk_update'
206 end
206 end
207 end
207 end
208 match '/time_entries/:id', :to => 'timelog#destroy', :via => :delete, :id => /\d+/
208 match '/time_entries/:id', :to => 'timelog#destroy', :via => :delete, :id => /\d+/
209 # TODO: delete /time_entries for bulk deletion
209 # TODO: delete /time_entries for bulk deletion
210 match '/time_entries/destroy', :to => 'timelog#destroy', :via => :delete
210 match '/time_entries/destroy', :to => 'timelog#destroy', :via => :delete
211
211
212 get 'projects/:id/activity', :to => 'activities#index', :as => :project_activity
212 get 'projects/:id/activity', :to => 'activities#index', :as => :project_activity
213 get 'activity', :to => 'activities#index'
213 get 'activity', :to => 'activities#index'
214
214
215 # repositories routes
215 # repositories routes
216 get 'projects/:id/repository/:repository_id/statistics', :to => 'repositories#stats'
216 get 'projects/:id/repository/:repository_id/statistics', :to => 'repositories#stats'
217 get 'projects/:id/repository/:repository_id/graph', :to => 'repositories#graph'
217 get 'projects/:id/repository/:repository_id/graph', :to => 'repositories#graph'
218
218
219 get 'projects/:id/repository/:repository_id/changes(/*path(.:ext))',
219 get 'projects/:id/repository/:repository_id/changes(/*path(.:ext))',
220 :to => 'repositories#changes'
220 :to => 'repositories#changes'
221
221
222 get 'projects/:id/repository/:repository_id/revisions/:rev', :to => 'repositories#revision'
222 get 'projects/:id/repository/:repository_id/revisions/:rev', :to => 'repositories#revision'
223 get 'projects/:id/repository/:repository_id/revision', :to => 'repositories#revision'
223 get 'projects/:id/repository/:repository_id/revision', :to => 'repositories#revision'
224 post 'projects/:id/repository/:repository_id/revisions/:rev/issues', :to => 'repositories#add_related_issue'
224 post 'projects/:id/repository/:repository_id/revisions/:rev/issues', :to => 'repositories#add_related_issue'
225 delete 'projects/:id/repository/:repository_id/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue'
225 delete 'projects/:id/repository/:repository_id/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue'
226 get 'projects/:id/repository/:repository_id/revisions', :to => 'repositories#revisions'
226 get 'projects/:id/repository/:repository_id/revisions', :to => 'repositories#revisions'
227 get 'projects/:id/repository/:repository_id/revisions/:rev/:action(/*path(.:ext))',
227 get 'projects/:id/repository/:repository_id/revisions/:rev/:action(/*path(.:ext))',
228 :controller => 'repositories',
228 :controller => 'repositories',
229 :format => false,
229 :format => false,
230 :constraints => {
230 :constraints => {
231 :action => /(browse|show|entry|raw|annotate|diff)/,
231 :action => /(browse|show|entry|raw|annotate|diff)/,
232 :rev => /[a-z0-9\.\-_]+/
232 :rev => /[a-z0-9\.\-_]+/
233 }
233 }
234
234
235 get 'projects/:id/repository/statistics', :to => 'repositories#stats'
235 get 'projects/:id/repository/statistics', :to => 'repositories#stats'
236 get 'projects/:id/repository/graph', :to => 'repositories#graph'
236 get 'projects/:id/repository/graph', :to => 'repositories#graph'
237
237
238 get 'projects/:id/repository/changes(/*path(.:ext))',
238 get 'projects/:id/repository/changes(/*path(.:ext))',
239 :to => 'repositories#changes'
239 :to => 'repositories#changes'
240
240
241 get 'projects/:id/repository/revisions', :to => 'repositories#revisions'
241 get 'projects/:id/repository/revisions', :to => 'repositories#revisions'
242 get 'projects/:id/repository/revisions/:rev', :to => 'repositories#revision'
242 get 'projects/:id/repository/revisions/:rev', :to => 'repositories#revision'
243 get 'projects/:id/repository/revision', :to => 'repositories#revision'
243 get 'projects/:id/repository/revision', :to => 'repositories#revision'
244 post 'projects/:id/repository/revisions/:rev/issues', :to => 'repositories#add_related_issue'
244 post 'projects/:id/repository/revisions/:rev/issues', :to => 'repositories#add_related_issue'
245 delete 'projects/:id/repository/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue'
245 delete 'projects/:id/repository/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue'
246 get 'projects/:id/repository/revisions/:rev/:action(/*path(.:ext))',
246 get 'projects/:id/repository/revisions/:rev/:action(/*path(.:ext))',
247 :controller => 'repositories',
247 :controller => 'repositories',
248 :format => false,
248 :format => false,
249 :constraints => {
249 :constraints => {
250 :action => /(browse|show|entry|raw|annotate|diff)/,
250 :action => /(browse|show|entry|raw|annotate|diff)/,
251 :rev => /[a-z0-9\.\-_]+/
251 :rev => /[a-z0-9\.\-_]+/
252 }
252 }
253 get 'projects/:id/repository/:repository_id/:action(/*path(.:ext))',
253 get 'projects/:id/repository/:repository_id/:action(/*path(.:ext))',
254 :controller => 'repositories',
254 :controller => 'repositories',
255 :action => /(browse|show|entry|raw|changes|annotate|diff)/
255 :action => /(browse|show|entry|raw|changes|annotate|diff)/
256 get 'projects/:id/repository/:action(/*path(.:ext))',
256 get 'projects/:id/repository/:action(/*path(.:ext))',
257 :controller => 'repositories',
257 :controller => 'repositories',
258 :action => /(browse|show|entry|raw|changes|annotate|diff)/
258 :action => /(browse|show|entry|raw|changes|annotate|diff)/
259
259
260 get 'projects/:id/repository/:repository_id', :to => 'repositories#show', :path => nil
260 get 'projects/:id/repository/:repository_id', :to => 'repositories#show', :path => nil
261 get 'projects/:id/repository', :to => 'repositories#show', :path => nil
261 get 'projects/:id/repository', :to => 'repositories#show', :path => nil
262
262
263 # additional routes for having the file name at the end of url
263 # additional routes for having the file name at the end of url
264 get 'attachments/:id/:filename', :to => 'attachments#show', :id => /\d+/, :filename => /.*/, :as => 'named_attachment'
264 get 'attachments/:id/:filename', :to => 'attachments#show', :id => /\d+/, :filename => /.*/, :as => 'named_attachment'
265 get 'attachments/download/:id/:filename', :to => 'attachments#download', :id => /\d+/, :filename => /.*/, :as => 'download_named_attachment'
265 get 'attachments/download/:id/:filename', :to => 'attachments#download', :id => /\d+/, :filename => /.*/, :as => 'download_named_attachment'
266 get 'attachments/download/:id', :to => 'attachments#download', :id => /\d+/
266 get 'attachments/download/:id', :to => 'attachments#download', :id => /\d+/
267 get 'attachments/thumbnail/:id(/:size)', :to => 'attachments#thumbnail', :id => /\d+/, :size => /\d+/, :as => 'thumbnail'
267 get 'attachments/thumbnail/:id(/:size)', :to => 'attachments#thumbnail', :id => /\d+/, :size => /\d+/, :as => 'thumbnail'
268 resources :attachments, :only => [:show, :destroy]
268 resources :attachments, :only => [:show, :destroy]
269
269
270 resources :groups do
270 resources :groups do
271 resources :memberships, :controller => 'principal_memberships'
271 resources :memberships, :controller => 'principal_memberships'
272 member do
272 member do
273 get 'autocomplete_for_user'
273 get 'autocomplete_for_user'
274 end
274 end
275 end
275 end
276
276
277 get 'groups/:id/users/new', :to => 'groups#new_users', :id => /\d+/, :as => 'new_group_users'
277 get 'groups/:id/users/new', :to => 'groups#new_users', :id => /\d+/, :as => 'new_group_users'
278 post 'groups/:id/users', :to => 'groups#add_users', :id => /\d+/, :as => 'group_users'
278 post 'groups/:id/users', :to => 'groups#add_users', :id => /\d+/, :as => 'group_users'
279 delete 'groups/:id/users/:user_id', :to => 'groups#remove_user', :id => /\d+/, :as => 'group_user'
279 delete 'groups/:id/users/:user_id', :to => 'groups#remove_user', :id => /\d+/, :as => 'group_user'
280
280
281 resources :trackers, :except => :show do
281 resources :trackers, :except => :show do
282 collection do
282 collection do
283 match 'fields', :via => [:get, :post]
283 match 'fields', :via => [:get, :post]
284 end
284 end
285 end
285 end
286 resources :issue_statuses, :except => :show do
286 resources :issue_statuses, :except => :show do
287 collection do
287 collection do
288 post 'update_issue_done_ratio'
288 post 'update_issue_done_ratio'
289 end
289 end
290 end
290 end
291 resources :custom_fields, :except => :show
291 resources :custom_fields, :except => :show
292 resources :roles do
292 resources :roles do
293 collection do
293 collection do
294 match 'permissions', :via => [:get, :post]
294 match 'permissions', :via => [:get, :post]
295 end
295 end
296 end
296 end
297 resources :enumerations, :except => :show
297 resources :enumerations, :except => :show
298 match 'enumerations/:type', :to => 'enumerations#index', :via => :get
298 match 'enumerations/:type', :to => 'enumerations#index', :via => :get
299
299
300 get 'projects/:id/search', :controller => 'search', :action => 'index'
300 get 'projects/:id/search', :controller => 'search', :action => 'index'
301 get 'search', :controller => 'search', :action => 'index'
301 get 'search', :controller => 'search', :action => 'index'
302
302
303 match 'mail_handler', :controller => 'mail_handler', :action => 'index', :via => :post
303 match 'mail_handler', :controller => 'mail_handler', :action => 'index', :via => :post
304
304
305 match 'admin', :controller => 'admin', :action => 'index', :via => :get
305 match 'admin', :controller => 'admin', :action => 'index', :via => :get
306 match 'admin/projects', :controller => 'admin', :action => 'projects', :via => :get
306 match 'admin/projects', :controller => 'admin', :action => 'projects', :via => :get
307 match 'admin/plugins', :controller => 'admin', :action => 'plugins', :via => :get
307 match 'admin/plugins', :controller => 'admin', :action => 'plugins', :via => :get
308 match 'admin/info', :controller => 'admin', :action => 'info', :via => :get
308 match 'admin/info', :controller => 'admin', :action => 'info', :via => :get
309 match 'admin/test_email', :controller => 'admin', :action => 'test_email', :via => :get
309 match 'admin/test_email', :controller => 'admin', :action => 'test_email', :via => :get
310 match 'admin/default_configuration', :controller => 'admin', :action => 'default_configuration', :via => :post
310 match 'admin/default_configuration', :controller => 'admin', :action => 'default_configuration', :via => :post
311
311
312 resources :auth_sources do
312 resources :auth_sources do
313 member do
313 member do
314 get 'test_connection', :as => 'try_connection'
314 get 'test_connection', :as => 'try_connection'
315 end
315 end
316 collection do
316 collection do
317 get 'autocomplete_for_new_user'
317 get 'autocomplete_for_new_user'
318 end
318 end
319 end
319 end
320
320
321 match 'workflows', :controller => 'workflows', :action => 'index', :via => :get
321 match 'workflows', :controller => 'workflows', :action => 'index', :via => :get
322 match 'workflows/edit', :controller => 'workflows', :action => 'edit', :via => [:get, :post]
322 match 'workflows/edit', :controller => 'workflows', :action => 'edit', :via => [:get, :post]
323 match 'workflows/permissions', :controller => 'workflows', :action => 'permissions', :via => [:get, :post]
323 match 'workflows/permissions', :controller => 'workflows', :action => 'permissions', :via => [:get, :post]
324 match 'workflows/copy', :controller => 'workflows', :action => 'copy', :via => [:get, :post]
324 match 'workflows/copy', :controller => 'workflows', :action => 'copy', :via => [:get, :post]
325 match 'settings', :controller => 'settings', :action => 'index', :via => :get
325 match 'settings', :controller => 'settings', :action => 'index', :via => :get
326 match 'settings/edit', :controller => 'settings', :action => 'edit', :via => [:get, :post]
326 match 'settings/edit', :controller => 'settings', :action => 'edit', :via => [:get, :post]
327 match 'settings/plugin/:id', :controller => 'settings', :action => 'plugin', :via => [:get, :post], :as => 'plugin_settings'
327 match 'settings/plugin/:id', :controller => 'settings', :action => 'plugin', :via => [:get, :post], :as => 'plugin_settings'
328
328
329 match 'sys/projects', :to => 'sys#projects', :via => :get
329 match 'sys/projects', :to => 'sys#projects', :via => :get
330 match 'sys/projects/:id/repository', :to => 'sys#create_project_repository', :via => :post
330 match 'sys/projects/:id/repository', :to => 'sys#create_project_repository', :via => :post
331 match 'sys/fetch_changesets', :to => 'sys#fetch_changesets', :via => [:get, :post]
331 match 'sys/fetch_changesets', :to => 'sys#fetch_changesets', :via => [:get, :post]
332
332
333 match 'uploads', :to => 'attachments#upload', :via => :post
333 match 'uploads', :to => 'attachments#upload', :via => :post
334
334
335 get 'robots.txt', :to => 'welcome#robots'
335 get 'robots.txt', :to => 'welcome#robots'
336
336
337 Dir.glob File.expand_path("plugins/*", Rails.root) do |plugin_dir|
337 Dir.glob File.expand_path("plugins/*", Rails.root) do |plugin_dir|
338 file = File.join(plugin_dir, "config/routes.rb")
338 file = File.join(plugin_dir, "config/routes.rb")
339 if File.exists?(file)
339 if File.exists?(file)
340 begin
340 begin
341 instance_eval File.read(file)
341 instance_eval File.read(file)
342 rescue Exception => e
342 rescue Exception => e
343 puts "An error occurred while loading the routes definition of #{File.basename(plugin_dir)} plugin (#{file}): #{e.message}."
343 puts "An error occurred while loading the routes definition of #{File.basename(plugin_dir)} plugin (#{file}): #{e.message}."
344 exit 1
344 exit 1
345 end
345 end
346 end
346 end
347 end
347 end
348 end
348 end
@@ -1,729 +1,728
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # Redmine - project management software
2 # Redmine - project management software
3 # Copyright (C) 2006-2014 Jean-Philippe Lang
3 # Copyright (C) 2006-2014 Jean-Philippe Lang
4 #
4 #
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
8 # of the License, or (at your option) any later version.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
18
19 require File.expand_path('../../test_helper', __FILE__)
19 require File.expand_path('../../test_helper', __FILE__)
20
20
21 class TimelogControllerTest < ActionController::TestCase
21 class TimelogControllerTest < ActionController::TestCase
22 fixtures :projects, :enabled_modules, :roles, :members,
22 fixtures :projects, :enabled_modules, :roles, :members,
23 :member_roles, :issues, :time_entries, :users,
23 :member_roles, :issues, :time_entries, :users,
24 :trackers, :enumerations, :issue_statuses,
24 :trackers, :enumerations, :issue_statuses,
25 :custom_fields, :custom_values,
25 :custom_fields, :custom_values,
26 :projects_trackers, :custom_fields_trackers,
26 :projects_trackers, :custom_fields_trackers,
27 :custom_fields_projects
27 :custom_fields_projects
28
28
29 include Redmine::I18n
29 include Redmine::I18n
30
30
31 def test_new
31 def test_new
32 @request.session[:user_id] = 3
32 @request.session[:user_id] = 3
33 get :new
33 get :new
34 assert_response :success
34 assert_response :success
35 assert_template 'new'
35 assert_template 'new'
36 assert_select 'input[name=?][type=hidden]', 'project_id', 0
36 assert_select 'input[name=?][type=hidden]', 'project_id', 0
37 assert_select 'input[name=?][type=hidden]', 'issue_id', 0
37 assert_select 'input[name=?][type=hidden]', 'issue_id', 0
38 assert_select 'select[name=?]', 'time_entry[project_id]' do
38 assert_select 'select[name=?]', 'time_entry[project_id]' do
39 # blank option for project
39 # blank option for project
40 assert_select 'option[value=]'
40 assert_select 'option[value=]'
41 end
41 end
42 end
42 end
43
43
44 def test_new_with_project_id
44 def test_new_with_project_id
45 @request.session[:user_id] = 3
45 @request.session[:user_id] = 3
46 get :new, :project_id => 1
46 get :new, :project_id => 1
47 assert_response :success
47 assert_response :success
48 assert_template 'new'
48 assert_template 'new'
49 assert_select 'input[name=?][type=hidden]', 'project_id'
49 assert_select 'input[name=?][type=hidden]', 'project_id'
50 assert_select 'input[name=?][type=hidden]', 'issue_id', 0
50 assert_select 'input[name=?][type=hidden]', 'issue_id', 0
51 assert_select 'select[name=?]', 'time_entry[project_id]', 0
51 assert_select 'select[name=?]', 'time_entry[project_id]', 0
52 end
52 end
53
53
54 def test_new_with_issue_id
54 def test_new_with_issue_id
55 @request.session[:user_id] = 3
55 @request.session[:user_id] = 3
56 get :new, :issue_id => 2
56 get :new, :issue_id => 2
57 assert_response :success
57 assert_response :success
58 assert_template 'new'
58 assert_template 'new'
59 assert_select 'input[name=?][type=hidden]', 'project_id', 0
59 assert_select 'input[name=?][type=hidden]', 'project_id', 0
60 assert_select 'input[name=?][type=hidden]', 'issue_id'
60 assert_select 'input[name=?][type=hidden]', 'issue_id'
61 assert_select 'select[name=?]', 'time_entry[project_id]', 0
61 assert_select 'select[name=?]', 'time_entry[project_id]', 0
62 end
62 end
63
63
64 def test_new_without_project_should_prefill_the_form
64 def test_new_without_project_should_prefill_the_form
65 @request.session[:user_id] = 3
65 @request.session[:user_id] = 3
66 get :new, :time_entry => {:project_id => '1'}
66 get :new, :time_entry => {:project_id => '1'}
67 assert_response :success
67 assert_response :success
68 assert_template 'new'
68 assert_template 'new'
69 assert_select 'select[name=?]', 'time_entry[project_id]' do
69 assert_select 'select[name=?]', 'time_entry[project_id]' do
70 assert_select 'option[value=1][selected=selected]'
70 assert_select 'option[value=1][selected=selected]'
71 end
71 end
72 end
72 end
73
73
74 def test_new_without_project_should_deny_without_permission
74 def test_new_without_project_should_deny_without_permission
75 Role.all.each {|role| role.remove_permission! :log_time}
75 Role.all.each {|role| role.remove_permission! :log_time}
76 @request.session[:user_id] = 3
76 @request.session[:user_id] = 3
77
77
78 get :new
78 get :new
79 assert_response 403
79 assert_response 403
80 end
80 end
81
81
82 def test_new_should_select_default_activity
82 def test_new_should_select_default_activity
83 @request.session[:user_id] = 3
83 @request.session[:user_id] = 3
84 get :new, :project_id => 1
84 get :new, :project_id => 1
85 assert_response :success
85 assert_response :success
86 assert_select 'select[name=?]', 'time_entry[activity_id]' do
86 assert_select 'select[name=?]', 'time_entry[activity_id]' do
87 assert_select 'option[selected=selected]', :text => 'Development'
87 assert_select 'option[selected=selected]', :text => 'Development'
88 end
88 end
89 end
89 end
90
90
91 def test_new_should_only_show_active_time_entry_activities
91 def test_new_should_only_show_active_time_entry_activities
92 @request.session[:user_id] = 3
92 @request.session[:user_id] = 3
93 get :new, :project_id => 1
93 get :new, :project_id => 1
94 assert_response :success
94 assert_response :success
95 assert_no_tag 'option', :content => 'Inactive Activity'
95 assert_no_tag 'option', :content => 'Inactive Activity'
96 end
96 end
97
97
98 def test_get_edit_existing_time
98 def test_get_edit_existing_time
99 @request.session[:user_id] = 2
99 @request.session[:user_id] = 2
100 get :edit, :id => 2, :project_id => nil
100 get :edit, :id => 2, :project_id => nil
101 assert_response :success
101 assert_response :success
102 assert_template 'edit'
102 assert_template 'edit'
103 # Default activity selected
103 assert_tag :tag => 'form', :attributes => { :action => '/time_entries/2' }
104 assert_tag :tag => 'form', :attributes => { :action => '/projects/ecookbook/time_entries/2' }
105 end
104 end
106
105
107 def test_get_edit_with_an_existing_time_entry_with_inactive_activity
106 def test_get_edit_with_an_existing_time_entry_with_inactive_activity
108 te = TimeEntry.find(1)
107 te = TimeEntry.find(1)
109 te.activity = TimeEntryActivity.find_by_name("Inactive Activity")
108 te.activity = TimeEntryActivity.find_by_name("Inactive Activity")
110 te.save!
109 te.save!
111
110
112 @request.session[:user_id] = 1
111 @request.session[:user_id] = 1
113 get :edit, :project_id => 1, :id => 1
112 get :edit, :project_id => 1, :id => 1
114 assert_response :success
113 assert_response :success
115 assert_template 'edit'
114 assert_template 'edit'
116 # Blank option since nothing is pre-selected
115 # Blank option since nothing is pre-selected
117 assert_tag :tag => 'option', :content => '--- Please select ---'
116 assert_tag :tag => 'option', :content => '--- Please select ---'
118 end
117 end
119
118
120 def test_post_create
119 def test_post_create
121 @request.session[:user_id] = 3
120 @request.session[:user_id] = 3
122 assert_difference 'TimeEntry.count' do
121 assert_difference 'TimeEntry.count' do
123 post :create, :project_id => 1,
122 post :create, :project_id => 1,
124 :time_entry => {:comments => 'Some work on TimelogControllerTest',
123 :time_entry => {:comments => 'Some work on TimelogControllerTest',
125 # Not the default activity
124 # Not the default activity
126 :activity_id => '11',
125 :activity_id => '11',
127 :spent_on => '2008-03-14',
126 :spent_on => '2008-03-14',
128 :issue_id => '1',
127 :issue_id => '1',
129 :hours => '7.3'}
128 :hours => '7.3'}
130 assert_redirected_to '/projects/ecookbook/time_entries'
129 assert_redirected_to '/projects/ecookbook/time_entries'
131 end
130 end
132
131
133 t = TimeEntry.order('id DESC').first
132 t = TimeEntry.order('id DESC').first
134 assert_not_nil t
133 assert_not_nil t
135 assert_equal 'Some work on TimelogControllerTest', t.comments
134 assert_equal 'Some work on TimelogControllerTest', t.comments
136 assert_equal 1, t.project_id
135 assert_equal 1, t.project_id
137 assert_equal 1, t.issue_id
136 assert_equal 1, t.issue_id
138 assert_equal 11, t.activity_id
137 assert_equal 11, t.activity_id
139 assert_equal 7.3, t.hours
138 assert_equal 7.3, t.hours
140 assert_equal 3, t.user_id
139 assert_equal 3, t.user_id
141 end
140 end
142
141
143 def test_post_create_with_blank_issue
142 def test_post_create_with_blank_issue
144 @request.session[:user_id] = 3
143 @request.session[:user_id] = 3
145 assert_difference 'TimeEntry.count' do
144 assert_difference 'TimeEntry.count' do
146 post :create, :project_id => 1,
145 post :create, :project_id => 1,
147 :time_entry => {:comments => 'Some work on TimelogControllerTest',
146 :time_entry => {:comments => 'Some work on TimelogControllerTest',
148 # Not the default activity
147 # Not the default activity
149 :activity_id => '11',
148 :activity_id => '11',
150 :issue_id => '',
149 :issue_id => '',
151 :spent_on => '2008-03-14',
150 :spent_on => '2008-03-14',
152 :hours => '7.3'}
151 :hours => '7.3'}
153 assert_redirected_to '/projects/ecookbook/time_entries'
152 assert_redirected_to '/projects/ecookbook/time_entries'
154 end
153 end
155
154
156 t = TimeEntry.order('id DESC').first
155 t = TimeEntry.order('id DESC').first
157 assert_not_nil t
156 assert_not_nil t
158 assert_equal 'Some work on TimelogControllerTest', t.comments
157 assert_equal 'Some work on TimelogControllerTest', t.comments
159 assert_equal 1, t.project_id
158 assert_equal 1, t.project_id
160 assert_nil t.issue_id
159 assert_nil t.issue_id
161 assert_equal 11, t.activity_id
160 assert_equal 11, t.activity_id
162 assert_equal 7.3, t.hours
161 assert_equal 7.3, t.hours
163 assert_equal 3, t.user_id
162 assert_equal 3, t.user_id
164 end
163 end
165
164
166 def test_create_and_continue_at_project_level
165 def test_create_and_continue_at_project_level
167 @request.session[:user_id] = 2
166 @request.session[:user_id] = 2
168 assert_difference 'TimeEntry.count' do
167 assert_difference 'TimeEntry.count' do
169 post :create, :time_entry => {:project_id => '1',
168 post :create, :time_entry => {:project_id => '1',
170 :activity_id => '11',
169 :activity_id => '11',
171 :issue_id => '',
170 :issue_id => '',
172 :spent_on => '2008-03-14',
171 :spent_on => '2008-03-14',
173 :hours => '7.3'},
172 :hours => '7.3'},
174 :continue => '1'
173 :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'
174 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
175 end
177 end
176 end
178
177
179 def test_create_and_continue_at_issue_level
178 def test_create_and_continue_at_issue_level
180 @request.session[:user_id] = 2
179 @request.session[:user_id] = 2
181 assert_difference 'TimeEntry.count' do
180 assert_difference 'TimeEntry.count' do
182 post :create, :time_entry => {:project_id => '',
181 post :create, :time_entry => {:project_id => '',
183 :activity_id => '11',
182 :activity_id => '11',
184 :issue_id => '1',
183 :issue_id => '1',
185 :spent_on => '2008-03-14',
184 :spent_on => '2008-03-14',
186 :hours => '7.3'},
185 :hours => '7.3'},
187 :continue => '1'
186 :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='
187 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
188 end
190 end
189 end
191
190
192 def test_create_and_continue_with_project_id
191 def test_create_and_continue_with_project_id
193 @request.session[:user_id] = 2
192 @request.session[:user_id] = 2
194 assert_difference 'TimeEntry.count' do
193 assert_difference 'TimeEntry.count' do
195 post :create, :project_id => 1,
194 post :create, :project_id => 1,
196 :time_entry => {:activity_id => '11',
195 :time_entry => {:activity_id => '11',
197 :issue_id => '',
196 :issue_id => '',
198 :spent_on => '2008-03-14',
197 :spent_on => '2008-03-14',
199 :hours => '7.3'},
198 :hours => '7.3'},
200 :continue => '1'
199 :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='
200 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
201 end
203 end
202 end
204
203
205 def test_create_and_continue_with_issue_id
204 def test_create_and_continue_with_issue_id
206 @request.session[:user_id] = 2
205 @request.session[:user_id] = 2
207 assert_difference 'TimeEntry.count' do
206 assert_difference 'TimeEntry.count' do
208 post :create, :issue_id => 1,
207 post :create, :issue_id => 1,
209 :time_entry => {:activity_id => '11',
208 :time_entry => {:activity_id => '11',
210 :issue_id => '1',
209 :issue_id => '1',
211 :spent_on => '2008-03-14',
210 :spent_on => '2008-03-14',
212 :hours => '7.3'},
211 :hours => '7.3'},
213 :continue => '1'
212 :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='
213 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
214 end
216 end
215 end
217
216
218 def test_create_without_log_time_permission_should_be_denied
217 def test_create_without_log_time_permission_should_be_denied
219 @request.session[:user_id] = 2
218 @request.session[:user_id] = 2
220 Role.find_by_name('Manager').remove_permission! :log_time
219 Role.find_by_name('Manager').remove_permission! :log_time
221 post :create, :project_id => 1,
220 post :create, :project_id => 1,
222 :time_entry => {:activity_id => '11',
221 :time_entry => {:activity_id => '11',
223 :issue_id => '',
222 :issue_id => '',
224 :spent_on => '2008-03-14',
223 :spent_on => '2008-03-14',
225 :hours => '7.3'}
224 :hours => '7.3'}
226
225
227 assert_response 403
226 assert_response 403
228 end
227 end
229
228
230 def test_create_without_project_and_issue_should_fail
229 def test_create_without_project_and_issue_should_fail
231 @request.session[:user_id] = 2
230 @request.session[:user_id] = 2
232 post :create, :time_entry => {:issue_id => ''}
231 post :create, :time_entry => {:issue_id => ''}
233
232
234 assert_response :success
233 assert_response :success
235 assert_template 'new'
234 assert_template 'new'
236 end
235 end
237
236
238 def test_create_with_failure
237 def test_create_with_failure
239 @request.session[:user_id] = 2
238 @request.session[:user_id] = 2
240 post :create, :project_id => 1,
239 post :create, :project_id => 1,
241 :time_entry => {:activity_id => '',
240 :time_entry => {:activity_id => '',
242 :issue_id => '',
241 :issue_id => '',
243 :spent_on => '2008-03-14',
242 :spent_on => '2008-03-14',
244 :hours => '7.3'}
243 :hours => '7.3'}
245
244
246 assert_response :success
245 assert_response :success
247 assert_template 'new'
246 assert_template 'new'
248 end
247 end
249
248
250 def test_create_without_project
249 def test_create_without_project
251 @request.session[:user_id] = 2
250 @request.session[:user_id] = 2
252 assert_difference 'TimeEntry.count' do
251 assert_difference 'TimeEntry.count' do
253 post :create, :time_entry => {:project_id => '1',
252 post :create, :time_entry => {:project_id => '1',
254 :activity_id => '11',
253 :activity_id => '11',
255 :issue_id => '',
254 :issue_id => '',
256 :spent_on => '2008-03-14',
255 :spent_on => '2008-03-14',
257 :hours => '7.3'}
256 :hours => '7.3'}
258 end
257 end
259
258
260 assert_redirected_to '/projects/ecookbook/time_entries'
259 assert_redirected_to '/projects/ecookbook/time_entries'
261 time_entry = TimeEntry.order('id DESC').first
260 time_entry = TimeEntry.order('id DESC').first
262 assert_equal 1, time_entry.project_id
261 assert_equal 1, time_entry.project_id
263 end
262 end
264
263
265 def test_create_without_project_should_fail_with_issue_not_inside_project
264 def test_create_without_project_should_fail_with_issue_not_inside_project
266 @request.session[:user_id] = 2
265 @request.session[:user_id] = 2
267 assert_no_difference 'TimeEntry.count' do
266 assert_no_difference 'TimeEntry.count' do
268 post :create, :time_entry => {:project_id => '1',
267 post :create, :time_entry => {:project_id => '1',
269 :activity_id => '11',
268 :activity_id => '11',
270 :issue_id => '5',
269 :issue_id => '5',
271 :spent_on => '2008-03-14',
270 :spent_on => '2008-03-14',
272 :hours => '7.3'}
271 :hours => '7.3'}
273 end
272 end
274
273
275 assert_response :success
274 assert_response :success
276 assert assigns(:time_entry).errors[:issue_id].present?
275 assert assigns(:time_entry).errors[:issue_id].present?
277 end
276 end
278
277
279 def test_create_without_project_should_deny_without_permission
278 def test_create_without_project_should_deny_without_permission
280 @request.session[:user_id] = 2
279 @request.session[:user_id] = 2
281 Project.find(3).disable_module!(:time_tracking)
280 Project.find(3).disable_module!(:time_tracking)
282
281
283 assert_no_difference 'TimeEntry.count' do
282 assert_no_difference 'TimeEntry.count' do
284 post :create, :time_entry => {:project_id => '3',
283 post :create, :time_entry => {:project_id => '3',
285 :activity_id => '11',
284 :activity_id => '11',
286 :issue_id => '',
285 :issue_id => '',
287 :spent_on => '2008-03-14',
286 :spent_on => '2008-03-14',
288 :hours => '7.3'}
287 :hours => '7.3'}
289 end
288 end
290
289
291 assert_response 403
290 assert_response 403
292 end
291 end
293
292
294 def test_create_without_project_with_failure
293 def test_create_without_project_with_failure
295 @request.session[:user_id] = 2
294 @request.session[:user_id] = 2
296 assert_no_difference 'TimeEntry.count' do
295 assert_no_difference 'TimeEntry.count' do
297 post :create, :time_entry => {:project_id => '1',
296 post :create, :time_entry => {:project_id => '1',
298 :activity_id => '11',
297 :activity_id => '11',
299 :issue_id => '',
298 :issue_id => '',
300 :spent_on => '2008-03-14',
299 :spent_on => '2008-03-14',
301 :hours => ''}
300 :hours => ''}
302 end
301 end
303
302
304 assert_response :success
303 assert_response :success
305 assert_tag 'select', :attributes => {:name => 'time_entry[project_id]'},
304 assert_tag 'select', :attributes => {:name => 'time_entry[project_id]'},
306 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}}
305 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}}
307 end
306 end
308
307
309 def test_update
308 def test_update
310 entry = TimeEntry.find(1)
309 entry = TimeEntry.find(1)
311 assert_equal 1, entry.issue_id
310 assert_equal 1, entry.issue_id
312 assert_equal 2, entry.user_id
311 assert_equal 2, entry.user_id
313
312
314 @request.session[:user_id] = 1
313 @request.session[:user_id] = 1
315 put :update, :id => 1,
314 put :update, :id => 1,
316 :time_entry => {:issue_id => '2',
315 :time_entry => {:issue_id => '2',
317 :hours => '8'}
316 :hours => '8'}
318 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
317 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
319 entry.reload
318 entry.reload
320
319
321 assert_equal 8, entry.hours
320 assert_equal 8, entry.hours
322 assert_equal 2, entry.issue_id
321 assert_equal 2, entry.issue_id
323 assert_equal 2, entry.user_id
322 assert_equal 2, entry.user_id
324 end
323 end
325
324
326 def test_update_should_allow_to_change_issue_to_another_project
325 def test_update_should_allow_to_change_issue_to_another_project
327 entry = TimeEntry.generate!(:issue_id => 1)
326 entry = TimeEntry.generate!(:issue_id => 1)
328
327
329 @request.session[:user_id] = 1
328 @request.session[:user_id] = 1
330 put :update, :id => entry.id, :time_entry => {:issue_id => '5'}
329 put :update, :id => entry.id, :time_entry => {:issue_id => '5'}
331 assert_response 302
330 assert_response 302
332 entry.reload
331 entry.reload
333
332
334 assert_equal 5, entry.issue_id
333 assert_equal 5, entry.issue_id
335 assert_equal 3, entry.project_id
334 assert_equal 3, entry.project_id
336 end
335 end
337
336
338 def test_update_should_not_allow_to_change_issue_to_an_invalid_project
337 def test_update_should_not_allow_to_change_issue_to_an_invalid_project
339 entry = TimeEntry.generate!(:issue_id => 1)
338 entry = TimeEntry.generate!(:issue_id => 1)
340 Project.find(3).disable_module!(:time_tracking)
339 Project.find(3).disable_module!(:time_tracking)
341
340
342 @request.session[:user_id] = 1
341 @request.session[:user_id] = 1
343 put :update, :id => entry.id, :time_entry => {:issue_id => '5'}
342 put :update, :id => entry.id, :time_entry => {:issue_id => '5'}
344 assert_response 200
343 assert_response 200
345 assert_include "Issue is invalid", assigns(:time_entry).errors.full_messages
344 assert_include "Issue is invalid", assigns(:time_entry).errors.full_messages
346 end
345 end
347
346
348 def test_get_bulk_edit
347 def test_get_bulk_edit
349 @request.session[:user_id] = 2
348 @request.session[:user_id] = 2
350 get :bulk_edit, :ids => [1, 2]
349 get :bulk_edit, :ids => [1, 2]
351 assert_response :success
350 assert_response :success
352 assert_template 'bulk_edit'
351 assert_template 'bulk_edit'
353
352
354 assert_select 'ul#bulk-selection' do
353 assert_select 'ul#bulk-selection' do
355 assert_select 'li', 2
354 assert_select 'li', 2
356 assert_select 'li a', :text => '03/23/2007 - eCookbook: 4.25 hours'
355 assert_select 'li a', :text => '03/23/2007 - eCookbook: 4.25 hours'
357 end
356 end
358
357
359 assert_select 'form#bulk_edit_form[action=?]', '/time_entries/bulk_update' do
358 assert_select 'form#bulk_edit_form[action=?]', '/time_entries/bulk_update' do
360 # System wide custom field
359 # System wide custom field
361 assert_select 'select[name=?]', 'time_entry[custom_field_values][10]'
360 assert_select 'select[name=?]', 'time_entry[custom_field_values][10]'
362
361
363 # Activities
362 # Activities
364 assert_select 'select[name=?]', 'time_entry[activity_id]' do
363 assert_select 'select[name=?]', 'time_entry[activity_id]' do
365 assert_select 'option[value=]', :text => '(No change)'
364 assert_select 'option[value=]', :text => '(No change)'
366 assert_select 'option[value=9]', :text => 'Design'
365 assert_select 'option[value=9]', :text => 'Design'
367 end
366 end
368 end
367 end
369 end
368 end
370
369
371 def test_get_bulk_edit_on_different_projects
370 def test_get_bulk_edit_on_different_projects
372 @request.session[:user_id] = 2
371 @request.session[:user_id] = 2
373 get :bulk_edit, :ids => [1, 2, 6]
372 get :bulk_edit, :ids => [1, 2, 6]
374 assert_response :success
373 assert_response :success
375 assert_template 'bulk_edit'
374 assert_template 'bulk_edit'
376 end
375 end
377
376
378 def test_bulk_update
377 def test_bulk_update
379 @request.session[:user_id] = 2
378 @request.session[:user_id] = 2
380 # update time entry activity
379 # update time entry activity
381 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
380 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
382
381
383 assert_response 302
382 assert_response 302
384 # check that the issues were updated
383 # check that the issues were updated
385 assert_equal [9, 9], TimeEntry.where(:id => [1, 2]).collect {|i| i.activity_id}
384 assert_equal [9, 9], TimeEntry.where(:id => [1, 2]).collect {|i| i.activity_id}
386 end
385 end
387
386
388 def test_bulk_update_with_failure
387 def test_bulk_update_with_failure
389 @request.session[:user_id] = 2
388 @request.session[:user_id] = 2
390 post :bulk_update, :ids => [1, 2], :time_entry => { :hours => 'A'}
389 post :bulk_update, :ids => [1, 2], :time_entry => { :hours => 'A'}
391
390
392 assert_response 302
391 assert_response 302
393 assert_match /Failed to save 2 time entrie/, flash[:error]
392 assert_match /Failed to save 2 time entrie/, flash[:error]
394 end
393 end
395
394
396 def test_bulk_update_on_different_projects
395 def test_bulk_update_on_different_projects
397 @request.session[:user_id] = 2
396 @request.session[:user_id] = 2
398 # makes user a manager on the other project
397 # makes user a manager on the other project
399 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
398 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
400
399
401 # update time entry activity
400 # update time entry activity
402 post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
401 post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
403
402
404 assert_response 302
403 assert_response 302
405 # check that the issues were updated
404 # check that the issues were updated
406 assert_equal [9, 9, 9], TimeEntry.where(:id => [1, 2, 4]).collect {|i| i.activity_id}
405 assert_equal [9, 9, 9], TimeEntry.where(:id => [1, 2, 4]).collect {|i| i.activity_id}
407 end
406 end
408
407
409 def test_bulk_update_on_different_projects_without_rights
408 def test_bulk_update_on_different_projects_without_rights
410 @request.session[:user_id] = 3
409 @request.session[:user_id] = 3
411 user = User.find(3)
410 user = User.find(3)
412 action = { :controller => "timelog", :action => "bulk_update" }
411 action = { :controller => "timelog", :action => "bulk_update" }
413 assert user.allowed_to?(action, TimeEntry.find(1).project)
412 assert user.allowed_to?(action, TimeEntry.find(1).project)
414 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
413 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
415 post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
414 post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
416 assert_response 403
415 assert_response 403
417 end
416 end
418
417
419 def test_bulk_update_custom_field
418 def test_bulk_update_custom_field
420 @request.session[:user_id] = 2
419 @request.session[:user_id] = 2
421 post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
420 post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
422
421
423 assert_response 302
422 assert_response 302
424 assert_equal ["0", "0"], TimeEntry.where(:id => [1, 2]).collect {|i| i.custom_value_for(10).value}
423 assert_equal ["0", "0"], TimeEntry.where(:id => [1, 2]).collect {|i| i.custom_value_for(10).value}
425 end
424 end
426
425
427 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
426 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
428 @request.session[:user_id] = 2
427 @request.session[:user_id] = 2
429 post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
428 post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
430
429
431 assert_response :redirect
430 assert_response :redirect
432 assert_redirected_to '/time_entries'
431 assert_redirected_to '/time_entries'
433 end
432 end
434
433
435 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
434 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
436 @request.session[:user_id] = 2
435 @request.session[:user_id] = 2
437 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
436 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
438
437
439 assert_response :redirect
438 assert_response :redirect
440 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
439 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
441 end
440 end
442
441
443 def test_post_bulk_update_without_edit_permission_should_be_denied
442 def test_post_bulk_update_without_edit_permission_should_be_denied
444 @request.session[:user_id] = 2
443 @request.session[:user_id] = 2
445 Role.find_by_name('Manager').remove_permission! :edit_time_entries
444 Role.find_by_name('Manager').remove_permission! :edit_time_entries
446 post :bulk_update, :ids => [1,2]
445 post :bulk_update, :ids => [1,2]
447
446
448 assert_response 403
447 assert_response 403
449 end
448 end
450
449
451 def test_destroy
450 def test_destroy
452 @request.session[:user_id] = 2
451 @request.session[:user_id] = 2
453 delete :destroy, :id => 1
452 delete :destroy, :id => 1
454 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
453 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
455 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
454 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
456 assert_nil TimeEntry.find_by_id(1)
455 assert_nil TimeEntry.find_by_id(1)
457 end
456 end
458
457
459 def test_destroy_should_fail
458 def test_destroy_should_fail
460 # simulate that this fails (e.g. due to a plugin), see #5700
459 # simulate that this fails (e.g. due to a plugin), see #5700
461 TimeEntry.any_instance.expects(:destroy).returns(false)
460 TimeEntry.any_instance.expects(:destroy).returns(false)
462
461
463 @request.session[:user_id] = 2
462 @request.session[:user_id] = 2
464 delete :destroy, :id => 1
463 delete :destroy, :id => 1
465 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
464 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
466 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
465 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
467 assert_not_nil TimeEntry.find_by_id(1)
466 assert_not_nil TimeEntry.find_by_id(1)
468 end
467 end
469
468
470 def test_index_all_projects
469 def test_index_all_projects
471 get :index
470 get :index
472 assert_response :success
471 assert_response :success
473 assert_template 'index'
472 assert_template 'index'
474 assert_not_nil assigns(:total_hours)
473 assert_not_nil assigns(:total_hours)
475 assert_equal "162.90", "%.2f" % assigns(:total_hours)
474 assert_equal "162.90", "%.2f" % assigns(:total_hours)
476 assert_tag :form,
475 assert_tag :form,
477 :attributes => {:action => "/time_entries", :id => 'query_form'}
476 :attributes => {:action => "/time_entries", :id => 'query_form'}
478 end
477 end
479
478
480 def test_index_all_projects_should_show_log_time_link
479 def test_index_all_projects_should_show_log_time_link
481 @request.session[:user_id] = 2
480 @request.session[:user_id] = 2
482 get :index
481 get :index
483 assert_response :success
482 assert_response :success
484 assert_template 'index'
483 assert_template 'index'
485 assert_tag 'a', :attributes => {:href => '/time_entries/new'}, :content => /Log time/
484 assert_tag 'a', :attributes => {:href => '/time_entries/new'}, :content => /Log time/
486 end
485 end
487
486
488 def test_index_my_spent_time
487 def test_index_my_spent_time
489 @request.session[:user_id] = 2
488 @request.session[:user_id] = 2
490 get :index, :user_id => 'me'
489 get :index, :user_id => 'me'
491 assert_response :success
490 assert_response :success
492 assert_template 'index'
491 assert_template 'index'
493 assert assigns(:entries).all? {|entry| entry.user_id == 2}
492 assert assigns(:entries).all? {|entry| entry.user_id == 2}
494 end
493 end
495
494
496 def test_index_at_project_level
495 def test_index_at_project_level
497 get :index, :project_id => 'ecookbook'
496 get :index, :project_id => 'ecookbook'
498 assert_response :success
497 assert_response :success
499 assert_template 'index'
498 assert_template 'index'
500 assert_not_nil assigns(:entries)
499 assert_not_nil assigns(:entries)
501 assert_equal 4, assigns(:entries).size
500 assert_equal 4, assigns(:entries).size
502 # project and subproject
501 # project and subproject
503 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
502 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
504 assert_not_nil assigns(:total_hours)
503 assert_not_nil assigns(:total_hours)
505 assert_equal "162.90", "%.2f" % assigns(:total_hours)
504 assert_equal "162.90", "%.2f" % assigns(:total_hours)
506 assert_tag :form,
505 assert_tag :form,
507 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
506 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
508 end
507 end
509
508
510 def test_index_with_display_subprojects_issues_to_false_should_not_include_subproject_entries
509 def test_index_with_display_subprojects_issues_to_false_should_not_include_subproject_entries
511 entry = TimeEntry.generate!(:project => Project.find(3))
510 entry = TimeEntry.generate!(:project => Project.find(3))
512
511
513 with_settings :display_subprojects_issues => '0' do
512 with_settings :display_subprojects_issues => '0' do
514 get :index, :project_id => 'ecookbook'
513 get :index, :project_id => 'ecookbook'
515 assert_response :success
514 assert_response :success
516 assert_template 'index'
515 assert_template 'index'
517 assert_not_include entry, assigns(:entries)
516 assert_not_include entry, assigns(:entries)
518 end
517 end
519 end
518 end
520
519
521 def test_index_with_display_subprojects_issues_to_false_and_subproject_filter_should_include_subproject_entries
520 def test_index_with_display_subprojects_issues_to_false_and_subproject_filter_should_include_subproject_entries
522 entry = TimeEntry.generate!(:project => Project.find(3))
521 entry = TimeEntry.generate!(:project => Project.find(3))
523
522
524 with_settings :display_subprojects_issues => '0' do
523 with_settings :display_subprojects_issues => '0' do
525 get :index, :project_id => 'ecookbook', :subproject_id => 3
524 get :index, :project_id => 'ecookbook', :subproject_id => 3
526 assert_response :success
525 assert_response :success
527 assert_template 'index'
526 assert_template 'index'
528 assert_include entry, assigns(:entries)
527 assert_include entry, assigns(:entries)
529 end
528 end
530 end
529 end
531
530
532 def test_index_at_project_level_with_date_range
531 def test_index_at_project_level_with_date_range
533 get :index, :project_id => 'ecookbook',
532 get :index, :project_id => 'ecookbook',
534 :f => ['spent_on'],
533 :f => ['spent_on'],
535 :op => {'spent_on' => '><'},
534 :op => {'spent_on' => '><'},
536 :v => {'spent_on' => ['2007-03-20', '2007-04-30']}
535 :v => {'spent_on' => ['2007-03-20', '2007-04-30']}
537 assert_response :success
536 assert_response :success
538 assert_template 'index'
537 assert_template 'index'
539 assert_not_nil assigns(:entries)
538 assert_not_nil assigns(:entries)
540 assert_equal 3, assigns(:entries).size
539 assert_equal 3, assigns(:entries).size
541 assert_not_nil assigns(:total_hours)
540 assert_not_nil assigns(:total_hours)
542 assert_equal "12.90", "%.2f" % assigns(:total_hours)
541 assert_equal "12.90", "%.2f" % assigns(:total_hours)
543 assert_tag :form,
542 assert_tag :form,
544 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
543 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
545 end
544 end
546
545
547 def test_index_at_project_level_with_date_range_using_from_and_to_params
546 def test_index_at_project_level_with_date_range_using_from_and_to_params
548 get :index, :project_id => 'ecookbook', :from => '2007-03-20', :to => '2007-04-30'
547 get :index, :project_id => 'ecookbook', :from => '2007-03-20', :to => '2007-04-30'
549 assert_response :success
548 assert_response :success
550 assert_template 'index'
549 assert_template 'index'
551 assert_not_nil assigns(:entries)
550 assert_not_nil assigns(:entries)
552 assert_equal 3, assigns(:entries).size
551 assert_equal 3, assigns(:entries).size
553 assert_not_nil assigns(:total_hours)
552 assert_not_nil assigns(:total_hours)
554 assert_equal "12.90", "%.2f" % assigns(:total_hours)
553 assert_equal "12.90", "%.2f" % assigns(:total_hours)
555 assert_tag :form,
554 assert_tag :form,
556 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
555 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
557 end
556 end
558
557
559 def test_index_at_project_level_with_period
558 def test_index_at_project_level_with_period
560 get :index, :project_id => 'ecookbook',
559 get :index, :project_id => 'ecookbook',
561 :f => ['spent_on'],
560 :f => ['spent_on'],
562 :op => {'spent_on' => '>t-'},
561 :op => {'spent_on' => '>t-'},
563 :v => {'spent_on' => ['7']}
562 :v => {'spent_on' => ['7']}
564 assert_response :success
563 assert_response :success
565 assert_template 'index'
564 assert_template 'index'
566 assert_not_nil assigns(:entries)
565 assert_not_nil assigns(:entries)
567 assert_not_nil assigns(:total_hours)
566 assert_not_nil assigns(:total_hours)
568 assert_tag :form,
567 assert_tag :form,
569 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
568 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
570 end
569 end
571
570
572 def test_index_at_issue_level
571 def test_index_at_issue_level
573 get :index, :issue_id => 1
572 get :index, :issue_id => 1
574 assert_response :success
573 assert_response :success
575 assert_template 'index'
574 assert_template 'index'
576 assert_not_nil assigns(:entries)
575 assert_not_nil assigns(:entries)
577 assert_equal 2, assigns(:entries).size
576 assert_equal 2, assigns(:entries).size
578 assert_not_nil assigns(:total_hours)
577 assert_not_nil assigns(:total_hours)
579 assert_equal 154.25, assigns(:total_hours)
578 assert_equal 154.25, assigns(:total_hours)
580 # display all time
579 # display all time
581 assert_nil assigns(:from)
580 assert_nil assigns(:from)
582 assert_nil assigns(:to)
581 assert_nil assigns(:to)
583 assert_tag :form,
582 assert_tag :form,
584 :attributes => {:action => "/issues/1/time_entries", :id => 'query_form'}
583 :attributes => {:action => "/issues/1/time_entries", :id => 'query_form'}
585 end
584 end
586
585
587 def test_index_should_sort_by_spent_on_and_created_on
586 def test_index_should_sort_by_spent_on_and_created_on
588 t1 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-16', :created_on => '2012-06-16 20:00:00', :activity_id => 10)
587 t1 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-16', :created_on => '2012-06-16 20:00:00', :activity_id => 10)
589 t2 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-16', :created_on => '2012-06-16 20:05:00', :activity_id => 10)
588 t2 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-16', :created_on => '2012-06-16 20:05:00', :activity_id => 10)
590 t3 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-15', :created_on => '2012-06-16 20:10:00', :activity_id => 10)
589 t3 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-15', :created_on => '2012-06-16 20:10:00', :activity_id => 10)
591
590
592 get :index, :project_id => 1,
591 get :index, :project_id => 1,
593 :f => ['spent_on'],
592 :f => ['spent_on'],
594 :op => {'spent_on' => '><'},
593 :op => {'spent_on' => '><'},
595 :v => {'spent_on' => ['2012-06-15', '2012-06-16']}
594 :v => {'spent_on' => ['2012-06-15', '2012-06-16']}
596 assert_response :success
595 assert_response :success
597 assert_equal [t2, t1, t3], assigns(:entries)
596 assert_equal [t2, t1, t3], assigns(:entries)
598
597
599 get :index, :project_id => 1,
598 get :index, :project_id => 1,
600 :f => ['spent_on'],
599 :f => ['spent_on'],
601 :op => {'spent_on' => '><'},
600 :op => {'spent_on' => '><'},
602 :v => {'spent_on' => ['2012-06-15', '2012-06-16']},
601 :v => {'spent_on' => ['2012-06-15', '2012-06-16']},
603 :sort => 'spent_on'
602 :sort => 'spent_on'
604 assert_response :success
603 assert_response :success
605 assert_equal [t3, t1, t2], assigns(:entries)
604 assert_equal [t3, t1, t2], assigns(:entries)
606 end
605 end
607
606
608 def test_index_with_filter_on_issue_custom_field
607 def test_index_with_filter_on_issue_custom_field
609 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
608 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
610 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
609 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
611
610
612 get :index, :f => ['issue.cf_2'], :op => {'issue.cf_2' => '='}, :v => {'issue.cf_2' => ['filter_on_issue_custom_field']}
611 get :index, :f => ['issue.cf_2'], :op => {'issue.cf_2' => '='}, :v => {'issue.cf_2' => ['filter_on_issue_custom_field']}
613 assert_response :success
612 assert_response :success
614 assert_equal [entry], assigns(:entries)
613 assert_equal [entry], assigns(:entries)
615 end
614 end
616
615
617 def test_index_with_issue_custom_field_column
616 def test_index_with_issue_custom_field_column
618 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
617 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
619 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
618 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
620
619
621 get :index, :c => %w(project spent_on issue comments hours issue.cf_2)
620 get :index, :c => %w(project spent_on issue comments hours issue.cf_2)
622 assert_response :success
621 assert_response :success
623 assert_include :'issue.cf_2', assigns(:query).column_names
622 assert_include :'issue.cf_2', assigns(:query).column_names
624 assert_select 'td.issue_cf_2', :text => 'filter_on_issue_custom_field'
623 assert_select 'td.issue_cf_2', :text => 'filter_on_issue_custom_field'
625 end
624 end
626
625
627 def test_index_with_time_entry_custom_field_column
626 def test_index_with_time_entry_custom_field_column
628 field = TimeEntryCustomField.generate!(:field_format => 'string')
627 field = TimeEntryCustomField.generate!(:field_format => 'string')
629 entry = TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value'})
628 entry = TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value'})
630 field_name = "cf_#{field.id}"
629 field_name = "cf_#{field.id}"
631
630
632 get :index, :c => ["hours", field_name]
631 get :index, :c => ["hours", field_name]
633 assert_response :success
632 assert_response :success
634 assert_include field_name.to_sym, assigns(:query).column_names
633 assert_include field_name.to_sym, assigns(:query).column_names
635 assert_select "td.#{field_name}", :text => 'CF Value'
634 assert_select "td.#{field_name}", :text => 'CF Value'
636 end
635 end
637
636
638 def test_index_with_time_entry_custom_field_sorting
637 def test_index_with_time_entry_custom_field_sorting
639 field = TimeEntryCustomField.generate!(:field_format => 'string', :name => 'String Field')
638 field = TimeEntryCustomField.generate!(:field_format => 'string', :name => 'String Field')
640 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 1'})
639 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 1'})
641 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 3'})
640 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 3'})
642 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 2'})
641 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 2'})
643 field_name = "cf_#{field.id}"
642 field_name = "cf_#{field.id}"
644
643
645 get :index, :c => ["hours", field_name], :sort => field_name
644 get :index, :c => ["hours", field_name], :sort => field_name
646 assert_response :success
645 assert_response :success
647 assert_include field_name.to_sym, assigns(:query).column_names
646 assert_include field_name.to_sym, assigns(:query).column_names
648 assert_select "th a.sort", :text => 'String Field'
647 assert_select "th a.sort", :text => 'String Field'
649
648
650 # Make sure that values are properly sorted
649 # Make sure that values are properly sorted
651 values = assigns(:entries).map {|e| e.custom_field_value(field)}.compact
650 values = assigns(:entries).map {|e| e.custom_field_value(field)}.compact
652 assert_equal 3, values.size
651 assert_equal 3, values.size
653 assert_equal values.sort, values
652 assert_equal values.sort, values
654 end
653 end
655
654
656 def test_index_atom_feed
655 def test_index_atom_feed
657 get :index, :project_id => 1, :format => 'atom'
656 get :index, :project_id => 1, :format => 'atom'
658 assert_response :success
657 assert_response :success
659 assert_equal 'application/atom+xml', @response.content_type
658 assert_equal 'application/atom+xml', @response.content_type
660 assert_not_nil assigns(:items)
659 assert_not_nil assigns(:items)
661 assert assigns(:items).first.is_a?(TimeEntry)
660 assert assigns(:items).first.is_a?(TimeEntry)
662 end
661 end
663
662
664 def test_index_at_project_level_should_include_csv_export_dialog
663 def test_index_at_project_level_should_include_csv_export_dialog
665 get :index, :project_id => 'ecookbook',
664 get :index, :project_id => 'ecookbook',
666 :f => ['spent_on'],
665 :f => ['spent_on'],
667 :op => {'spent_on' => '>='},
666 :op => {'spent_on' => '>='},
668 :v => {'spent_on' => ['2007-04-01']},
667 :v => {'spent_on' => ['2007-04-01']},
669 :c => ['spent_on', 'user']
668 :c => ['spent_on', 'user']
670 assert_response :success
669 assert_response :success
671
670
672 assert_select '#csv-export-options' do
671 assert_select '#csv-export-options' do
673 assert_select 'form[action=?][method=get]', '/projects/ecookbook/time_entries.csv' do
672 assert_select 'form[action=?][method=get]', '/projects/ecookbook/time_entries.csv' do
674 # filter
673 # filter
675 assert_select 'input[name=?][value=?]', 'f[]', 'spent_on'
674 assert_select 'input[name=?][value=?]', 'f[]', 'spent_on'
676 assert_select 'input[name=?][value=?]', 'op[spent_on]', '&gt;='
675 assert_select 'input[name=?][value=?]', 'op[spent_on]', '&gt;='
677 assert_select 'input[name=?][value=?]', 'v[spent_on][]', '2007-04-01'
676 assert_select 'input[name=?][value=?]', 'v[spent_on][]', '2007-04-01'
678 # columns
677 # columns
679 assert_select 'input[name=?][value=?]', 'c[]', 'spent_on'
678 assert_select 'input[name=?][value=?]', 'c[]', 'spent_on'
680 assert_select 'input[name=?][value=?]', 'c[]', 'user'
679 assert_select 'input[name=?][value=?]', 'c[]', 'user'
681 assert_select 'input[name=?]', 'c[]', 2
680 assert_select 'input[name=?]', 'c[]', 2
682 end
681 end
683 end
682 end
684 end
683 end
685
684
686 def test_index_cross_project_should_include_csv_export_dialog
685 def test_index_cross_project_should_include_csv_export_dialog
687 get :index
686 get :index
688 assert_response :success
687 assert_response :success
689
688
690 assert_select '#csv-export-options' do
689 assert_select '#csv-export-options' do
691 assert_select 'form[action=?][method=get]', '/time_entries.csv'
690 assert_select 'form[action=?][method=get]', '/time_entries.csv'
692 end
691 end
693 end
692 end
694
693
695 def test_index_at_issue_level_should_include_csv_export_dialog
694 def test_index_at_issue_level_should_include_csv_export_dialog
696 get :index, :issue_id => 3
695 get :index, :issue_id => 3
697 assert_response :success
696 assert_response :success
698
697
699 assert_select '#csv-export-options' do
698 assert_select '#csv-export-options' do
700 assert_select 'form[action=?][method=get]', '/issues/3/time_entries.csv'
699 assert_select 'form[action=?][method=get]', '/issues/3/time_entries.csv'
701 end
700 end
702 end
701 end
703
702
704 def test_index_csv_all_projects
703 def test_index_csv_all_projects
705 with_settings :date_format => '%m/%d/%Y' do
704 with_settings :date_format => '%m/%d/%Y' do
706 get :index, :format => 'csv'
705 get :index, :format => 'csv'
707 assert_response :success
706 assert_response :success
708 assert_equal 'text/csv; header=present', response.content_type
707 assert_equal 'text/csv; header=present', response.content_type
709 end
708 end
710 end
709 end
711
710
712 def test_index_csv
711 def test_index_csv
713 with_settings :date_format => '%m/%d/%Y' do
712 with_settings :date_format => '%m/%d/%Y' do
714 get :index, :project_id => 1, :format => 'csv'
713 get :index, :project_id => 1, :format => 'csv'
715 assert_response :success
714 assert_response :success
716 assert_equal 'text/csv; header=present', response.content_type
715 assert_equal 'text/csv; header=present', response.content_type
717 end
716 end
718 end
717 end
719
718
720 def test_index_csv_should_fill_issue_column_with_tracker_id_and_subject
719 def test_index_csv_should_fill_issue_column_with_tracker_id_and_subject
721 issue = Issue.find(1)
720 issue = Issue.find(1)
722 entry = TimeEntry.generate!(:issue => issue, :comments => "Issue column content test")
721 entry = TimeEntry.generate!(:issue => issue, :comments => "Issue column content test")
723
722
724 get :index, :format => 'csv'
723 get :index, :format => 'csv'
725 line = response.body.split("\n").detect {|l| l.include?(entry.comments)}
724 line = response.body.split("\n").detect {|l| l.include?(entry.comments)}
726 assert_not_nil line
725 assert_not_nil line
727 assert_include "#{issue.tracker} #1: #{issue.subject}", line
726 assert_include "#{issue.tracker} #1: #{issue.subject}", line
728 end
727 end
729 end
728 end
@@ -1,75 +1,65
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 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.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
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 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingTimelogsTest < Redmine::RoutingTest
20 class RoutingTimelogsTest < Redmine::RoutingTest
21 def test_timelogs_global
21 def test_timelogs_global
22 should_route 'GET /time_entries' => 'timelog#index'
22 should_route 'GET /time_entries' => 'timelog#index'
23 should_route 'GET /time_entries.csv' => 'timelog#index', :format => 'csv'
23 should_route 'GET /time_entries.csv' => 'timelog#index', :format => 'csv'
24 should_route 'GET /time_entries.atom' => 'timelog#index', :format => 'atom'
24 should_route 'GET /time_entries.atom' => 'timelog#index', :format => 'atom'
25 should_route 'GET /time_entries/new' => 'timelog#new'
25 should_route 'GET /time_entries/new' => 'timelog#new'
26 should_route 'POST /time_entries' => 'timelog#create'
26 should_route 'POST /time_entries' => 'timelog#create'
27
27
28 should_route 'GET /time_entries/22/edit' => 'timelog#edit', :id => '22'
28 should_route 'GET /time_entries/22/edit' => 'timelog#edit', :id => '22'
29 should_route 'PUT /time_entries/22' => 'timelog#update', :id => '22'
29 should_route 'PUT /time_entries/22' => 'timelog#update', :id => '22'
30 should_route 'DELETE /time_entries/22' => 'timelog#destroy', :id => '22'
30 should_route 'DELETE /time_entries/22' => 'timelog#destroy', :id => '22'
31 end
31 end
32
32
33 def test_timelogs_scoped_under_project
33 def test_timelogs_scoped_under_project
34 should_route 'GET /projects/foo/time_entries' => 'timelog#index', :project_id => 'foo'
34 should_route 'GET /projects/foo/time_entries' => 'timelog#index', :project_id => 'foo'
35 should_route 'GET /projects/foo/time_entries.csv' => 'timelog#index', :project_id => 'foo', :format => 'csv'
35 should_route 'GET /projects/foo/time_entries.csv' => 'timelog#index', :project_id => 'foo', :format => 'csv'
36 should_route 'GET /projects/foo/time_entries.atom' => 'timelog#index', :project_id => 'foo', :format => 'atom'
36 should_route 'GET /projects/foo/time_entries.atom' => 'timelog#index', :project_id => 'foo', :format => 'atom'
37 should_route 'GET /projects/foo/time_entries/new' => 'timelog#new', :project_id => 'foo'
37 should_route 'GET /projects/foo/time_entries/new' => 'timelog#new', :project_id => 'foo'
38 should_route 'POST /projects/foo/time_entries' => 'timelog#create', :project_id => 'foo'
38 should_route 'POST /projects/foo/time_entries' => 'timelog#create', :project_id => 'foo'
39
40 # TODO: unused?
41 should_route 'GET /projects/foo/time_entries/22/edit' => 'timelog#edit', :project_id => 'foo', :id => '22'
42 should_route 'PUT /projects/foo/time_entries/22' => 'timelog#update', :project_id => 'foo', :id => '22'
43 should_route 'DELETE /projects/foo/time_entries/22' => 'timelog#destroy', :project_id => 'foo', :id => '22'
44 end
39 end
45
40
46 def test_timelogs_scoped_under_issues
41 def test_timelogs_scoped_under_issues
47 should_route 'GET /issues/234/time_entries' => 'timelog#index', :issue_id => '234'
42 should_route 'GET /issues/234/time_entries' => 'timelog#index', :issue_id => '234'
48 should_route 'GET /issues/234/time_entries.csv' => 'timelog#index', :issue_id => '234', :format => 'csv'
43 should_route 'GET /issues/234/time_entries.csv' => 'timelog#index', :issue_id => '234', :format => 'csv'
49 should_route 'GET /issues/234/time_entries.atom' => 'timelog#index', :issue_id => '234', :format => 'atom'
44 should_route 'GET /issues/234/time_entries.atom' => 'timelog#index', :issue_id => '234', :format => 'atom'
50 should_route 'GET /issues/234/time_entries/new' => 'timelog#new', :issue_id => '234'
45 should_route 'GET /issues/234/time_entries/new' => 'timelog#new', :issue_id => '234'
51 should_route 'POST /issues/234/time_entries' => 'timelog#create', :issue_id => '234'
46 should_route 'POST /issues/234/time_entries' => 'timelog#create', :issue_id => '234'
52
53 # TODO: unused?
54 should_route 'GET /issues/234/time_entries/22/edit' => 'timelog#edit', :issue_id => '234', :id => '22'
55 should_route 'PUT /issues/234/time_entries/22' => 'timelog#update', :issue_id => '234', :id => '22'
56 should_route 'DELETE /issues/234/time_entries/22' => 'timelog#destroy', :issue_id => '234', :id => '22'
57 end
47 end
58
48
59 def test_timelogs_report
49 def test_timelogs_report
60 should_route 'GET /time_entries/report' => 'timelog#report'
50 should_route 'GET /time_entries/report' => 'timelog#report'
61 should_route 'GET /time_entries/report.csv' => 'timelog#report', :format => 'csv'
51 should_route 'GET /time_entries/report.csv' => 'timelog#report', :format => 'csv'
62
52
63 should_route 'GET /projects/foo/time_entries/report' => 'timelog#report', :project_id => 'foo'
53 should_route 'GET /projects/foo/time_entries/report' => 'timelog#report', :project_id => 'foo'
64 should_route 'GET /projects/foo/time_entries/report.csv' => 'timelog#report', :project_id => 'foo', :format => 'csv'
54 should_route 'GET /projects/foo/time_entries/report.csv' => 'timelog#report', :project_id => 'foo', :format => 'csv'
65
55
66 should_route 'GET /issues/234/time_entries/report' => 'timelog#report', :issue_id => '234'
56 should_route 'GET /issues/234/time_entries/report' => 'timelog#report', :issue_id => '234'
67 should_route 'GET /issues/234/time_entries/report.csv' => 'timelog#report', :issue_id => '234', :format => 'csv'
57 should_route 'GET /issues/234/time_entries/report.csv' => 'timelog#report', :issue_id => '234', :format => 'csv'
68 end
58 end
69
59
70 def test_timelogs_bulk_edit
60 def test_timelogs_bulk_edit
71 should_route 'GET /time_entries/bulk_edit' => 'timelog#bulk_edit'
61 should_route 'GET /time_entries/bulk_edit' => 'timelog#bulk_edit'
72 should_route 'POST /time_entries/bulk_update' => 'timelog#bulk_update'
62 should_route 'POST /time_entries/bulk_update' => 'timelog#bulk_update'
73 should_route 'DELETE /time_entries/destroy' => 'timelog#destroy'
63 should_route 'DELETE /time_entries/destroy' => 'timelog#destroy'
74 end
64 end
75 end
65 end
General Comments 0
You need to be logged in to leave comments. Login now