You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

issues_controller.rb 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. # frozen_string_literal: true
  2. # Redmine - project management software
  3. # Copyright (C) 2006- Jean-Philippe Lang
  4. #
  5. # This program is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License
  7. # as published by the Free Software Foundation; either version 2
  8. # of the License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. class IssuesController < ApplicationController
  19. default_search_scope :issues
  20. before_action :find_issue, :only => [:show, :edit, :update, :issue_tab]
  21. before_action :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]
  22. before_action :authorize, :except => [:index, :new, :create]
  23. before_action :find_optional_project, :only => [:index, :new, :create]
  24. before_action :build_new_issue_from_params, :only => [:new, :create]
  25. accept_atom_auth :index, :show
  26. accept_api_auth :index, :show, :create, :update, :destroy
  27. rescue_from Query::StatementInvalid, :with => :query_statement_invalid
  28. rescue_from Query::QueryError, :with => :query_error
  29. helper :journals
  30. helper :projects
  31. helper :custom_fields
  32. helper :issue_relations
  33. helper :watchers
  34. helper :attachments
  35. helper :queries
  36. include QueriesHelper
  37. helper :repositories
  38. helper :timelog
  39. def index
  40. use_session = !request.format.csv?
  41. retrieve_default_query(use_session)
  42. retrieve_query(IssueQuery, use_session)
  43. if @query.valid?
  44. respond_to do |format|
  45. format.html do
  46. @issue_count = @query.issue_count
  47. @issue_pages = Paginator.new @issue_count, per_page_option, params['page']
  48. @issues = @query.issues(:offset => @issue_pages.offset, :limit => @issue_pages.per_page)
  49. render :layout => !request.xhr?
  50. end
  51. format.api do
  52. @offset, @limit = api_offset_and_limit
  53. @query.column_names = %w(author)
  54. @issue_count = @query.issue_count
  55. @issues = @query.issues(:offset => @offset, :limit => @limit)
  56. Issue.load_visible_relations(@issues) if include_in_api_response?('relations')
  57. if User.current.allowed_to?(:view_time_entries, nil, :global => true)
  58. Issue.load_visible_spent_hours(@issues)
  59. Issue.load_visible_total_spent_hours(@issues)
  60. end
  61. end
  62. format.atom do
  63. issues = @query.issues(:limit => Setting.feeds_limit.to_i)
  64. render_feed(issues,
  65. :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}")
  66. end
  67. format.csv do
  68. issues = @query.issues(:limit => Setting.issues_export_limit.to_i)
  69. send_data(query_to_csv(issues, @query, params[:csv]),
  70. :type => 'text/csv; header=present', :filename => "#{filename_for_export(@query, 'issues')}.csv")
  71. end
  72. format.pdf do
  73. @issues = @query.issues(:limit => Setting.issues_export_limit.to_i)
  74. send_file_headers! :type => 'application/pdf', :filename => "#{filename_for_export(@query, 'issues')}.pdf"
  75. end
  76. end
  77. else
  78. respond_to do |format|
  79. format.html {render :layout => !request.xhr?}
  80. format.any(:atom, :csv, :pdf) {head 422}
  81. format.api {render_validation_errors(@query)}
  82. end
  83. end
  84. rescue ActiveRecord::RecordNotFound
  85. render_404
  86. end
  87. def show
  88. if !api_request? || include_in_api_response?('journals')
  89. @journals = @issue.visible_journals_with_index
  90. @journals.reverse! if User.current.wants_comments_in_reverse_order?
  91. end
  92. if !api_request? || include_in_api_response?('relations')
  93. @relations = @issue.relations.select {|r| r.other_issue(@issue)&.visible?}
  94. end
  95. if !api_request? || include_in_api_response?('allowed_statuses')
  96. @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
  97. end
  98. if User.current.allowed_to?(:view_time_entries, @project)
  99. Issue.load_visible_spent_hours([@issue])
  100. Issue.load_visible_total_spent_hours([@issue])
  101. end
  102. respond_to do |format|
  103. format.html do
  104. @priorities = IssuePriority.active
  105. @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
  106. @time_entries = @issue.time_entries.visible.preload(:activity, :user)
  107. @relation = IssueRelation.new
  108. @has_changesets = @issue.changesets.visible.preload(:repository, :user).exists?
  109. retrieve_previous_and_next_issue_ids
  110. render :template => 'issues/show'
  111. end
  112. format.api do
  113. if include_in_api_response?('changesets')
  114. @changesets = @issue.changesets.visible.preload(:repository, :user).to_a
  115. @changesets.reverse! if User.current.wants_comments_in_reverse_order?
  116. end
  117. end
  118. format.atom do
  119. render :template => 'journals/index', :layout => false,
  120. :content_type => 'application/atom+xml'
  121. end
  122. format.pdf do
  123. send_file_headers!(:type => 'application/pdf',
  124. :filename => "#{@project.identifier}-#{@issue.id}.pdf")
  125. end
  126. end
  127. end
  128. def new
  129. respond_to do |format|
  130. format.html {render :action => 'new', :layout => !request.xhr?}
  131. format.js
  132. end
  133. end
  134. def create
  135. unless User.current.allowed_to?(:add_issues, @issue.project, :global => true)
  136. raise ::Unauthorized
  137. end
  138. call_hook(:controller_issues_new_before_save, {:params => params, :issue => @issue})
  139. @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
  140. if @issue.save
  141. call_hook(:controller_issues_new_after_save, {:params => params, :issue => @issue})
  142. respond_to do |format|
  143. format.html do
  144. render_attachment_warning_if_needed(@issue)
  145. flash[:notice] =
  146. l(:notice_issue_successful_create,
  147. :id => view_context.link_to("##{@issue.id}", issue_path(@issue),
  148. :title => @issue.subject))
  149. redirect_after_create
  150. end
  151. format.api do
  152. render :action => 'show', :status => :created,
  153. :location => issue_url(@issue)
  154. end
  155. end
  156. return
  157. else
  158. respond_to do |format|
  159. format.html do
  160. if @issue.project.nil?
  161. render_error :status => 422
  162. else
  163. render :action => 'new'
  164. end
  165. end
  166. format.api {render_validation_errors(@issue)}
  167. end
  168. end
  169. end
  170. def edit
  171. return unless update_issue_from_params
  172. respond_to do |format|
  173. format.html {}
  174. format.js
  175. end
  176. end
  177. def update
  178. return unless update_issue_from_params
  179. attachments = params[:attachments] || params.dig(:issue, :uploads)
  180. if @issue.attachments_addable?
  181. @issue.save_attachments(attachments)
  182. else
  183. attachments = attachments.to_unsafe_hash if attachments.respond_to?(:to_unsafe_hash)
  184. if [Hash, Array].any? { |klass| attachments.is_a?(klass) } && attachments.any?
  185. flash[:warning] = l(:warning_attachments_not_saved, attachments.size)
  186. end
  187. end
  188. saved = false
  189. begin
  190. saved = save_issue_with_child_records
  191. rescue ActiveRecord::StaleObjectError
  192. @issue.detach_saved_attachments
  193. @conflict = true
  194. if params[:last_journal_id]
  195. @conflict_journals = @issue.journals_after(params[:last_journal_id]).to_a
  196. unless User.current.allowed_to?(:view_private_notes, @issue.project)
  197. @conflict_journals.reject!(&:private_notes?)
  198. end
  199. end
  200. end
  201. if saved
  202. render_attachment_warning_if_needed(@issue)
  203. unless @issue.current_journal.new_record? || params[:no_flash]
  204. flash[:notice] = l(:notice_successful_update)
  205. end
  206. respond_to do |format|
  207. format.html do
  208. redirect_back_or_default(
  209. issue_path(@issue, previous_and_next_issue_ids_params)
  210. )
  211. end
  212. format.api {render_api_ok}
  213. end
  214. else
  215. respond_to do |format|
  216. format.html {render :action => 'edit'}
  217. format.api {render_validation_errors(@issue)}
  218. end
  219. end
  220. end
  221. def issue_tab
  222. return render_error :status => 422 unless request.xhr?
  223. tab = params[:name]
  224. case tab
  225. when 'time_entries'
  226. @time_entries = @issue.time_entries.visible.preload(:activity, :user).to_a
  227. render :partial => 'issues/tabs/time_entries', :locals => {:time_entries => @time_entries}
  228. when 'changesets'
  229. @changesets = @issue.changesets.visible.preload(:repository, :user).to_a
  230. @changesets.reverse! if User.current.wants_comments_in_reverse_order?
  231. render :partial => 'issues/tabs/changesets', :locals => {:changesets => @changesets, :project => @project}
  232. end
  233. end
  234. # Bulk edit/copy a set of issues
  235. def bulk_edit
  236. @issues.sort!
  237. @copy = params[:copy].present?
  238. @notes = params[:notes]
  239. if @copy
  240. unless User.current.allowed_to?(:copy_issues, @projects)
  241. raise ::Unauthorized
  242. end
  243. else
  244. unless @issues.all?(&:attributes_editable?)
  245. raise ::Unauthorized
  246. end
  247. end
  248. edited_issues = Issue.where(:id => @issues.map(&:id)).to_a
  249. @values_by_custom_field = {}
  250. edited_issues.each do |issue|
  251. issue.custom_field_values.each do |c|
  252. if c.value_present?
  253. @values_by_custom_field[c.custom_field] ||= []
  254. @values_by_custom_field[c.custom_field] << issue.id
  255. end
  256. end
  257. end
  258. @allowed_projects = Issue.allowed_target_projects
  259. if params[:issue]
  260. @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s}
  261. if @target_project
  262. target_projects = [@target_project]
  263. edited_issues.each {|issue| issue.project = @target_project}
  264. end
  265. end
  266. target_projects ||= @projects
  267. @trackers = target_projects.map {|p| Issue.allowed_target_trackers(p)}.reduce(:&)
  268. if params[:issue]
  269. @target_tracker = @trackers.detect {|t| t.id.to_s == params[:issue][:tracker_id].to_s}
  270. if @target_tracker
  271. edited_issues.each {|issue| issue.tracker = @target_tracker}
  272. end
  273. end
  274. if @copy
  275. # Copied issues will get their default statuses
  276. @available_statuses = []
  277. else
  278. @available_statuses = edited_issues.map(&:new_statuses_allowed_to).reduce(:&)
  279. end
  280. if params[:issue]
  281. @target_status = @available_statuses.detect {|t| t.id.to_s == params[:issue][:status_id].to_s}
  282. if @target_status
  283. edited_issues.each {|issue| issue.status = @target_status}
  284. end
  285. end
  286. edited_issues.each do |issue|
  287. issue.custom_field_values.each do |c|
  288. if c.value_present? && @values_by_custom_field[c.custom_field]
  289. @values_by_custom_field[c.custom_field].delete(issue.id)
  290. end
  291. end
  292. end
  293. @values_by_custom_field.delete_if {|k, v| v.blank?}
  294. @custom_fields =
  295. edited_issues.map{|i| i.editable_custom_fields}.
  296. reduce(:&).select {|field| field.format.bulk_edit_supported}
  297. @assignables = target_projects.map(&:assignable_users).reduce(:&)
  298. @versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
  299. @categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
  300. if @copy
  301. @attachments_present = @issues.detect {|i| i.attachments.any?}.present?
  302. @subtasks_present = @issues.detect {|i| !i.leaf?}.present?
  303. @watchers_present = User.current.allowed_to?(:add_issue_watchers, @projects) &&
  304. Watcher.where(:watchable_type => 'Issue',
  305. :watchable_id => @issues.map(&:id)).exists?
  306. end
  307. @safe_attributes = edited_issues.map(&:safe_attribute_names).reduce(:&)
  308. @issue_params = params[:issue] || {}
  309. @issue_params[:custom_field_values] ||= {}
  310. end
  311. def bulk_update
  312. @issues.sort!
  313. @copy = params[:copy].present?
  314. attributes = parse_params_for_bulk_update(params[:issue])
  315. copy_subtasks = (params[:copy_subtasks] == '1')
  316. copy_attachments = (params[:copy_attachments] == '1')
  317. copy_watchers = (params[:copy_watchers] == '1')
  318. if @copy
  319. unless User.current.allowed_to?(:copy_issues, @projects)
  320. raise ::Unauthorized
  321. end
  322. target_projects = @projects
  323. if attributes['project_id'].present?
  324. target_projects = Project.where(:id => attributes['project_id']).to_a
  325. end
  326. unless User.current.allowed_to?(:add_issues, target_projects)
  327. raise ::Unauthorized
  328. end
  329. unless User.current.allowed_to?(:add_issue_watchers, @projects)
  330. copy_watchers = false
  331. end
  332. else
  333. unless @issues.all?(&:attributes_editable?)
  334. raise ::Unauthorized
  335. end
  336. end
  337. unsaved_issues = []
  338. saved_issues = []
  339. if @copy && copy_subtasks
  340. # Descendant issues will be copied with the parent task
  341. # Don't copy them twice
  342. @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}}
  343. end
  344. @issues.each do |orig_issue|
  345. orig_issue.reload
  346. if @copy
  347. issue = orig_issue.copy(
  348. {},
  349. :attachments => copy_attachments,
  350. :subtasks => copy_subtasks,
  351. :watchers => copy_watchers,
  352. :link => link_copy?(params[:link_copy])
  353. )
  354. else
  355. issue = orig_issue
  356. end
  357. journal = issue.init_journal(User.current, params[:notes])
  358. issue.safe_attributes = attributes
  359. call_hook(:controller_issues_bulk_edit_before_save, {:params => params, :issue => issue})
  360. if issue.save
  361. saved_issues << issue
  362. else
  363. unsaved_issues << orig_issue
  364. end
  365. end
  366. if unsaved_issues.empty?
  367. flash[:notice] = l(:notice_successful_update) unless saved_issues.empty?
  368. if params[:follow]
  369. if @issues.size == 1 && saved_issues.size == 1
  370. redirect_to issue_path(saved_issues.first)
  371. elsif saved_issues.map(&:project).uniq.size == 1
  372. redirect_to project_issues_path(saved_issues.map(&:project).first)
  373. end
  374. else
  375. redirect_back_or_default _project_issues_path(@project)
  376. end
  377. else
  378. @saved_issues = @issues
  379. @unsaved_issues = unsaved_issues
  380. @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a
  381. bulk_edit
  382. render :action => 'bulk_edit'
  383. end
  384. end
  385. def destroy
  386. raise Unauthorized unless @issues.all?(&:deletable?)
  387. # all issues and their descendants are about to be deleted
  388. issues_and_descendants_ids = Issue.self_and_descendants(@issues).pluck(:id)
  389. time_entries = TimeEntry.where(:issue_id => issues_and_descendants_ids)
  390. @hours = time_entries.sum(:hours).to_f
  391. if @hours > 0
  392. case params[:todo]
  393. when 'destroy'
  394. # nothing to do
  395. when 'nullify'
  396. if Setting.timelog_required_fields.include?('issue_id')
  397. flash.now[:error] = l(:field_issue) + " " + ::I18n.t('activerecord.errors.messages.blank')
  398. return
  399. else
  400. time_entries.update_all(:issue_id => nil)
  401. end
  402. when 'reassign'
  403. reassign_to = @project && @project.issues.find_by_id(params[:reassign_to_id])
  404. if reassign_to.nil?
  405. flash.now[:error] = l(:error_issue_not_found_in_project)
  406. return
  407. elsif issues_and_descendants_ids.include?(reassign_to.id)
  408. flash.now[:error] = l(:error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted)
  409. return
  410. else
  411. time_entries.update_all(:issue_id => reassign_to.id, :project_id => reassign_to.project_id)
  412. end
  413. else
  414. # display the destroy form if it's a user request
  415. return unless api_request?
  416. end
  417. end
  418. @issues.each do |issue|
  419. begin
  420. issue.reload.destroy
  421. rescue ::ActiveRecord::RecordNotFound # raised by #reload if issue no longer exists
  422. # nothing to do, issue was already deleted (eg. by a parent)
  423. end
  424. end
  425. respond_to do |format|
  426. format.html do
  427. flash[:notice] = l(:notice_successful_delete)
  428. redirect_back_or_default _project_issues_path(@project)
  429. end
  430. format.api {render_api_ok}
  431. end
  432. end
  433. # Overrides Redmine::MenuManager::MenuController::ClassMethods for
  434. # when the "New issue" tab is enabled
  435. def current_menu_item
  436. if Setting.new_item_menu_tab == '1' && [:new, :create].include?(action_name.to_sym)
  437. :new_issue
  438. else
  439. super
  440. end
  441. end
  442. private
  443. def query_error(exception)
  444. session.delete(:issue_query)
  445. super
  446. end
  447. def retrieve_default_query(use_session)
  448. return if params[:query_id].present?
  449. return if api_request?
  450. return if params[:set_filter]
  451. if params[:without_default].present?
  452. params[:set_filter] = 1
  453. return
  454. end
  455. if !params[:set_filter] && use_session && session[:issue_query]
  456. query_id, project_id = session[:issue_query].values_at(:id, :project_id)
  457. return if IssueQuery.where(id: query_id).exists? && project_id == @project&.id
  458. end
  459. if default_query = IssueQuery.default(project: @project)
  460. params[:query_id] = default_query.id
  461. end
  462. end
  463. def retrieve_previous_and_next_issue_ids
  464. if params[:prev_issue_id].present? || params[:next_issue_id].present?
  465. @prev_issue_id = params[:prev_issue_id].presence.try(:to_i)
  466. @next_issue_id = params[:next_issue_id].presence.try(:to_i)
  467. @issue_position = params[:issue_position].presence.try(:to_i)
  468. @issue_count = params[:issue_count].presence.try(:to_i)
  469. else
  470. retrieve_query_from_session
  471. if @query
  472. @per_page = per_page_option
  473. limit = 500
  474. issue_ids = @query.issue_ids(:limit => (limit + 1))
  475. if (idx = issue_ids.index(@issue.id)) && idx < limit
  476. if issue_ids.size < limit
  477. @issue_position = idx + 1
  478. @issue_count = issue_ids.size
  479. end
  480. @prev_issue_id = issue_ids[idx - 1] if idx > 0
  481. @next_issue_id = issue_ids[idx + 1] if idx < (issue_ids.size - 1)
  482. end
  483. query_params = @query.as_params
  484. if @issue_position
  485. query_params = query_params.merge(:page => (@issue_position / per_page_option) + 1, :per_page => per_page_option)
  486. end
  487. @query_path = _project_issues_path(@query.project, query_params)
  488. end
  489. end
  490. end
  491. def previous_and_next_issue_ids_params
  492. {
  493. :prev_issue_id => params[:prev_issue_id],
  494. :next_issue_id => params[:next_issue_id],
  495. :issue_position => params[:issue_position],
  496. :issue_count => params[:issue_count]
  497. }.reject {|k, v| k.blank?}
  498. end
  499. # Used by #edit and #update to set some common instance variables
  500. # from the params
  501. def update_issue_from_params
  502. @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
  503. if params[:time_entry]
  504. @time_entry.safe_attributes = params[:time_entry]
  505. end
  506. @issue.init_journal(User.current)
  507. issue_attributes = params[:issue]
  508. if issue_attributes && issue_attributes[:assigned_to_id] == 'me'
  509. issue_attributes[:assigned_to_id] = User.current.id
  510. end
  511. if issue_attributes && params[:conflict_resolution]
  512. case params[:conflict_resolution]
  513. when 'overwrite'
  514. issue_attributes = issue_attributes.dup
  515. issue_attributes.delete(:lock_version)
  516. when 'add_notes'
  517. issue_attributes = issue_attributes.slice(:notes, :private_notes)
  518. when 'cancel'
  519. redirect_to issue_path(@issue)
  520. return false
  521. end
  522. end
  523. issue_attributes = replace_none_values_with_blank(issue_attributes)
  524. @issue.safe_attributes = issue_attributes
  525. @priorities = IssuePriority.active
  526. @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
  527. true
  528. end
  529. # Used by #new and #create to build a new issue from the params
  530. # The new issue will be copied from an existing one if copy_from parameter is given
  531. def build_new_issue_from_params
  532. @issue = Issue.new
  533. if params[:copy_from]
  534. begin
  535. @issue.init_journal(User.current)
  536. @copy_from = Issue.visible.find(params[:copy_from])
  537. unless User.current.allowed_to?(:copy_issues, @copy_from.project)
  538. raise ::Unauthorized
  539. end
  540. @link_copy = link_copy?(params[:link_copy]) || request.get?
  541. @copy_attachments = params[:copy_attachments].present? || request.get?
  542. @copy_subtasks = params[:copy_subtasks].present? || request.get?
  543. @copy_watchers = User.current.allowed_to?(:add_issue_watchers, @project)
  544. @issue.copy_from(@copy_from, :attachments => @copy_attachments,
  545. :subtasks => @copy_subtasks, :watchers => @copy_watchers,
  546. :link => @link_copy)
  547. @issue.parent_issue_id = @copy_from.parent_id
  548. rescue ActiveRecord::RecordNotFound
  549. render_404
  550. return
  551. end
  552. end
  553. @issue.project = @project
  554. if request.get?
  555. @issue.project ||= @issue.allowed_target_projects.first
  556. end
  557. @issue.author ||= User.current
  558. @issue.start_date ||= User.current.today if Setting.default_issue_start_date_to_creation_date?
  559. attrs = (params[:issue] || {}).deep_dup
  560. if action_name == 'new' && params[:was_default_status] == attrs[:status_id]
  561. attrs.delete(:status_id)
  562. end
  563. if action_name == 'new' && params[:form_update_triggered_by] == 'issue_project_id'
  564. # Discard submitted version when changing the project on the issue form
  565. # so we can use the default version for the new project
  566. attrs.delete(:fixed_version_id)
  567. end
  568. attrs[:assigned_to_id] = User.current.id if attrs[:assigned_to_id] == 'me'
  569. @issue.safe_attributes = attrs
  570. if @issue.project
  571. @issue.tracker ||= @issue.allowed_target_trackers.first
  572. if @issue.tracker.nil?
  573. if @issue.project.trackers.any?
  574. # None of the project trackers is allowed to the user
  575. render_error :message => l(:error_no_tracker_allowed_for_new_issue_in_project), :status => 403
  576. else
  577. # Project has no trackers
  578. render_error l(:error_no_tracker_in_project)
  579. end
  580. return false
  581. end
  582. if @issue.status.nil?
  583. render_error l(:error_no_default_issue_status)
  584. return false
  585. end
  586. elsif request.get?
  587. render_error :message => l(:error_no_projects_with_tracker_allowed_for_new_issue), :status => 403
  588. return false
  589. end
  590. @priorities = IssuePriority.active
  591. @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
  592. end
  593. # Saves @issue and a time_entry from the parameters
  594. def save_issue_with_child_records
  595. Issue.transaction do
  596. if params[:time_entry] &&
  597. (params[:time_entry][:hours].present? || params[:time_entry][:comments].present?) &&
  598. User.current.allowed_to?(:log_time, @issue.project)
  599. time_entry = @time_entry || TimeEntry.new
  600. time_entry.project = @issue.project
  601. time_entry.issue = @issue
  602. time_entry.author = User.current
  603. time_entry.user = User.current
  604. time_entry.spent_on = User.current.today
  605. time_entry.safe_attributes = params[:time_entry]
  606. @issue.time_entries << time_entry
  607. end
  608. call_hook(
  609. :controller_issues_edit_before_save,
  610. {:params => params, :issue => @issue,
  611. :time_entry => time_entry,
  612. :journal => @issue.current_journal}
  613. )
  614. if @issue.save
  615. call_hook(
  616. :controller_issues_edit_after_save,
  617. {:params => params, :issue => @issue,
  618. :time_entry => time_entry,
  619. :journal => @issue.current_journal}
  620. )
  621. else
  622. raise ActiveRecord::Rollback
  623. end
  624. end
  625. end
  626. # Returns true if the issue copy should be linked
  627. # to the original issue
  628. def link_copy?(param)
  629. case Setting.link_copied_issue
  630. when 'yes'
  631. true
  632. when 'no'
  633. false
  634. when 'ask'
  635. param == '1'
  636. end
  637. end
  638. # Redirects user after a successful issue creation
  639. def redirect_after_create
  640. if params[:continue]
  641. url_params = {}
  642. url_params[:issue] =
  643. {
  644. :tracker_id => @issue.tracker,
  645. :parent_issue_id => @issue.parent_issue_id
  646. }.reject {|k, v| v.nil?}
  647. url_params[:back_url] = params[:back_url].presence
  648. if params[:project_id]
  649. redirect_to new_project_issue_path(@issue.project, url_params)
  650. else
  651. url_params[:issue][:project_id] = @issue.project_id
  652. redirect_to new_issue_path(url_params)
  653. end
  654. elsif params[:follow]
  655. redirect_to issue_path(@issue)
  656. else
  657. redirect_back_or_default issue_path(@issue)
  658. end
  659. end
  660. end