##// END OF EJS Templates
Refactor: Move the updating of an Issue to the #update method....
Eric Davis -
r3372:c68d853bf4cf
parent child
Show More
@@ -1,569 +1,587
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 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 class IssuesController < ApplicationController
18 class IssuesController < ApplicationController
19 menu_item :new_issue, :only => :new
19 menu_item :new_issue, :only => :new
20 default_search_scope :issues
20 default_search_scope :issues
21
21
22 before_filter :find_issue, :only => [:show, :edit, :update, :reply]
22 before_filter :find_issue, :only => [:show, :edit, :update, :reply]
23 before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
23 before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
24 before_filter :find_project, :only => [:new, :update_form, :preview]
24 before_filter :find_project, :only => [:new, :update_form, :preview]
25 before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :context_menu]
25 before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :context_menu]
26 before_filter :find_optional_project, :only => [:index, :changes, :gantt, :calendar]
26 before_filter :find_optional_project, :only => [:index, :changes, :gantt, :calendar]
27 accept_key_auth :index, :show, :changes
27 accept_key_auth :index, :show, :changes
28
28
29 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
29 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
30
30
31 helper :journals
31 helper :journals
32 helper :projects
32 helper :projects
33 include ProjectsHelper
33 include ProjectsHelper
34 helper :custom_fields
34 helper :custom_fields
35 include CustomFieldsHelper
35 include CustomFieldsHelper
36 helper :issue_relations
36 helper :issue_relations
37 include IssueRelationsHelper
37 include IssueRelationsHelper
38 helper :watchers
38 helper :watchers
39 include WatchersHelper
39 include WatchersHelper
40 helper :attachments
40 helper :attachments
41 include AttachmentsHelper
41 include AttachmentsHelper
42 helper :queries
42 helper :queries
43 include QueriesHelper
43 include QueriesHelper
44 helper :sort
44 helper :sort
45 include SortHelper
45 include SortHelper
46 include IssuesHelper
46 include IssuesHelper
47 helper :timelog
47 helper :timelog
48 include Redmine::Export::PDF
48 include Redmine::Export::PDF
49
49
50 verify :method => [:post, :delete],
50 verify :method => [:post, :delete],
51 :only => :destroy,
51 :only => :destroy,
52 :render => { :nothing => true, :status => :method_not_allowed }
52 :render => { :nothing => true, :status => :method_not_allowed }
53
53
54 def index
54 def index
55 retrieve_query
55 retrieve_query
56 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
56 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
57 sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.available_columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
57 sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.available_columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
58
58
59 if @query.valid?
59 if @query.valid?
60 limit = case params[:format]
60 limit = case params[:format]
61 when 'csv', 'pdf'
61 when 'csv', 'pdf'
62 Setting.issues_export_limit.to_i
62 Setting.issues_export_limit.to_i
63 when 'atom'
63 when 'atom'
64 Setting.feeds_limit.to_i
64 Setting.feeds_limit.to_i
65 else
65 else
66 per_page_option
66 per_page_option
67 end
67 end
68
68
69 @issue_count = @query.issue_count
69 @issue_count = @query.issue_count
70 @issue_pages = Paginator.new self, @issue_count, limit, params['page']
70 @issue_pages = Paginator.new self, @issue_count, limit, params['page']
71 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
71 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
72 :order => sort_clause,
72 :order => sort_clause,
73 :offset => @issue_pages.current.offset,
73 :offset => @issue_pages.current.offset,
74 :limit => limit)
74 :limit => limit)
75 @issue_count_by_group = @query.issue_count_by_group
75 @issue_count_by_group = @query.issue_count_by_group
76
76
77 respond_to do |format|
77 respond_to do |format|
78 format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
78 format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
79 format.xml { render :layout => false }
79 format.xml { render :layout => false }
80 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
80 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
81 format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
81 format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
82 format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
82 format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
83 end
83 end
84 else
84 else
85 # Send html if the query is not valid
85 # Send html if the query is not valid
86 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
86 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
87 end
87 end
88 rescue ActiveRecord::RecordNotFound
88 rescue ActiveRecord::RecordNotFound
89 render_404
89 render_404
90 end
90 end
91
91
92 def changes
92 def changes
93 retrieve_query
93 retrieve_query
94 sort_init 'id', 'desc'
94 sort_init 'id', 'desc'
95 sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.available_columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
95 sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.available_columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
96
96
97 if @query.valid?
97 if @query.valid?
98 @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
98 @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
99 :limit => 25)
99 :limit => 25)
100 end
100 end
101 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
101 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
102 render :layout => false, :content_type => 'application/atom+xml'
102 render :layout => false, :content_type => 'application/atom+xml'
103 rescue ActiveRecord::RecordNotFound
103 rescue ActiveRecord::RecordNotFound
104 render_404
104 render_404
105 end
105 end
106
106
107 def show
107 def show
108 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
108 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
109 @journals.each_with_index {|j,i| j.indice = i+1}
109 @journals.each_with_index {|j,i| j.indice = i+1}
110 @journals.reverse! if User.current.wants_comments_in_reverse_order?
110 @journals.reverse! if User.current.wants_comments_in_reverse_order?
111 @changesets = @issue.changesets.visible.all
111 @changesets = @issue.changesets.visible.all
112 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
112 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
113 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
113 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
114 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
114 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
115 @priorities = IssuePriority.all
115 @priorities = IssuePriority.all
116 @time_entry = TimeEntry.new
116 @time_entry = TimeEntry.new
117 respond_to do |format|
117 respond_to do |format|
118 format.html { render :template => 'issues/show.rhtml' }
118 format.html { render :template => 'issues/show.rhtml' }
119 format.xml { render :layout => false }
119 format.xml { render :layout => false }
120 format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
120 format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
121 format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
121 format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
122 end
122 end
123 end
123 end
124
124
125 # Add a new issue
125 # Add a new issue
126 # The new issue will be created from an existing one if copy_from parameter is given
126 # The new issue will be created from an existing one if copy_from parameter is given
127 def new
127 def new
128 @issue = Issue.new
128 @issue = Issue.new
129 @issue.copy_from(params[:copy_from]) if params[:copy_from]
129 @issue.copy_from(params[:copy_from]) if params[:copy_from]
130 @issue.project = @project
130 @issue.project = @project
131 # Tracker must be set before custom field values
131 # Tracker must be set before custom field values
132 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
132 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
133 if @issue.tracker.nil?
133 if @issue.tracker.nil?
134 render_error l(:error_no_tracker_in_project)
134 render_error l(:error_no_tracker_in_project)
135 return
135 return
136 end
136 end
137 if params[:issue].is_a?(Hash)
137 if params[:issue].is_a?(Hash)
138 @issue.safe_attributes = params[:issue]
138 @issue.safe_attributes = params[:issue]
139 @issue.watcher_user_ids = params[:issue]['watcher_user_ids'] if User.current.allowed_to?(:add_issue_watchers, @project)
139 @issue.watcher_user_ids = params[:issue]['watcher_user_ids'] if User.current.allowed_to?(:add_issue_watchers, @project)
140 end
140 end
141 @issue.author = User.current
141 @issue.author = User.current
142
142
143 default_status = IssueStatus.default
143 default_status = IssueStatus.default
144 unless default_status
144 unless default_status
145 render_error l(:error_no_default_issue_status)
145 render_error l(:error_no_default_issue_status)
146 return
146 return
147 end
147 end
148 @issue.status = default_status
148 @issue.status = default_status
149 @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq
149 @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq
150
150
151 if request.get? || request.xhr?
151 if request.get? || request.xhr?
152 @issue.start_date ||= Date.today
152 @issue.start_date ||= Date.today
153 else
153 else
154 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
154 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
155 # Check that the user is allowed to apply the requested status
155 # Check that the user is allowed to apply the requested status
156 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
156 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
157 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
157 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
158 if @issue.save
158 if @issue.save
159 attach_files(@issue, params[:attachments])
159 attach_files(@issue, params[:attachments])
160 flash[:notice] = l(:notice_successful_create)
160 flash[:notice] = l(:notice_successful_create)
161 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
161 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
162 respond_to do |format|
162 respond_to do |format|
163 format.html {
163 format.html {
164 redirect_to(params[:continue] ? { :action => 'new', :tracker_id => @issue.tracker } :
164 redirect_to(params[:continue] ? { :action => 'new', :tracker_id => @issue.tracker } :
165 { :action => 'show', :id => @issue })
165 { :action => 'show', :id => @issue })
166 }
166 }
167 format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'issues', :action => 'show', :id => @issue) }
167 format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'issues', :action => 'show', :id => @issue) }
168 end
168 end
169 return
169 return
170 else
170 else
171 respond_to do |format|
171 respond_to do |format|
172 format.html { }
172 format.html { }
173 format.xml { render(:xml => @issue.errors, :status => :unprocessable_entity); return }
173 format.xml { render(:xml => @issue.errors, :status => :unprocessable_entity); return }
174 end
174 end
175 end
175 end
176 end
176 end
177 @priorities = IssuePriority.all
177 @priorities = IssuePriority.all
178 render :layout => !request.xhr?
178 render :layout => !request.xhr?
179 end
179 end
180
180
181 # Attributes that can be updated on workflow transition (without :edit permission)
181 # Attributes that can be updated on workflow transition (without :edit permission)
182 # TODO: make it configurable (at least per role)
182 # TODO: make it configurable (at least per role)
183 UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
183 UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
184
184
185 def edit
185 def edit
186 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
186 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
187 @priorities = IssuePriority.all
187 @priorities = IssuePriority.all
188 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
188 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
189 @time_entry = TimeEntry.new
189 @time_entry = TimeEntry.new
190
190
191 @notes = params[:notes]
191 @notes = params[:notes]
192 journal = @issue.init_journal(User.current, @notes)
192 journal = @issue.init_journal(User.current, @notes)
193 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
193 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
194 if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
194 if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
195 attrs = params[:issue].dup
195 attrs = params[:issue].dup
196 attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
196 attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
197 attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
197 attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
198 @issue.safe_attributes = attrs
198 @issue.safe_attributes = attrs
199 end
199 end
200
200
201 respond_to do |format|
202 format.html { }
203 format.xml { }
204 end
205 end
206
207 #--
208 # Start converting to the Rails REST controllers
209 #++
210 def update
211 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
212 @priorities = IssuePriority.all
213 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
214 @time_entry = TimeEntry.new
215
216 @notes = params[:notes]
217 journal = @issue.init_journal(User.current, @notes)
218 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
219 if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
220 attrs = params[:issue].dup
221 attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
222 attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
223 @issue.safe_attributes = attrs
224 end
225
201 if request.get?
226 if request.get?
202 # nop
227 # nop
203 else
228 else
204 @time_entry = TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
229 @time_entry = TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
205 @time_entry.attributes = params[:time_entry]
230 @time_entry.attributes = params[:time_entry]
206 if (@time_entry.hours.nil? || @time_entry.valid?) && @issue.valid?
231 if (@time_entry.hours.nil? || @time_entry.valid?) && @issue.valid?
207 attachments = attach_files(@issue, params[:attachments])
232 attachments = attach_files(@issue, params[:attachments])
208 attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
233 attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
209 call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
234 call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
210 if @issue.save
235 if @issue.save
211 # Log spend time
236 # Log spend time
212 if User.current.allowed_to?(:log_time, @project)
237 if User.current.allowed_to?(:log_time, @project)
213 @time_entry.save
238 @time_entry.save
214 end
239 end
215 if !journal.new_record?
240 if !journal.new_record?
216 # Only send notification if something was actually changed
241 # Only send notification if something was actually changed
217 flash[:notice] = l(:notice_successful_update)
242 flash[:notice] = l(:notice_successful_update)
218 end
243 end
219 call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
244 call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
220 respond_to do |format|
245 respond_to do |format|
221 format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
246 format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
222 format.xml { head :ok }
247 format.xml { head :ok }
223 end
248 end
224 return
249 return
225 end
250 end
226 end
251 end
227 # failure
252 # failure
228 respond_to do |format|
253 respond_to do |format|
229 format.html { render :action => 'edit' }
254 format.html { render :action => 'edit' }
230 format.xml { render :xml => @issue.errors, :status => :unprocessable_entity }
255 format.xml { render :xml => @issue.errors, :status => :unprocessable_entity }
231 end
256 end
232 end
257 end
233 rescue ActiveRecord::StaleObjectError
258 rescue ActiveRecord::StaleObjectError
234 # Optimistic locking exception
259 # Optimistic locking exception
235 flash.now[:error] = l(:notice_locking_conflict)
260 flash.now[:error] = l(:notice_locking_conflict)
236 # Remove the previously added attachments if issue was not updated
261 # Remove the previously added attachments if issue was not updated
237 attachments.each(&:destroy)
262 attachments.each(&:destroy)
238 end
263 end
239
264
240 #--
241 # Start converting to the Rails REST controllers
242 #++
243 def update
244 edit
245 end
246
247 def reply
265 def reply
248 journal = Journal.find(params[:journal_id]) if params[:journal_id]
266 journal = Journal.find(params[:journal_id]) if params[:journal_id]
249 if journal
267 if journal
250 user = journal.user
268 user = journal.user
251 text = journal.notes
269 text = journal.notes
252 else
270 else
253 user = @issue.author
271 user = @issue.author
254 text = @issue.description
272 text = @issue.description
255 end
273 end
256 content = "#{ll(Setting.default_language, :text_user_wrote, user)}\\n> "
274 content = "#{ll(Setting.default_language, :text_user_wrote, user)}\\n> "
257 content << text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\\n> ") + "\\n\\n"
275 content << text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\\n> ") + "\\n\\n"
258 render(:update) { |page|
276 render(:update) { |page|
259 page.<< "$('notes').value = \"#{content}\";"
277 page.<< "$('notes').value = \"#{content}\";"
260 page.show 'update'
278 page.show 'update'
261 page << "Form.Element.focus('notes');"
279 page << "Form.Element.focus('notes');"
262 page << "Element.scrollTo('update');"
280 page << "Element.scrollTo('update');"
263 page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
281 page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
264 }
282 }
265 end
283 end
266
284
267 # Bulk edit a set of issues
285 # Bulk edit a set of issues
268 def bulk_edit
286 def bulk_edit
269 if request.post?
287 if request.post?
270 attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
288 attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
271 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
289 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
272 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
290 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
273
291
274 unsaved_issue_ids = []
292 unsaved_issue_ids = []
275 @issues.each do |issue|
293 @issues.each do |issue|
276 journal = issue.init_journal(User.current, params[:notes])
294 journal = issue.init_journal(User.current, params[:notes])
277 issue.safe_attributes = attributes
295 issue.safe_attributes = attributes
278 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
296 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
279 unless issue.save
297 unless issue.save
280 # Keep unsaved issue ids to display them in flash error
298 # Keep unsaved issue ids to display them in flash error
281 unsaved_issue_ids << issue.id
299 unsaved_issue_ids << issue.id
282 end
300 end
283 end
301 end
284 if unsaved_issue_ids.empty?
302 if unsaved_issue_ids.empty?
285 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
303 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
286 else
304 else
287 flash[:error] = l(:notice_failed_to_save_issues, :count => unsaved_issue_ids.size,
305 flash[:error] = l(:notice_failed_to_save_issues, :count => unsaved_issue_ids.size,
288 :total => @issues.size,
306 :total => @issues.size,
289 :ids => '#' + unsaved_issue_ids.join(', #'))
307 :ids => '#' + unsaved_issue_ids.join(', #'))
290 end
308 end
291 redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
309 redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
292 return
310 return
293 end
311 end
294 @available_statuses = Workflow.available_statuses(@project)
312 @available_statuses = Workflow.available_statuses(@project)
295 @custom_fields = @project.all_issue_custom_fields
313 @custom_fields = @project.all_issue_custom_fields
296 end
314 end
297
315
298 def move
316 def move
299 @copy = params[:copy_options] && params[:copy_options][:copy]
317 @copy = params[:copy_options] && params[:copy_options][:copy]
300 @allowed_projects = []
318 @allowed_projects = []
301 # find projects to which the user is allowed to move the issue
319 # find projects to which the user is allowed to move the issue
302 if User.current.admin?
320 if User.current.admin?
303 # admin is allowed to move issues to any active (visible) project
321 # admin is allowed to move issues to any active (visible) project
304 @allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current))
322 @allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current))
305 else
323 else
306 User.current.memberships.each {|m| @allowed_projects << m.project if m.roles.detect {|r| r.allowed_to?(:move_issues)}}
324 User.current.memberships.each {|m| @allowed_projects << m.project if m.roles.detect {|r| r.allowed_to?(:move_issues)}}
307 end
325 end
308 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
326 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
309 @target_project ||= @project
327 @target_project ||= @project
310 @trackers = @target_project.trackers
328 @trackers = @target_project.trackers
311 @available_statuses = Workflow.available_statuses(@project)
329 @available_statuses = Workflow.available_statuses(@project)
312 if request.post?
330 if request.post?
313 new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
331 new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
314 unsaved_issue_ids = []
332 unsaved_issue_ids = []
315 moved_issues = []
333 moved_issues = []
316 @issues.each do |issue|
334 @issues.each do |issue|
317 changed_attributes = {}
335 changed_attributes = {}
318 [:assigned_to_id, :status_id, :start_date, :due_date].each do |valid_attribute|
336 [:assigned_to_id, :status_id, :start_date, :due_date].each do |valid_attribute|
319 unless params[valid_attribute].blank?
337 unless params[valid_attribute].blank?
320 changed_attributes[valid_attribute] = (params[valid_attribute] == 'none' ? nil : params[valid_attribute])
338 changed_attributes[valid_attribute] = (params[valid_attribute] == 'none' ? nil : params[valid_attribute])
321 end
339 end
322 end
340 end
323 issue.init_journal(User.current)
341 issue.init_journal(User.current)
324 call_hook(:controller_issues_move_before_save, { :params => params, :issue => issue, :target_project => @target_project, :copy => !!@copy })
342 call_hook(:controller_issues_move_before_save, { :params => params, :issue => issue, :target_project => @target_project, :copy => !!@copy })
325 if r = issue.move_to(@target_project, new_tracker, {:copy => @copy, :attributes => changed_attributes})
343 if r = issue.move_to(@target_project, new_tracker, {:copy => @copy, :attributes => changed_attributes})
326 moved_issues << r
344 moved_issues << r
327 else
345 else
328 unsaved_issue_ids << issue.id
346 unsaved_issue_ids << issue.id
329 end
347 end
330 end
348 end
331 if unsaved_issue_ids.empty?
349 if unsaved_issue_ids.empty?
332 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
350 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
333 else
351 else
334 flash[:error] = l(:notice_failed_to_save_issues, :count => unsaved_issue_ids.size,
352 flash[:error] = l(:notice_failed_to_save_issues, :count => unsaved_issue_ids.size,
335 :total => @issues.size,
353 :total => @issues.size,
336 :ids => '#' + unsaved_issue_ids.join(', #'))
354 :ids => '#' + unsaved_issue_ids.join(', #'))
337 end
355 end
338 if params[:follow]
356 if params[:follow]
339 if @issues.size == 1 && moved_issues.size == 1
357 if @issues.size == 1 && moved_issues.size == 1
340 redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first
358 redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first
341 else
359 else
342 redirect_to :controller => 'issues', :action => 'index', :project_id => (@target_project || @project)
360 redirect_to :controller => 'issues', :action => 'index', :project_id => (@target_project || @project)
343 end
361 end
344 else
362 else
345 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
363 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
346 end
364 end
347 return
365 return
348 end
366 end
349 render :layout => false if request.xhr?
367 render :layout => false if request.xhr?
350 end
368 end
351
369
352 def destroy
370 def destroy
353 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
371 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
354 if @hours > 0
372 if @hours > 0
355 case params[:todo]
373 case params[:todo]
356 when 'destroy'
374 when 'destroy'
357 # nothing to do
375 # nothing to do
358 when 'nullify'
376 when 'nullify'
359 TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
377 TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
360 when 'reassign'
378 when 'reassign'
361 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
379 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
362 if reassign_to.nil?
380 if reassign_to.nil?
363 flash.now[:error] = l(:error_issue_not_found_in_project)
381 flash.now[:error] = l(:error_issue_not_found_in_project)
364 return
382 return
365 else
383 else
366 TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
384 TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
367 end
385 end
368 else
386 else
369 unless params[:format] == 'xml'
387 unless params[:format] == 'xml'
370 # display the destroy form if it's a user request
388 # display the destroy form if it's a user request
371 return
389 return
372 end
390 end
373 end
391 end
374 end
392 end
375 @issues.each(&:destroy)
393 @issues.each(&:destroy)
376 respond_to do |format|
394 respond_to do |format|
377 format.html { redirect_to :action => 'index', :project_id => @project }
395 format.html { redirect_to :action => 'index', :project_id => @project }
378 format.xml { head :ok }
396 format.xml { head :ok }
379 end
397 end
380 end
398 end
381
399
382 def gantt
400 def gantt
383 @gantt = Redmine::Helpers::Gantt.new(params)
401 @gantt = Redmine::Helpers::Gantt.new(params)
384 retrieve_query
402 retrieve_query
385 @query.group_by = nil
403 @query.group_by = nil
386 if @query.valid?
404 if @query.valid?
387 events = []
405 events = []
388 # Issues that have start and due dates
406 # Issues that have start and due dates
389 events += @query.issues(:include => [:tracker, :assigned_to, :priority],
407 events += @query.issues(:include => [:tracker, :assigned_to, :priority],
390 :order => "start_date, due_date",
408 :order => "start_date, due_date",
391 :conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
409 :conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
392 )
410 )
393 # Issues that don't have a due date but that are assigned to a version with a date
411 # Issues that don't have a due date but that are assigned to a version with a date
394 events += @query.issues(:include => [:tracker, :assigned_to, :priority, :fixed_version],
412 events += @query.issues(:include => [:tracker, :assigned_to, :priority, :fixed_version],
395 :order => "start_date, effective_date",
413 :order => "start_date, effective_date",
396 :conditions => ["(((start_date>=? and start_date<=?) or (effective_date>=? and effective_date<=?) or (start_date<? and effective_date>?)) and start_date is not null and due_date is null and effective_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
414 :conditions => ["(((start_date>=? and start_date<=?) or (effective_date>=? and effective_date<=?) or (start_date<? and effective_date>?)) and start_date is not null and due_date is null and effective_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
397 )
415 )
398 # Versions
416 # Versions
399 events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @gantt.date_from, @gantt.date_to])
417 events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @gantt.date_from, @gantt.date_to])
400
418
401 @gantt.events = events
419 @gantt.events = events
402 end
420 end
403
421
404 basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
422 basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
405
423
406 respond_to do |format|
424 respond_to do |format|
407 format.html { render :template => "issues/gantt.rhtml", :layout => !request.xhr? }
425 format.html { render :template => "issues/gantt.rhtml", :layout => !request.xhr? }
408 format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image')
426 format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image')
409 format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{basename}.pdf") }
427 format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{basename}.pdf") }
410 end
428 end
411 end
429 end
412
430
413 def calendar
431 def calendar
414 if params[:year] and params[:year].to_i > 1900
432 if params[:year] and params[:year].to_i > 1900
415 @year = params[:year].to_i
433 @year = params[:year].to_i
416 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
434 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
417 @month = params[:month].to_i
435 @month = params[:month].to_i
418 end
436 end
419 end
437 end
420 @year ||= Date.today.year
438 @year ||= Date.today.year
421 @month ||= Date.today.month
439 @month ||= Date.today.month
422
440
423 @calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
441 @calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
424 retrieve_query
442 retrieve_query
425 @query.group_by = nil
443 @query.group_by = nil
426 if @query.valid?
444 if @query.valid?
427 events = []
445 events = []
428 events += @query.issues(:include => [:tracker, :assigned_to, :priority],
446 events += @query.issues(:include => [:tracker, :assigned_to, :priority],
429 :conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?))", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
447 :conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?))", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
430 )
448 )
431 events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
449 events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
432
450
433 @calendar.events = events
451 @calendar.events = events
434 end
452 end
435
453
436 render :layout => false if request.xhr?
454 render :layout => false if request.xhr?
437 end
455 end
438
456
439 def context_menu
457 def context_menu
440 @issues = Issue.find_all_by_id(params[:ids], :include => :project)
458 @issues = Issue.find_all_by_id(params[:ids], :include => :project)
441 if (@issues.size == 1)
459 if (@issues.size == 1)
442 @issue = @issues.first
460 @issue = @issues.first
443 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
461 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
444 end
462 end
445 projects = @issues.collect(&:project).compact.uniq
463 projects = @issues.collect(&:project).compact.uniq
446 @project = projects.first if projects.size == 1
464 @project = projects.first if projects.size == 1
447
465
448 @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
466 @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
449 :log_time => (@project && User.current.allowed_to?(:log_time, @project)),
467 :log_time => (@project && User.current.allowed_to?(:log_time, @project)),
450 :update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))),
468 :update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))),
451 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
469 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
452 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
470 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
453 :delete => (@project && User.current.allowed_to?(:delete_issues, @project))
471 :delete => (@project && User.current.allowed_to?(:delete_issues, @project))
454 }
472 }
455 if @project
473 if @project
456 @assignables = @project.assignable_users
474 @assignables = @project.assignable_users
457 @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
475 @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
458 @trackers = @project.trackers
476 @trackers = @project.trackers
459 end
477 end
460
478
461 @priorities = IssuePriority.all.reverse
479 @priorities = IssuePriority.all.reverse
462 @statuses = IssueStatus.find(:all, :order => 'position')
480 @statuses = IssueStatus.find(:all, :order => 'position')
463 @back = params[:back_url] || request.env['HTTP_REFERER']
481 @back = params[:back_url] || request.env['HTTP_REFERER']
464
482
465 render :layout => false
483 render :layout => false
466 end
484 end
467
485
468 def update_form
486 def update_form
469 if params[:id].blank?
487 if params[:id].blank?
470 @issue = Issue.new
488 @issue = Issue.new
471 @issue.project = @project
489 @issue.project = @project
472 else
490 else
473 @issue = @project.issues.visible.find(params[:id])
491 @issue = @project.issues.visible.find(params[:id])
474 end
492 end
475 @issue.attributes = params[:issue]
493 @issue.attributes = params[:issue]
476 @allowed_statuses = ([@issue.status] + @issue.status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq
494 @allowed_statuses = ([@issue.status] + @issue.status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq
477 @priorities = IssuePriority.all
495 @priorities = IssuePriority.all
478
496
479 render :partial => 'attributes'
497 render :partial => 'attributes'
480 end
498 end
481
499
482 def preview
500 def preview
483 @issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank?
501 @issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank?
484 @attachements = @issue.attachments if @issue
502 @attachements = @issue.attachments if @issue
485 @text = params[:notes] || (params[:issue] ? params[:issue][:description] : nil)
503 @text = params[:notes] || (params[:issue] ? params[:issue][:description] : nil)
486 render :partial => 'common/preview'
504 render :partial => 'common/preview'
487 end
505 end
488
506
489 private
507 private
490 def find_issue
508 def find_issue
491 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
509 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
492 @project = @issue.project
510 @project = @issue.project
493 rescue ActiveRecord::RecordNotFound
511 rescue ActiveRecord::RecordNotFound
494 render_404
512 render_404
495 end
513 end
496
514
497 # Filter for bulk operations
515 # Filter for bulk operations
498 def find_issues
516 def find_issues
499 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
517 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
500 raise ActiveRecord::RecordNotFound if @issues.empty?
518 raise ActiveRecord::RecordNotFound if @issues.empty?
501 projects = @issues.collect(&:project).compact.uniq
519 projects = @issues.collect(&:project).compact.uniq
502 if projects.size == 1
520 if projects.size == 1
503 @project = projects.first
521 @project = projects.first
504 else
522 else
505 # TODO: let users bulk edit/move/destroy issues from different projects
523 # TODO: let users bulk edit/move/destroy issues from different projects
506 render_error 'Can not bulk edit/move/destroy issues from different projects'
524 render_error 'Can not bulk edit/move/destroy issues from different projects'
507 return false
525 return false
508 end
526 end
509 rescue ActiveRecord::RecordNotFound
527 rescue ActiveRecord::RecordNotFound
510 render_404
528 render_404
511 end
529 end
512
530
513 def find_project
531 def find_project
514 project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
532 project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
515 @project = Project.find(project_id)
533 @project = Project.find(project_id)
516 rescue ActiveRecord::RecordNotFound
534 rescue ActiveRecord::RecordNotFound
517 render_404
535 render_404
518 end
536 end
519
537
520 def find_optional_project
538 def find_optional_project
521 @project = Project.find(params[:project_id]) unless params[:project_id].blank?
539 @project = Project.find(params[:project_id]) unless params[:project_id].blank?
522 allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
540 allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
523 allowed ? true : deny_access
541 allowed ? true : deny_access
524 rescue ActiveRecord::RecordNotFound
542 rescue ActiveRecord::RecordNotFound
525 render_404
543 render_404
526 end
544 end
527
545
528 # Retrieve query from session or build a new query
546 # Retrieve query from session or build a new query
529 def retrieve_query
547 def retrieve_query
530 if !params[:query_id].blank?
548 if !params[:query_id].blank?
531 cond = "project_id IS NULL"
549 cond = "project_id IS NULL"
532 cond << " OR project_id = #{@project.id}" if @project
550 cond << " OR project_id = #{@project.id}" if @project
533 @query = Query.find(params[:query_id], :conditions => cond)
551 @query = Query.find(params[:query_id], :conditions => cond)
534 @query.project = @project
552 @query.project = @project
535 session[:query] = {:id => @query.id, :project_id => @query.project_id}
553 session[:query] = {:id => @query.id, :project_id => @query.project_id}
536 sort_clear
554 sort_clear
537 else
555 else
538 if api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
556 if api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
539 # Give it a name, required to be valid
557 # Give it a name, required to be valid
540 @query = Query.new(:name => "_")
558 @query = Query.new(:name => "_")
541 @query.project = @project
559 @query.project = @project
542 if params[:fields] and params[:fields].is_a? Array
560 if params[:fields] and params[:fields].is_a? Array
543 params[:fields].each do |field|
561 params[:fields].each do |field|
544 @query.add_filter(field, params[:operators][field], params[:values][field])
562 @query.add_filter(field, params[:operators][field], params[:values][field])
545 end
563 end
546 else
564 else
547 @query.available_filters.keys.each do |field|
565 @query.available_filters.keys.each do |field|
548 @query.add_short_filter(field, params[field]) if params[field]
566 @query.add_short_filter(field, params[field]) if params[field]
549 end
567 end
550 end
568 end
551 @query.group_by = params[:group_by]
569 @query.group_by = params[:group_by]
552 @query.column_names = params[:query] && params[:query][:column_names]
570 @query.column_names = params[:query] && params[:query][:column_names]
553 session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
571 session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
554 else
572 else
555 @query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
573 @query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
556 @query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
574 @query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
557 @query.project = @project
575 @query.project = @project
558 end
576 end
559 end
577 end
560 end
578 end
561
579
562 # Rescues an invalid query statement. Just in case...
580 # Rescues an invalid query statement. Just in case...
563 def query_statement_invalid(exception)
581 def query_statement_invalid(exception)
564 logger.error "Query::StatementInvalid: #{exception.message}" if logger
582 logger.error "Query::StatementInvalid: #{exception.message}" if logger
565 session.delete(:query)
583 session.delete(:query)
566 sort_clear
584 sort_clear
567 render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
585 render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
568 end
586 end
569 end
587 end
@@ -1,287 +1,288
1 ActionController::Routing::Routes.draw do |map|
1 ActionController::Routing::Routes.draw do |map|
2 # Add your own custom routes here.
2 # Add your own custom routes here.
3 # The priority is based upon order of creation: first created -> highest priority.
3 # The priority is based upon order of creation: first created -> highest priority.
4
4
5 # Here's a sample route:
5 # Here's a sample route:
6 # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
6 # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
7 # Keep in mind you can assign values other than :controller and :action
7 # Keep in mind you can assign values other than :controller and :action
8
8
9 map.home '', :controller => 'welcome'
9 map.home '', :controller => 'welcome'
10
10
11 map.signin 'login', :controller => 'account', :action => 'login'
11 map.signin 'login', :controller => 'account', :action => 'login'
12 map.signout 'logout', :controller => 'account', :action => 'logout'
12 map.signout 'logout', :controller => 'account', :action => 'logout'
13
13
14 map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
14 map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
15 map.connect 'help/:ctrl/:page', :controller => 'help'
15 map.connect 'help/:ctrl/:page', :controller => 'help'
16
16
17 map.connect 'time_entries/:id/edit', :action => 'edit', :controller => 'timelog'
17 map.connect 'time_entries/:id/edit', :action => 'edit', :controller => 'timelog'
18 map.connect 'projects/:project_id/time_entries/new', :action => 'edit', :controller => 'timelog'
18 map.connect 'projects/:project_id/time_entries/new', :action => 'edit', :controller => 'timelog'
19 map.connect 'projects/:project_id/issues/:issue_id/time_entries/new', :action => 'edit', :controller => 'timelog'
19 map.connect 'projects/:project_id/issues/:issue_id/time_entries/new', :action => 'edit', :controller => 'timelog'
20
20
21 map.with_options :controller => 'timelog' do |timelog|
21 map.with_options :controller => 'timelog' do |timelog|
22 timelog.connect 'projects/:project_id/time_entries', :action => 'details'
22 timelog.connect 'projects/:project_id/time_entries', :action => 'details'
23
23
24 timelog.with_options :action => 'details', :conditions => {:method => :get} do |time_details|
24 timelog.with_options :action => 'details', :conditions => {:method => :get} do |time_details|
25 time_details.connect 'time_entries'
25 time_details.connect 'time_entries'
26 time_details.connect 'time_entries.:format'
26 time_details.connect 'time_entries.:format'
27 time_details.connect 'issues/:issue_id/time_entries'
27 time_details.connect 'issues/:issue_id/time_entries'
28 time_details.connect 'issues/:issue_id/time_entries.:format'
28 time_details.connect 'issues/:issue_id/time_entries.:format'
29 time_details.connect 'projects/:project_id/time_entries.:format'
29 time_details.connect 'projects/:project_id/time_entries.:format'
30 time_details.connect 'projects/:project_id/issues/:issue_id/time_entries'
30 time_details.connect 'projects/:project_id/issues/:issue_id/time_entries'
31 time_details.connect 'projects/:project_id/issues/:issue_id/time_entries.:format'
31 time_details.connect 'projects/:project_id/issues/:issue_id/time_entries.:format'
32 end
32 end
33 timelog.connect 'projects/:project_id/time_entries/report', :action => 'report'
33 timelog.connect 'projects/:project_id/time_entries/report', :action => 'report'
34 timelog.with_options :action => 'report',:conditions => {:method => :get} do |time_report|
34 timelog.with_options :action => 'report',:conditions => {:method => :get} do |time_report|
35 time_report.connect 'time_entries/report'
35 time_report.connect 'time_entries/report'
36 time_report.connect 'time_entries/report.:format'
36 time_report.connect 'time_entries/report.:format'
37 time_report.connect 'projects/:project_id/time_entries/report.:format'
37 time_report.connect 'projects/:project_id/time_entries/report.:format'
38 end
38 end
39
39
40 timelog.with_options :action => 'edit', :conditions => {:method => :get} do |time_edit|
40 timelog.with_options :action => 'edit', :conditions => {:method => :get} do |time_edit|
41 time_edit.connect 'issues/:issue_id/time_entries/new'
41 time_edit.connect 'issues/:issue_id/time_entries/new'
42 end
42 end
43
43
44 timelog.connect 'time_entries/:id/destroy', :action => 'destroy', :conditions => {:method => :post}
44 timelog.connect 'time_entries/:id/destroy', :action => 'destroy', :conditions => {:method => :post}
45 end
45 end
46
46
47 map.connect 'projects/:id/wiki', :controller => 'wikis', :action => 'edit', :conditions => {:method => :post}
47 map.connect 'projects/:id/wiki', :controller => 'wikis', :action => 'edit', :conditions => {:method => :post}
48 map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :get}
48 map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :get}
49 map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :post}
49 map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :post}
50 map.with_options :controller => 'wiki' do |wiki_routes|
50 map.with_options :controller => 'wiki' do |wiki_routes|
51 wiki_routes.with_options :conditions => {:method => :get} do |wiki_views|
51 wiki_routes.with_options :conditions => {:method => :get} do |wiki_views|
52 wiki_views.connect 'projects/:id/wiki/:page', :action => 'special', :page => /page_index|date_index|export/i
52 wiki_views.connect 'projects/:id/wiki/:page', :action => 'special', :page => /page_index|date_index|export/i
53 wiki_views.connect 'projects/:id/wiki/:page', :action => 'index', :page => nil
53 wiki_views.connect 'projects/:id/wiki/:page', :action => 'index', :page => nil
54 wiki_views.connect 'projects/:id/wiki/:page/edit', :action => 'edit'
54 wiki_views.connect 'projects/:id/wiki/:page/edit', :action => 'edit'
55 wiki_views.connect 'projects/:id/wiki/:page/rename', :action => 'rename'
55 wiki_views.connect 'projects/:id/wiki/:page/rename', :action => 'rename'
56 wiki_views.connect 'projects/:id/wiki/:page/history', :action => 'history'
56 wiki_views.connect 'projects/:id/wiki/:page/history', :action => 'history'
57 wiki_views.connect 'projects/:id/wiki/:page/diff/:version/vs/:version_from', :action => 'diff'
57 wiki_views.connect 'projects/:id/wiki/:page/diff/:version/vs/:version_from', :action => 'diff'
58 wiki_views.connect 'projects/:id/wiki/:page/annotate/:version', :action => 'annotate'
58 wiki_views.connect 'projects/:id/wiki/:page/annotate/:version', :action => 'annotate'
59 end
59 end
60
60
61 wiki_routes.connect 'projects/:id/wiki/:page/:action',
61 wiki_routes.connect 'projects/:id/wiki/:page/:action',
62 :action => /edit|rename|destroy|preview|protect/,
62 :action => /edit|rename|destroy|preview|protect/,
63 :conditions => {:method => :post}
63 :conditions => {:method => :post}
64 end
64 end
65
65
66 map.with_options :controller => 'messages' do |messages_routes|
66 map.with_options :controller => 'messages' do |messages_routes|
67 messages_routes.with_options :conditions => {:method => :get} do |messages_views|
67 messages_routes.with_options :conditions => {:method => :get} do |messages_views|
68 messages_views.connect 'boards/:board_id/topics/new', :action => 'new'
68 messages_views.connect 'boards/:board_id/topics/new', :action => 'new'
69 messages_views.connect 'boards/:board_id/topics/:id', :action => 'show'
69 messages_views.connect 'boards/:board_id/topics/:id', :action => 'show'
70 messages_views.connect 'boards/:board_id/topics/:id/edit', :action => 'edit'
70 messages_views.connect 'boards/:board_id/topics/:id/edit', :action => 'edit'
71 end
71 end
72 messages_routes.with_options :conditions => {:method => :post} do |messages_actions|
72 messages_routes.with_options :conditions => {:method => :post} do |messages_actions|
73 messages_actions.connect 'boards/:board_id/topics/new', :action => 'new'
73 messages_actions.connect 'boards/:board_id/topics/new', :action => 'new'
74 messages_actions.connect 'boards/:board_id/topics/:id/replies', :action => 'reply'
74 messages_actions.connect 'boards/:board_id/topics/:id/replies', :action => 'reply'
75 messages_actions.connect 'boards/:board_id/topics/:id/:action', :action => /edit|destroy/
75 messages_actions.connect 'boards/:board_id/topics/:id/:action', :action => /edit|destroy/
76 end
76 end
77 end
77 end
78
78
79 map.with_options :controller => 'boards' do |board_routes|
79 map.with_options :controller => 'boards' do |board_routes|
80 board_routes.with_options :conditions => {:method => :get} do |board_views|
80 board_routes.with_options :conditions => {:method => :get} do |board_views|
81 board_views.connect 'projects/:project_id/boards', :action => 'index'
81 board_views.connect 'projects/:project_id/boards', :action => 'index'
82 board_views.connect 'projects/:project_id/boards/new', :action => 'new'
82 board_views.connect 'projects/:project_id/boards/new', :action => 'new'
83 board_views.connect 'projects/:project_id/boards/:id', :action => 'show'
83 board_views.connect 'projects/:project_id/boards/:id', :action => 'show'
84 board_views.connect 'projects/:project_id/boards/:id.:format', :action => 'show'
84 board_views.connect 'projects/:project_id/boards/:id.:format', :action => 'show'
85 board_views.connect 'projects/:project_id/boards/:id/edit', :action => 'edit'
85 board_views.connect 'projects/:project_id/boards/:id/edit', :action => 'edit'
86 end
86 end
87 board_routes.with_options :conditions => {:method => :post} do |board_actions|
87 board_routes.with_options :conditions => {:method => :post} do |board_actions|
88 board_actions.connect 'projects/:project_id/boards', :action => 'new'
88 board_actions.connect 'projects/:project_id/boards', :action => 'new'
89 board_actions.connect 'projects/:project_id/boards/:id/:action', :action => /edit|destroy/
89 board_actions.connect 'projects/:project_id/boards/:id/:action', :action => /edit|destroy/
90 end
90 end
91 end
91 end
92
92
93 map.with_options :controller => 'documents' do |document_routes|
93 map.with_options :controller => 'documents' do |document_routes|
94 document_routes.with_options :conditions => {:method => :get} do |document_views|
94 document_routes.with_options :conditions => {:method => :get} do |document_views|
95 document_views.connect 'projects/:project_id/documents', :action => 'index'
95 document_views.connect 'projects/:project_id/documents', :action => 'index'
96 document_views.connect 'projects/:project_id/documents/new', :action => 'new'
96 document_views.connect 'projects/:project_id/documents/new', :action => 'new'
97 document_views.connect 'documents/:id', :action => 'show'
97 document_views.connect 'documents/:id', :action => 'show'
98 document_views.connect 'documents/:id/edit', :action => 'edit'
98 document_views.connect 'documents/:id/edit', :action => 'edit'
99 end
99 end
100 document_routes.with_options :conditions => {:method => :post} do |document_actions|
100 document_routes.with_options :conditions => {:method => :post} do |document_actions|
101 document_actions.connect 'projects/:project_id/documents', :action => 'new'
101 document_actions.connect 'projects/:project_id/documents', :action => 'new'
102 document_actions.connect 'documents/:id/:action', :action => /destroy|edit/
102 document_actions.connect 'documents/:id/:action', :action => /destroy|edit/
103 end
103 end
104 end
104 end
105
105
106 map.with_options :controller => 'issues' do |issues_routes|
106 map.with_options :controller => 'issues' do |issues_routes|
107 issues_routes.with_options :conditions => {:method => :get} do |issues_views|
107 issues_routes.with_options :conditions => {:method => :get} do |issues_views|
108 issues_views.connect 'issues', :action => 'index'
108 issues_views.connect 'issues', :action => 'index'
109 issues_views.connect 'issues.:format', :action => 'index'
109 issues_views.connect 'issues.:format', :action => 'index'
110 issues_views.connect 'projects/:project_id/issues', :action => 'index'
110 issues_views.connect 'projects/:project_id/issues', :action => 'index'
111 issues_views.connect 'projects/:project_id/issues.:format', :action => 'index'
111 issues_views.connect 'projects/:project_id/issues.:format', :action => 'index'
112 issues_views.connect 'projects/:project_id/issues/new', :action => 'new'
112 issues_views.connect 'projects/:project_id/issues/new', :action => 'new'
113 issues_views.connect 'projects/:project_id/issues/gantt', :action => 'gantt'
113 issues_views.connect 'projects/:project_id/issues/gantt', :action => 'gantt'
114 issues_views.connect 'projects/:project_id/issues/calendar', :action => 'calendar'
114 issues_views.connect 'projects/:project_id/issues/calendar', :action => 'calendar'
115 issues_views.connect 'projects/:project_id/issues/:copy_from/copy', :action => 'new'
115 issues_views.connect 'projects/:project_id/issues/:copy_from/copy', :action => 'new'
116 issues_views.connect 'issues/:id', :action => 'show', :id => /\d+/
116 issues_views.connect 'issues/:id', :action => 'show', :id => /\d+/
117 issues_views.connect 'issues/:id.:format', :action => 'show', :id => /\d+/
117 issues_views.connect 'issues/:id.:format', :action => 'show', :id => /\d+/
118 issues_views.connect 'issues/:id/edit', :action => 'edit', :id => /\d+/
118 issues_views.connect 'issues/:id/edit', :action => 'edit', :id => /\d+/
119 issues_views.connect 'issues/:id/move', :action => 'move', :id => /\d+/
119 issues_views.connect 'issues/:id/move', :action => 'move', :id => /\d+/
120 end
120 end
121 issues_routes.with_options :conditions => {:method => :post} do |issues_actions|
121 issues_routes.with_options :conditions => {:method => :post} do |issues_actions|
122 issues_actions.connect 'issues', :action => 'index'
122 issues_actions.connect 'issues', :action => 'index'
123 issues_actions.connect 'projects/:project_id/issues', :action => 'new'
123 issues_actions.connect 'projects/:project_id/issues', :action => 'new'
124 issues_actions.connect 'issues/:id/quoted', :action => 'reply', :id => /\d+/
124 issues_actions.connect 'issues/:id/quoted', :action => 'reply', :id => /\d+/
125 issues_actions.connect 'issues/:id/:action', :action => /edit|move|destroy/, :id => /\d+/
125 issues_actions.connect 'issues/:id/:action', :action => /edit|move|destroy/, :id => /\d+/
126 issues_actions.connect 'issues.:format', :action => 'new', :format => /xml/
126 issues_actions.connect 'issues.:format', :action => 'new', :format => /xml/
127 end
127 end
128 issues_routes.with_options :conditions => {:method => :put} do |issues_actions|
128 issues_routes.with_options :conditions => {:method => :put} do |issues_actions|
129 issues_actions.connect 'issues/:id.:format', :action => 'edit', :id => /\d+/, :format => /xml/
129 issues_actions.connect 'issues/:id/edit', :action => 'update', :id => /\d+/
130 issues_actions.connect 'issues/:id.:format', :action => 'update', :id => /\d+/, :format => /xml/
130 end
131 end
131 issues_routes.with_options :conditions => {:method => :delete} do |issues_actions|
132 issues_routes.with_options :conditions => {:method => :delete} do |issues_actions|
132 issues_actions.connect 'issues/:id.:format', :action => 'destroy', :id => /\d+/, :format => /xml/
133 issues_actions.connect 'issues/:id.:format', :action => 'destroy', :id => /\d+/, :format => /xml/
133 end
134 end
134 issues_routes.connect 'issues/:action'
135 issues_routes.connect 'issues/:action'
135 end
136 end
136
137
137 map.with_options :controller => 'issue_relations', :conditions => {:method => :post} do |relations|
138 map.with_options :controller => 'issue_relations', :conditions => {:method => :post} do |relations|
138 relations.connect 'issues/:issue_id/relations/:id', :action => 'new'
139 relations.connect 'issues/:issue_id/relations/:id', :action => 'new'
139 relations.connect 'issues/:issue_id/relations/:id/destroy', :action => 'destroy'
140 relations.connect 'issues/:issue_id/relations/:id/destroy', :action => 'destroy'
140 end
141 end
141
142
142 map.with_options :controller => 'reports', :conditions => {:method => :get} do |reports|
143 map.with_options :controller => 'reports', :conditions => {:method => :get} do |reports|
143 reports.connect 'projects/:id/issues/report', :action => 'issue_report'
144 reports.connect 'projects/:id/issues/report', :action => 'issue_report'
144 reports.connect 'projects/:id/issues/report/:detail', :action => 'issue_report_details'
145 reports.connect 'projects/:id/issues/report/:detail', :action => 'issue_report_details'
145 end
146 end
146
147
147 map.with_options :controller => 'news' do |news_routes|
148 map.with_options :controller => 'news' do |news_routes|
148 news_routes.with_options :conditions => {:method => :get} do |news_views|
149 news_routes.with_options :conditions => {:method => :get} do |news_views|
149 news_views.connect 'news', :action => 'index'
150 news_views.connect 'news', :action => 'index'
150 news_views.connect 'projects/:project_id/news', :action => 'index'
151 news_views.connect 'projects/:project_id/news', :action => 'index'
151 news_views.connect 'projects/:project_id/news.:format', :action => 'index'
152 news_views.connect 'projects/:project_id/news.:format', :action => 'index'
152 news_views.connect 'news.:format', :action => 'index'
153 news_views.connect 'news.:format', :action => 'index'
153 news_views.connect 'projects/:project_id/news/new', :action => 'new'
154 news_views.connect 'projects/:project_id/news/new', :action => 'new'
154 news_views.connect 'news/:id', :action => 'show'
155 news_views.connect 'news/:id', :action => 'show'
155 news_views.connect 'news/:id/edit', :action => 'edit'
156 news_views.connect 'news/:id/edit', :action => 'edit'
156 end
157 end
157 news_routes.with_options do |news_actions|
158 news_routes.with_options do |news_actions|
158 news_actions.connect 'projects/:project_id/news', :action => 'new'
159 news_actions.connect 'projects/:project_id/news', :action => 'new'
159 news_actions.connect 'news/:id/edit', :action => 'edit'
160 news_actions.connect 'news/:id/edit', :action => 'edit'
160 news_actions.connect 'news/:id/destroy', :action => 'destroy'
161 news_actions.connect 'news/:id/destroy', :action => 'destroy'
161 end
162 end
162 end
163 end
163
164
164 map.connect 'projects/:id/members/new', :controller => 'members', :action => 'new'
165 map.connect 'projects/:id/members/new', :controller => 'members', :action => 'new'
165
166
166 map.with_options :controller => 'users' do |users|
167 map.with_options :controller => 'users' do |users|
167 users.with_options :conditions => {:method => :get} do |user_views|
168 users.with_options :conditions => {:method => :get} do |user_views|
168 user_views.connect 'users', :action => 'index'
169 user_views.connect 'users', :action => 'index'
169 user_views.connect 'users/:id', :action => 'show', :id => /\d+/
170 user_views.connect 'users/:id', :action => 'show', :id => /\d+/
170 user_views.connect 'users/new', :action => 'add'
171 user_views.connect 'users/new', :action => 'add'
171 user_views.connect 'users/:id/edit/:tab', :action => 'edit', :tab => nil
172 user_views.connect 'users/:id/edit/:tab', :action => 'edit', :tab => nil
172 end
173 end
173 users.with_options :conditions => {:method => :post} do |user_actions|
174 users.with_options :conditions => {:method => :post} do |user_actions|
174 user_actions.connect 'users', :action => 'add'
175 user_actions.connect 'users', :action => 'add'
175 user_actions.connect 'users/new', :action => 'add'
176 user_actions.connect 'users/new', :action => 'add'
176 user_actions.connect 'users/:id/edit', :action => 'edit'
177 user_actions.connect 'users/:id/edit', :action => 'edit'
177 user_actions.connect 'users/:id/memberships', :action => 'edit_membership'
178 user_actions.connect 'users/:id/memberships', :action => 'edit_membership'
178 user_actions.connect 'users/:id/memberships/:membership_id', :action => 'edit_membership'
179 user_actions.connect 'users/:id/memberships/:membership_id', :action => 'edit_membership'
179 user_actions.connect 'users/:id/memberships/:membership_id/destroy', :action => 'destroy_membership'
180 user_actions.connect 'users/:id/memberships/:membership_id/destroy', :action => 'destroy_membership'
180 end
181 end
181 end
182 end
182
183
183 map.with_options :controller => 'projects' do |projects|
184 map.with_options :controller => 'projects' do |projects|
184 projects.with_options :conditions => {:method => :get} do |project_views|
185 projects.with_options :conditions => {:method => :get} do |project_views|
185 project_views.connect 'projects', :action => 'index'
186 project_views.connect 'projects', :action => 'index'
186 project_views.connect 'projects.:format', :action => 'index'
187 project_views.connect 'projects.:format', :action => 'index'
187 project_views.connect 'projects/new', :action => 'add'
188 project_views.connect 'projects/new', :action => 'add'
188 project_views.connect 'projects/:id', :action => 'show'
189 project_views.connect 'projects/:id', :action => 'show'
189 project_views.connect 'projects/:id.:format', :action => 'show'
190 project_views.connect 'projects/:id.:format', :action => 'show'
190 project_views.connect 'projects/:id/:action', :action => /roadmap|destroy|settings/
191 project_views.connect 'projects/:id/:action', :action => /roadmap|destroy|settings/
191 project_views.connect 'projects/:id/files', :action => 'list_files'
192 project_views.connect 'projects/:id/files', :action => 'list_files'
192 project_views.connect 'projects/:id/files/new', :action => 'add_file'
193 project_views.connect 'projects/:id/files/new', :action => 'add_file'
193 project_views.connect 'projects/:id/versions/new', :action => 'add_version'
194 project_views.connect 'projects/:id/versions/new', :action => 'add_version'
194 project_views.connect 'projects/:id/categories/new', :action => 'add_issue_category'
195 project_views.connect 'projects/:id/categories/new', :action => 'add_issue_category'
195 project_views.connect 'projects/:id/settings/:tab', :action => 'settings'
196 project_views.connect 'projects/:id/settings/:tab', :action => 'settings'
196 end
197 end
197
198
198 projects.with_options :action => 'activity', :conditions => {:method => :get} do |activity|
199 projects.with_options :action => 'activity', :conditions => {:method => :get} do |activity|
199 activity.connect 'projects/:id/activity'
200 activity.connect 'projects/:id/activity'
200 activity.connect 'projects/:id/activity.:format'
201 activity.connect 'projects/:id/activity.:format'
201 activity.connect 'activity', :id => nil
202 activity.connect 'activity', :id => nil
202 activity.connect 'activity.:format', :id => nil
203 activity.connect 'activity.:format', :id => nil
203 end
204 end
204
205
205 projects.with_options :conditions => {:method => :post} do |project_actions|
206 projects.with_options :conditions => {:method => :post} do |project_actions|
206 project_actions.connect 'projects/new', :action => 'add'
207 project_actions.connect 'projects/new', :action => 'add'
207 project_actions.connect 'projects', :action => 'add'
208 project_actions.connect 'projects', :action => 'add'
208 project_actions.connect 'projects.:format', :action => 'add', :format => /xml/
209 project_actions.connect 'projects.:format', :action => 'add', :format => /xml/
209 project_actions.connect 'projects/:id/:action', :action => /edit|destroy|archive|unarchive/
210 project_actions.connect 'projects/:id/:action', :action => /edit|destroy|archive|unarchive/
210 project_actions.connect 'projects/:id/files/new', :action => 'add_file'
211 project_actions.connect 'projects/:id/files/new', :action => 'add_file'
211 project_actions.connect 'projects/:id/versions/new', :action => 'add_version'
212 project_actions.connect 'projects/:id/versions/new', :action => 'add_version'
212 project_actions.connect 'projects/:id/categories/new', :action => 'add_issue_category'
213 project_actions.connect 'projects/:id/categories/new', :action => 'add_issue_category'
213 project_actions.connect 'projects/:id/activities/save', :action => 'save_activities'
214 project_actions.connect 'projects/:id/activities/save', :action => 'save_activities'
214 end
215 end
215
216
216 projects.with_options :conditions => {:method => :put} do |project_actions|
217 projects.with_options :conditions => {:method => :put} do |project_actions|
217 project_actions.conditions 'projects/:id.:format', :action => 'edit', :format => /xml/
218 project_actions.conditions 'projects/:id.:format', :action => 'edit', :format => /xml/
218 end
219 end
219
220
220 projects.with_options :conditions => {:method => :delete} do |project_actions|
221 projects.with_options :conditions => {:method => :delete} do |project_actions|
221 project_actions.conditions 'projects/:id.:format', :action => 'destroy', :format => /xml/
222 project_actions.conditions 'projects/:id.:format', :action => 'destroy', :format => /xml/
222 project_actions.conditions 'projects/:id/reset_activities', :action => 'reset_activities'
223 project_actions.conditions 'projects/:id/reset_activities', :action => 'reset_activities'
223 end
224 end
224 end
225 end
225
226
226 map.with_options :controller => 'versions' do |versions|
227 map.with_options :controller => 'versions' do |versions|
227 versions.with_options :conditions => {:method => :post} do |version_actions|
228 versions.with_options :conditions => {:method => :post} do |version_actions|
228 version_actions.connect 'projects/:project_id/versions/close_completed', :action => 'close_completed'
229 version_actions.connect 'projects/:project_id/versions/close_completed', :action => 'close_completed'
229 end
230 end
230 end
231 end
231
232
232 map.with_options :controller => 'repositories' do |repositories|
233 map.with_options :controller => 'repositories' do |repositories|
233 repositories.with_options :conditions => {:method => :get} do |repository_views|
234 repositories.with_options :conditions => {:method => :get} do |repository_views|
234 repository_views.connect 'projects/:id/repository', :action => 'show'
235 repository_views.connect 'projects/:id/repository', :action => 'show'
235 repository_views.connect 'projects/:id/repository/edit', :action => 'edit'
236 repository_views.connect 'projects/:id/repository/edit', :action => 'edit'
236 repository_views.connect 'projects/:id/repository/statistics', :action => 'stats'
237 repository_views.connect 'projects/:id/repository/statistics', :action => 'stats'
237 repository_views.connect 'projects/:id/repository/revisions', :action => 'revisions'
238 repository_views.connect 'projects/:id/repository/revisions', :action => 'revisions'
238 repository_views.connect 'projects/:id/repository/revisions.:format', :action => 'revisions'
239 repository_views.connect 'projects/:id/repository/revisions.:format', :action => 'revisions'
239 repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision'
240 repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision'
240 repository_views.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff'
241 repository_views.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff'
241 repository_views.connect 'projects/:id/repository/revisions/:rev/diff.:format', :action => 'diff'
242 repository_views.connect 'projects/:id/repository/revisions/:rev/diff.:format', :action => 'diff'
242 repository_views.connect 'projects/:id/repository/revisions/:rev/raw/*path', :action => 'entry', :format => 'raw', :requirements => { :rev => /[a-z0-9\.\-_]+/ }
243 repository_views.connect 'projects/:id/repository/revisions/:rev/raw/*path', :action => 'entry', :format => 'raw', :requirements => { :rev => /[a-z0-9\.\-_]+/ }
243 repository_views.connect 'projects/:id/repository/revisions/:rev/:action/*path', :requirements => { :rev => /[a-z0-9\.\-_]+/ }
244 repository_views.connect 'projects/:id/repository/revisions/:rev/:action/*path', :requirements => { :rev => /[a-z0-9\.\-_]+/ }
244 repository_views.connect 'projects/:id/repository/raw/*path', :action => 'entry', :format => 'raw'
245 repository_views.connect 'projects/:id/repository/raw/*path', :action => 'entry', :format => 'raw'
245 # TODO: why the following route is required?
246 # TODO: why the following route is required?
246 repository_views.connect 'projects/:id/repository/entry/*path', :action => 'entry'
247 repository_views.connect 'projects/:id/repository/entry/*path', :action => 'entry'
247 repository_views.connect 'projects/:id/repository/:action/*path'
248 repository_views.connect 'projects/:id/repository/:action/*path'
248 end
249 end
249
250
250 repositories.connect 'projects/:id/repository/:action', :conditions => {:method => :post}
251 repositories.connect 'projects/:id/repository/:action', :conditions => {:method => :post}
251 end
252 end
252
253
253 map.connect 'attachments/:id', :controller => 'attachments', :action => 'show', :id => /\d+/
254 map.connect 'attachments/:id', :controller => 'attachments', :action => 'show', :id => /\d+/
254 map.connect 'attachments/:id/:filename', :controller => 'attachments', :action => 'show', :id => /\d+/, :filename => /.*/
255 map.connect 'attachments/:id/:filename', :controller => 'attachments', :action => 'show', :id => /\d+/, :filename => /.*/
255 map.connect 'attachments/download/:id/:filename', :controller => 'attachments', :action => 'download', :id => /\d+/, :filename => /.*/
256 map.connect 'attachments/download/:id/:filename', :controller => 'attachments', :action => 'download', :id => /\d+/, :filename => /.*/
256
257
257 map.resources :groups
258 map.resources :groups
258
259
259 #left old routes at the bottom for backwards compat
260 #left old routes at the bottom for backwards compat
260 map.connect 'projects/:project_id/issues/:action', :controller => 'issues'
261 map.connect 'projects/:project_id/issues/:action', :controller => 'issues'
261 map.connect 'projects/:project_id/documents/:action', :controller => 'documents'
262 map.connect 'projects/:project_id/documents/:action', :controller => 'documents'
262 map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
263 map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
263 map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
264 map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
264 map.connect 'wiki/:id/:page/:action', :page => nil, :controller => 'wiki'
265 map.connect 'wiki/:id/:page/:action', :page => nil, :controller => 'wiki'
265 map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
266 map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
266 map.connect 'projects/:project_id/news/:action', :controller => 'news'
267 map.connect 'projects/:project_id/news/:action', :controller => 'news'
267 map.connect 'projects/:project_id/timelog/:action/:id', :controller => 'timelog', :project_id => /.+/
268 map.connect 'projects/:project_id/timelog/:action/:id', :controller => 'timelog', :project_id => /.+/
268 map.with_options :controller => 'repositories' do |omap|
269 map.with_options :controller => 'repositories' do |omap|
269 omap.repositories_show 'repositories/browse/:id/*path', :action => 'browse'
270 omap.repositories_show 'repositories/browse/:id/*path', :action => 'browse'
270 omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes'
271 omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes'
271 omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff'
272 omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff'
272 omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry'
273 omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry'
273 omap.repositories_entry 'repositories/annotate/:id/*path', :action => 'annotate'
274 omap.repositories_entry 'repositories/annotate/:id/*path', :action => 'annotate'
274 omap.connect 'repositories/revision/:id/:rev', :action => 'revision'
275 omap.connect 'repositories/revision/:id/:rev', :action => 'revision'
275 end
276 end
276
277
277 map.with_options :controller => 'sys' do |sys|
278 map.with_options :controller => 'sys' do |sys|
278 sys.connect 'sys/projects.:format', :action => 'projects', :conditions => {:method => :get}
279 sys.connect 'sys/projects.:format', :action => 'projects', :conditions => {:method => :get}
279 sys.connect 'sys/projects/:id/repository.:format', :action => 'create_project_repository', :conditions => {:method => :post}
280 sys.connect 'sys/projects/:id/repository.:format', :action => 'create_project_repository', :conditions => {:method => :post}
280 end
281 end
281
282
282 # Install the default route as the lowest priority.
283 # Install the default route as the lowest priority.
283 map.connect ':controller/:action/:id'
284 map.connect ':controller/:action/:id'
284 map.connect 'robots.txt', :controller => 'welcome', :action => 'robots'
285 map.connect 'robots.txt', :controller => 'welcome', :action => 'robots'
285 # Used for OpenID
286 # Used for OpenID
286 map.root :controller => 'account', :action => 'login'
287 map.root :controller => 'account', :action => 'login'
287 end
288 end
@@ -1,167 +1,167
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2010 Jean-Philippe Lang
2 # Copyright (C) 2006-2010 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.dirname(__FILE__)}/../test_helper"
18 require "#{File.dirname(__FILE__)}/../test_helper"
19
19
20 class IssuesApiTest < ActionController::IntegrationTest
20 class IssuesApiTest < ActionController::IntegrationTest
21 fixtures :projects,
21 fixtures :projects,
22 :users,
22 :users,
23 :roles,
23 :roles,
24 :members,
24 :members,
25 :member_roles,
25 :member_roles,
26 :issues,
26 :issues,
27 :issue_statuses,
27 :issue_statuses,
28 :versions,
28 :versions,
29 :trackers,
29 :trackers,
30 :projects_trackers,
30 :projects_trackers,
31 :issue_categories,
31 :issue_categories,
32 :enabled_modules,
32 :enabled_modules,
33 :enumerations,
33 :enumerations,
34 :attachments,
34 :attachments,
35 :workflows,
35 :workflows,
36 :custom_fields,
36 :custom_fields,
37 :custom_values,
37 :custom_values,
38 :custom_fields_projects,
38 :custom_fields_projects,
39 :custom_fields_trackers,
39 :custom_fields_trackers,
40 :time_entries,
40 :time_entries,
41 :journals,
41 :journals,
42 :journal_details,
42 :journal_details,
43 :queries
43 :queries
44
44
45 def setup
45 def setup
46 Setting.rest_api_enabled = '1'
46 Setting.rest_api_enabled = '1'
47 end
47 end
48
48
49 def test_index_routing
49 def test_index_routing
50 assert_routing(
50 assert_routing(
51 {:method => :get, :path => '/issues.xml'},
51 {:method => :get, :path => '/issues.xml'},
52 :controller => 'issues', :action => 'index', :format => 'xml'
52 :controller => 'issues', :action => 'index', :format => 'xml'
53 )
53 )
54 end
54 end
55
55
56 def test_index
56 def test_index
57 get '/issues.xml'
57 get '/issues.xml'
58 assert_response :success
58 assert_response :success
59 assert_equal 'application/xml', @response.content_type
59 assert_equal 'application/xml', @response.content_type
60 end
60 end
61
61
62 def test_index_with_filter
62 def test_index_with_filter
63 get '/issues.xml?status_id=5'
63 get '/issues.xml?status_id=5'
64 assert_response :success
64 assert_response :success
65 assert_equal 'application/xml', @response.content_type
65 assert_equal 'application/xml', @response.content_type
66 assert_tag :tag => 'issues',
66 assert_tag :tag => 'issues',
67 :children => { :count => Issue.visible.count(:conditions => {:status_id => 5}),
67 :children => { :count => Issue.visible.count(:conditions => {:status_id => 5}),
68 :only => { :tag => 'issue' } }
68 :only => { :tag => 'issue' } }
69 end
69 end
70
70
71 def test_show_routing
71 def test_show_routing
72 assert_routing(
72 assert_routing(
73 {:method => :get, :path => '/issues/1.xml'},
73 {:method => :get, :path => '/issues/1.xml'},
74 :controller => 'issues', :action => 'show', :id => '1', :format => 'xml'
74 :controller => 'issues', :action => 'show', :id => '1', :format => 'xml'
75 )
75 )
76 end
76 end
77
77
78 def test_show
78 def test_show
79 get '/issues/1.xml'
79 get '/issues/1.xml'
80 assert_response :success
80 assert_response :success
81 assert_equal 'application/xml', @response.content_type
81 assert_equal 'application/xml', @response.content_type
82 end
82 end
83
83
84 def test_create_routing
84 def test_create_routing
85 assert_routing(
85 assert_routing(
86 {:method => :post, :path => '/issues.xml'},
86 {:method => :post, :path => '/issues.xml'},
87 :controller => 'issues', :action => 'new', :format => 'xml'
87 :controller => 'issues', :action => 'new', :format => 'xml'
88 )
88 )
89 end
89 end
90
90
91 def test_create
91 def test_create
92 attributes = {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}
92 attributes = {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}
93 assert_difference 'Issue.count' do
93 assert_difference 'Issue.count' do
94 post '/issues.xml', {:issue => attributes}, :authorization => credentials('jsmith')
94 post '/issues.xml', {:issue => attributes}, :authorization => credentials('jsmith')
95 end
95 end
96 assert_response :created
96 assert_response :created
97 assert_equal 'application/xml', @response.content_type
97 assert_equal 'application/xml', @response.content_type
98 issue = Issue.first(:order => 'id DESC')
98 issue = Issue.first(:order => 'id DESC')
99 attributes.each do |attribute, value|
99 attributes.each do |attribute, value|
100 assert_equal value, issue.send(attribute)
100 assert_equal value, issue.send(attribute)
101 end
101 end
102 end
102 end
103
103
104 def test_create_failure
104 def test_create_failure
105 attributes = {:project_id => 1}
105 attributes = {:project_id => 1}
106 assert_no_difference 'Issue.count' do
106 assert_no_difference 'Issue.count' do
107 post '/issues.xml', {:issue => attributes}, :authorization => credentials('jsmith')
107 post '/issues.xml', {:issue => attributes}, :authorization => credentials('jsmith')
108 end
108 end
109 assert_response :unprocessable_entity
109 assert_response :unprocessable_entity
110 assert_equal 'application/xml', @response.content_type
110 assert_equal 'application/xml', @response.content_type
111 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
111 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
112 end
112 end
113
113
114 def test_update_routing
114 def test_update_routing
115 assert_routing(
115 assert_routing(
116 {:method => :put, :path => '/issues/1.xml'},
116 {:method => :put, :path => '/issues/1.xml'},
117 :controller => 'issues', :action => 'edit', :id => '1', :format => 'xml'
117 :controller => 'issues', :action => 'update', :id => '1', :format => 'xml'
118 )
118 )
119 end
119 end
120
120
121 def test_update
121 def test_update
122 attributes = {:subject => 'API update'}
122 attributes = {:subject => 'API update'}
123 assert_no_difference 'Issue.count' do
123 assert_no_difference 'Issue.count' do
124 assert_difference 'Journal.count' do
124 assert_difference 'Journal.count' do
125 put '/issues/1.xml', {:issue => attributes}, :authorization => credentials('jsmith')
125 put '/issues/1.xml', {:issue => attributes}, :authorization => credentials('jsmith')
126 end
126 end
127 end
127 end
128 assert_response :ok
128 assert_response :ok
129 assert_equal 'application/xml', @response.content_type
129 assert_equal 'application/xml', @response.content_type
130 issue = Issue.find(1)
130 issue = Issue.find(1)
131 attributes.each do |attribute, value|
131 attributes.each do |attribute, value|
132 assert_equal value, issue.send(attribute)
132 assert_equal value, issue.send(attribute)
133 end
133 end
134 end
134 end
135
135
136 def test_update_failure
136 def test_update_failure
137 attributes = {:subject => ''}
137 attributes = {:subject => ''}
138 assert_no_difference 'Issue.count' do
138 assert_no_difference 'Issue.count' do
139 assert_no_difference 'Journal.count' do
139 assert_no_difference 'Journal.count' do
140 put '/issues/1.xml', {:issue => attributes}, :authorization => credentials('jsmith')
140 put '/issues/1.xml', {:issue => attributes}, :authorization => credentials('jsmith')
141 end
141 end
142 end
142 end
143 assert_response :unprocessable_entity
143 assert_response :unprocessable_entity
144 assert_equal 'application/xml', @response.content_type
144 assert_equal 'application/xml', @response.content_type
145 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
145 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
146 end
146 end
147
147
148 def test_destroy_routing
148 def test_destroy_routing
149 assert_routing(
149 assert_routing(
150 {:method => :delete, :path => '/issues/1.xml'},
150 {:method => :delete, :path => '/issues/1.xml'},
151 :controller => 'issues', :action => 'destroy', :id => '1', :format => 'xml'
151 :controller => 'issues', :action => 'destroy', :id => '1', :format => 'xml'
152 )
152 )
153 end
153 end
154
154
155 def test_destroy
155 def test_destroy
156 assert_difference 'Issue.count', -1 do
156 assert_difference 'Issue.count', -1 do
157 delete '/issues/1.xml', {}, :authorization => credentials('jsmith')
157 delete '/issues/1.xml', {}, :authorization => credentials('jsmith')
158 end
158 end
159 assert_response :ok
159 assert_response :ok
160 assert_equal 'application/xml', @response.content_type
160 assert_equal 'application/xml', @response.content_type
161 assert_nil Issue.find_by_id(1)
161 assert_nil Issue.find_by_id(1)
162 end
162 end
163
163
164 def credentials(user, password=nil)
164 def credentials(user, password=nil)
165 ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)
165 ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)
166 end
166 end
167 end
167 end
@@ -1,129 +1,129
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 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.dirname(__FILE__)}/../test_helper"
18 require "#{File.dirname(__FILE__)}/../test_helper"
19
19
20 class IssuesTest < ActionController::IntegrationTest
20 class IssuesTest < ActionController::IntegrationTest
21 fixtures :projects,
21 fixtures :projects,
22 :users,
22 :users,
23 :roles,
23 :roles,
24 :members,
24 :members,
25 :trackers,
25 :trackers,
26 :projects_trackers,
26 :projects_trackers,
27 :enabled_modules,
27 :enabled_modules,
28 :issue_statuses,
28 :issue_statuses,
29 :issues,
29 :issues,
30 :enumerations,
30 :enumerations,
31 :custom_fields,
31 :custom_fields,
32 :custom_values,
32 :custom_values,
33 :custom_fields_trackers
33 :custom_fields_trackers
34
34
35 # create an issue
35 # create an issue
36 def test_add_issue
36 def test_add_issue
37 log_user('jsmith', 'jsmith')
37 log_user('jsmith', 'jsmith')
38 get 'projects/1/issues/new', :tracker_id => '1'
38 get 'projects/1/issues/new', :tracker_id => '1'
39 assert_response :success
39 assert_response :success
40 assert_template 'issues/new'
40 assert_template 'issues/new'
41
41
42 post 'projects/1/issues', :tracker_id => "1",
42 post 'projects/1/issues', :tracker_id => "1",
43 :issue => { :start_date => "2006-12-26",
43 :issue => { :start_date => "2006-12-26",
44 :priority_id => "4",
44 :priority_id => "4",
45 :subject => "new test issue",
45 :subject => "new test issue",
46 :category_id => "",
46 :category_id => "",
47 :description => "new issue",
47 :description => "new issue",
48 :done_ratio => "0",
48 :done_ratio => "0",
49 :due_date => "",
49 :due_date => "",
50 :assigned_to_id => "" },
50 :assigned_to_id => "" },
51 :custom_fields => {'2' => 'Value for field 2'}
51 :custom_fields => {'2' => 'Value for field 2'}
52 # find created issue
52 # find created issue
53 issue = Issue.find_by_subject("new test issue")
53 issue = Issue.find_by_subject("new test issue")
54 assert_kind_of Issue, issue
54 assert_kind_of Issue, issue
55
55
56 # check redirection
56 # check redirection
57 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
57 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
58 follow_redirect!
58 follow_redirect!
59 assert_equal issue, assigns(:issue)
59 assert_equal issue, assigns(:issue)
60
60
61 # check issue attributes
61 # check issue attributes
62 assert_equal 'jsmith', issue.author.login
62 assert_equal 'jsmith', issue.author.login
63 assert_equal 1, issue.project.id
63 assert_equal 1, issue.project.id
64 assert_equal 1, issue.status.id
64 assert_equal 1, issue.status.id
65 end
65 end
66
66
67 # add then remove 2 attachments to an issue
67 # add then remove 2 attachments to an issue
68 def test_issue_attachements
68 def test_issue_attachements
69 log_user('jsmith', 'jsmith')
69 log_user('jsmith', 'jsmith')
70 set_tmp_attachments_directory
70 set_tmp_attachments_directory
71
71
72 post 'issues/1/edit',
72 put 'issues/1/edit',
73 :notes => 'Some notes',
73 :notes => 'Some notes',
74 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'This is an attachment'}}
74 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'This is an attachment'}}
75 assert_redirected_to "issues/1"
75 assert_redirected_to "issues/1"
76
76
77 # make sure attachment was saved
77 # make sure attachment was saved
78 attachment = Issue.find(1).attachments.find_by_filename("testfile.txt")
78 attachment = Issue.find(1).attachments.find_by_filename("testfile.txt")
79 assert_kind_of Attachment, attachment
79 assert_kind_of Attachment, attachment
80 assert_equal Issue.find(1), attachment.container
80 assert_equal Issue.find(1), attachment.container
81 assert_equal 'This is an attachment', attachment.description
81 assert_equal 'This is an attachment', attachment.description
82 # verify the size of the attachment stored in db
82 # verify the size of the attachment stored in db
83 #assert_equal file_data_1.length, attachment.filesize
83 #assert_equal file_data_1.length, attachment.filesize
84 # verify that the attachment was written to disk
84 # verify that the attachment was written to disk
85 assert File.exist?(attachment.diskfile)
85 assert File.exist?(attachment.diskfile)
86
86
87 # remove the attachments
87 # remove the attachments
88 Issue.find(1).attachments.each(&:destroy)
88 Issue.find(1).attachments.each(&:destroy)
89 assert_equal 0, Issue.find(1).attachments.length
89 assert_equal 0, Issue.find(1).attachments.length
90 end
90 end
91
91
92 def test_other_formats_links_on_get_index
92 def test_other_formats_links_on_get_index
93 get '/projects/ecookbook/issues'
93 get '/projects/ecookbook/issues'
94
94
95 %w(Atom PDF CSV).each do |format|
95 %w(Atom PDF CSV).each do |format|
96 assert_tag :a, :content => format,
96 assert_tag :a, :content => format,
97 :attributes => { :href => "/projects/ecookbook/issues.#{format.downcase}",
97 :attributes => { :href => "/projects/ecookbook/issues.#{format.downcase}",
98 :rel => 'nofollow' }
98 :rel => 'nofollow' }
99 end
99 end
100 end
100 end
101
101
102 def test_other_formats_links_on_post_index_without_project_id_in_url
102 def test_other_formats_links_on_post_index_without_project_id_in_url
103 post '/issues', :project_id => 'ecookbook'
103 post '/issues', :project_id => 'ecookbook'
104
104
105 %w(Atom PDF CSV).each do |format|
105 %w(Atom PDF CSV).each do |format|
106 assert_tag :a, :content => format,
106 assert_tag :a, :content => format,
107 :attributes => { :href => "/projects/ecookbook/issues.#{format.downcase}",
107 :attributes => { :href => "/projects/ecookbook/issues.#{format.downcase}",
108 :rel => 'nofollow' }
108 :rel => 'nofollow' }
109 end
109 end
110 end
110 end
111
111
112 def test_pagination_links_on_get_index
112 def test_pagination_links_on_get_index
113 Setting.per_page_options = '2'
113 Setting.per_page_options = '2'
114 get '/projects/ecookbook/issues'
114 get '/projects/ecookbook/issues'
115
115
116 assert_tag :a, :content => '2',
116 assert_tag :a, :content => '2',
117 :attributes => { :href => '/projects/ecookbook/issues?page=2' }
117 :attributes => { :href => '/projects/ecookbook/issues?page=2' }
118
118
119 end
119 end
120
120
121 def test_pagination_links_on_post_index_without_project_id_in_url
121 def test_pagination_links_on_post_index_without_project_id_in_url
122 Setting.per_page_options = '2'
122 Setting.per_page_options = '2'
123 post '/issues', :project_id => 'ecookbook'
123 post '/issues', :project_id => 'ecookbook'
124
124
125 assert_tag :a, :content => '2',
125 assert_tag :a, :content => '2',
126 :attributes => { :href => '/projects/ecookbook/issues?page=2' }
126 :attributes => { :href => '/projects/ecookbook/issues?page=2' }
127
127
128 end
128 end
129 end
129 end
General Comments 0
You need to be logged in to leave comments. Login now