]> source.dussan.org Git - redmine.git/commitdiff
Implements /issues/new form for creating issues outside a project (#1003).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Sat, 14 Feb 2015 08:03:51 +0000 (08:03 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Sat, 14 Feb 2015 08:03:51 +0000 (08:03 +0000)
git-svn-id: http://svn.redmine.org/redmine/trunk@13999 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/controllers/issues_controller.rb
app/helpers/issues_helper.rb
app/models/issue.rb
app/views/issues/_form.html.erb
app/views/issues/new.html.erb
config/routes.rb
test/functional/issues_controller_test.rb
test/integration/routing/issues_test.rb

index 8c1d73751fbdcf3136278b5b7a7cde22d83f71ee..1cf9092943aebb69490da0a42c0dd56b135fb4b2 100644 (file)
@@ -21,9 +21,8 @@ class IssuesController < ApplicationController
 
   before_filter :find_issue, :only => [:show, :edit, :update]
   before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]
-  before_filter :find_project, :only => [:new, :create]
-  before_filter :authorize, :except => [:index]
-  before_filter :find_optional_project, :only => [:index]
+  before_filter :authorize, :except => [:index, :new, :create]
+  before_filter :find_optional_project, :only => [:index, :new, :create]
   before_filter :build_new_issue_from_params, :only => [:new, :create]
   accept_rss_auth :index, :show
   accept_api_auth :index, :show, :create, :update, :destroy
@@ -154,12 +153,7 @@ class IssuesController < ApplicationController
         format.html {
           render_attachment_warning_if_needed(@issue)
           flash[:notice] = l(:notice_issue_successful_create, :id => view_context.link_to("##{@issue.id}", issue_path(@issue), :title => @issue.subject))
-          if params[:continue]
-            attrs = {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?}
-            redirect_to new_project_issue_path(@issue.project, :issue => attrs)
-          else
-            redirect_to issue_path(@issue)
-          end
+          redirect_after_create
         }
         format.api  { render :action => 'show', :status => :created, :location => issue_url(@issue) }
       end
@@ -359,13 +353,6 @@ class IssuesController < ApplicationController
 
   private
 
-  def find_project
-    project_id = params[:project_id] || (params[:issue] && params[:issue][:project_id])
-    @project = Project.find(project_id)
-  rescue ActiveRecord::RecordNotFound
-    render_404
-  end
-
   def retrieve_previous_and_next_issue_ids
     retrieve_query_from_session
     if @query
@@ -434,6 +421,9 @@ class IssuesController < ApplicationController
       end
     end
     @issue.project = @project
+    if request.get?
+      @issue.project ||= @issue.allowed_target_projects.first
+    end
     @issue.author ||= User.current
     @issue.start_date ||= Date.today if Setting.default_issue_start_date_to_creation_date?
 
@@ -443,14 +433,16 @@ class IssuesController < ApplicationController
       end
       @issue.safe_attributes = attrs
     end
-    @issue.tracker ||= @project.trackers.first
-    if @issue.tracker.nil?
-      render_error l(:error_no_tracker_in_project)
-      return false
-    end
-    if @issue.status.nil?
-      render_error l(:error_no_default_issue_status)
-      return false
+    if @issue.project
+      @issue.tracker ||= @issue.project.trackers.first
+      if @issue.tracker.nil?
+        render_error l(:error_no_tracker_in_project)
+        return false
+      end
+      if @issue.status.nil?
+        render_error l(:error_no_default_issue_status)
+        return false
+      end
     end
 
     @priorities = IssuePriority.active
@@ -505,4 +497,19 @@ class IssuesController < ApplicationController
       param == '1'
     end
   end
+
+  # Redirects user after a successful issue creation
+  def redirect_after_create
+    if params[:continue]
+      attrs = {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?}
+      if params[:project_id]
+        redirect_to new_project_issue_path(@issue.project, :issue => attrs)
+      else
+        attrs.merge! :project_id => @issue.project_id
+        redirect_to new_issue_path(:issue => attrs)
+      end
+    else
+      redirect_to issue_path(@issue)
+    end
+  end
 end
index e34039d764cae6e2a14d3b5b96d2cfd1d2d2c4cc..a88adceb9c45a4a1c659def567e545ce2da6205a 100644 (file)
@@ -202,10 +202,15 @@ module IssuesHelper
   # Returns the path for updating the issue form
   # with project as the current project
   def update_issue_form_path(project, issue)
+    options = {:format => 'js'}
     if issue.new_record?
-      new_project_issue_path(project, :format => 'js')
+      if project
+        new_project_issue_path(project, options)
+      else
+        new_issue_path(options)
+      end
     else
-      edit_issue_path(issue, :format => 'js')
+      edit_issue_path(issue, options)
     end
   end
 
index 1e1fb9afcbd7dbaec3bf04a932955b7a4939ac66..5a49b0d7bae169b214fe92353dec6f9ec5d14fd6 100644 (file)
@@ -380,15 +380,7 @@ class Issue < ActiveRecord::Base
   end
 
   safe_attributes 'project_id',
-    :if => lambda {|issue, user|
-      if issue.new_record?
-        issue.copy?
-      else
-        user.allowed_to?(:edit_issues, issue.project)
-      end
-    }
-
-  safe_attributes 'tracker_id',
+    'tracker_id',
     'status_id',
     'category_id',
     'assigned_to_id',
@@ -429,7 +421,8 @@ class Issue < ActiveRecord::Base
     names = super
     names -= disabled_core_fields
     names -= read_only_attribute_names(user)
-    if new_record? && copy?
+    if new_record?
+       # Make sure that project_id can always be set for new issues
       names |= %w(project_id)
     end
     names
index 44578610c36e1f36a54878d1fec1490dc52ba45d..c071cb0e169e1c006e435a25bff168a8e19075bb 100644 (file)
@@ -7,7 +7,7 @@
 </p>
 <% end %>
 
-<% if @issue.safe_attribute? 'project_id' %>
+<% if @issue.safe_attribute?('project_id') && (!@issue.new_record? || @project.nil? || @issue.copy?) %>
 <p><%= f.select :project_id, project_tree_options_for_select(@issue.allowed_target_projects, :selected => @issue.project), {:required => true},
                 :onchange => "updateIssueFrom('#{escape_javascript update_issue_form_path(@project, @issue)}')" %></p>
 <% end %>
index f168c39a46b8b63986e6ce5ce8dda69b2ea5ea40..c93a1fc1fc425ebc3b28a3a8ceefc5f5f46973a1 100644 (file)
@@ -2,7 +2,7 @@
 
 <%= call_hook(:view_issues_new_top, {:issue => @issue}) %>
 
-<%= labelled_form_for @issue, :url => project_issues_path(@project),
+<%= labelled_form_for @issue, :url => _project_issues_path(@project),
                              :html => {:id => 'issue-form', :multipart => true} do |f| %>
   <%= error_messages_for 'issue' %>
   <%= hidden_field_tag 'copy_from', params[:copy_from] if params[:copy_from] %>
@@ -49,7 +49,7 @@
 
   <%= submit_tag l(:button_create) %>
   <%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
-  <%= preview_link preview_new_issue_path(:project_id => @project), 'issue-form' %>
+  <%= preview_link preview_new_issue_path(:project_id => @issue.project), 'issue-form' %>
 <% end %>
 
 <div id="preview" class="wiki"></div>
index a32ec76efa266b2cdc7127dc589679ad027e8ec0..142fa915ff6bb99cb9ddb1926829cdc16b223417 100644 (file)
@@ -185,6 +185,8 @@ Rails.application.routes.draw do
       resources :relations, :controller => 'issue_relations', :only => [:index, :show, :create, :destroy]
     end
   end
+  # Used when updating the form of a new issue outside a project
+  post '/issues/new', :to => 'issues#new'
   match '/issues', :controller => 'issues', :action => 'destroy', :via => :delete
 
   resources :queries, :except => [:show]
index 5e8762e0850d9c3fdec888ce9bf1058391579db9..3d048edbc8d86089ff490cd6609feaa97e39b9aa 100644 (file)
@@ -1497,6 +1497,7 @@ class IssuesControllerTest < ActionController::TestCase
     assert_response :success
     assert_template 'new'
 
+    assert_select 'form#issue-form[action=?]', '/projects/ecookbook/issues'
     assert_select 'form#issue-form' do
       assert_select 'input[name=?]', 'issue[is_private]'
       assert_select 'select[name=?]', 'issue[project_id]', 0
@@ -1552,6 +1553,21 @@ class IssuesControllerTest < ActionController::TestCase
     end
   end
 
+  def test_new_without_project_id
+    @request.session[:user_id] = 2
+    get :new
+    assert_response :success
+    assert_template 'new'
+
+    assert_select 'form#issue-form[action=?]', '/issues'
+    assert_select 'form#issue-form' do
+      assert_select 'select[name=?]', 'issue[project_id]'
+    end
+
+    assert_nil assigns(:project)
+    assert_not_nil assigns(:issue)
+  end
+
   def test_new_should_select_default_status
     @request.session[:user_id] = 2
 
@@ -2164,6 +2180,59 @@ class IssuesControllerTest < ActionController::TestCase
     assert issue.is_private?
   end
 
+  def test_create_without_project_id
+    @request.session[:user_id] = 2
+
+    assert_difference 'Issue.count' do
+      post :create,
+           :issue => {:project_id => 3,
+                      :tracker_id => 2,
+                      :subject => 'Foo'}
+      assert_response 302
+    end
+    issue = Issue.order('id DESC').first
+    assert_equal 3, issue.project_id
+    assert_equal 2, issue.tracker_id
+  end
+
+  def test_create_without_project_id_and_continue_should_redirect_without_project_id
+    @request.session[:user_id] = 2
+
+    assert_difference 'Issue.count' do
+      post :create,
+           :issue => {:project_id => 3,
+                      :tracker_id => 2,
+                      :subject => 'Foo'},
+           :continue => '1'
+      assert_redirected_to '/issues/new?issue%5Bproject_id%5D=3&issue%5Btracker_id%5D=2'
+    end
+  end
+
+  def test_create_without_project_id_should_be_denied_without_permission
+    Role.non_member.remove_permission! :add_issues
+    Role.anonymous.remove_permission! :add_issues
+    @request.session[:user_id] = 2
+
+    assert_no_difference 'Issue.count' do
+      post :create,
+           :issue => {:project_id => 3,
+                      :tracker_id => 2,
+                      :subject => 'Foo'}
+      assert_response 403
+    end
+  end
+
+  def test_create_without_project_id_with_failure
+    @request.session[:user_id] = 2
+
+    post :create,
+         :issue => {:project_id => 3,
+                    :tracker_id => 2,
+                    :subject => ''}
+    assert_response :success
+    assert_nil assigns(:project)
+  end
+
   def test_post_create_should_send_a_notification
     ActionMailer::Base.deliveries.clear
     @request.session[:user_id] = 2
index 1b59f3214e5eac11d1908b401480e2bd737d0a02..c7d7523a79a095bd1d2b658ffdcf0868745916de 100644 (file)
@@ -27,6 +27,9 @@ class RoutingIssuesTest < Redmine::RoutingTest
     should_route 'GET /issues/64.pdf' => 'issues#show', :id => '64', :format => 'pdf'
     should_route 'GET /issues/64.atom' => 'issues#show', :id => '64', :format => 'atom'
 
+    should_route 'GET /issues/new' => 'issues#new'
+    should_route 'POST /issues' => 'issues#create'
+
     should_route 'GET /issues/64/edit' => 'issues#edit', :id => '64'
     should_route 'PUT /issues/64' => 'issues#update', :id => '64'
     should_route 'DELETE /issues/64' => 'issues#destroy', :id => '64'
@@ -50,6 +53,7 @@ class RoutingIssuesTest < Redmine::RoutingTest
   end
 
   def test_issues_form_update
+    should_route 'POST /issues/new' => 'issues#new'
     should_route 'POST /projects/23/issues/new' => 'issues#new', :project_id => '23'
     should_route 'PATCH /issues/23/edit' => 'issues#edit', :id => '23'
   end