diff --git a/app/controllers/watchers_controller.rb b/app/controllers/watchers_controller.rb index e564d64ec..00d36f90c 100644 --- a/app/controllers/watchers_controller.rb +++ b/app/controllers/watchers_controller.rb @@ -19,6 +19,7 @@ class WatchersController < ApplicationController before_filter :find_project before_filter :require_login, :check_project_privacy, :only => [:watch, :unwatch] before_filter :authorize, :only => [:new, :destroy] + accept_api_auth :create, :destroy def watch if @watched.respond_to?(:visible?) && !@watched.visible?(User.current) @@ -36,15 +37,19 @@ class WatchersController < ApplicationController end def create - if params[:watcher].is_a?(Hash) && request.post? - user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]] - user_ids.each do |user_id| - Watcher.create(:watchable => @watched, :user_id => user_id) - end + user_ids = [] + if params[:watcher].is_a?(Hash) + user_ids << (params[:watcher][:user_ids] || params[:watcher][:user_id]) + else + user_ids << params[:user_id] + end + user_ids.flatten.compact.uniq.each do |user_id| + Watcher.create(:watchable => @watched, :user_id => user_id) end respond_to do |format| format.html { redirect_to_referer_or {render :text => 'Watcher added.', :layout => true}} format.js + format.api { render_api_ok } end end @@ -56,10 +61,11 @@ class WatchersController < ApplicationController end def destroy - @watched.set_watcher(User.find(params[:user_id]), false) if request.post? + @watched.set_watcher(User.find(params[:user_id]), false) respond_to do |format| format.html { redirect_to :back } format.js + format.api { render_api_ok } end end diff --git a/app/views/issues/show.api.rsb b/app/views/issues/show.api.rsb index 441d1442e..7363937f5 100644 --- a/app/views/issues/show.api.rsb +++ b/app/views/issues/show.api.rsb @@ -64,4 +64,10 @@ api.issue do end end end if include_in_api_response?('journals') + + api.array :watchers do + @issue.watcher_users.each do |user| + api.user :id => user.id, :name => user.name + end + end if include_in_api_response?('watchers') && User.current.allowed_to?(:view_issue_watchers, @issue.project) end diff --git a/config/routes.rb b/config/routes.rb index 061bdf7ba..19b08e1d8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -84,6 +84,9 @@ RedmineApp::Application.routes.draw do match 'watchers/watch', :controller=> 'watchers', :action => 'watch', :via => :post match 'watchers/unwatch', :controller=> 'watchers', :action => 'unwatch', :via => :post match 'watchers/autocomplete_for_user', :controller=> 'watchers', :action => 'autocomplete_for_user', :via => :get + # Specific routes for issue watchers API + post 'issues/:object_id/watchers', :to => 'watchers#create', :object_type => 'issue' + delete 'issues/:object_id/watchers/:user_id' => 'watchers#destroy', :object_type => 'issue' resources :projects do member do diff --git a/test/integration/api_test/issues_test.rb b/test/integration/api_test/issues_test.rb index 3917f8618..176c18105 100644 --- a/test/integration/api_test/issues_test.rb +++ b/test/integration/api_test/issues_test.rb @@ -453,6 +453,21 @@ class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base end end + test "GET /issues/:id.xml?include=watchers should include watchers" do + Watcher.create!(:user_id => 3, :watchable => Issue.find(1)) + + get '/issues/1.xml?include=watchers', {}, credentials('jsmith') + + assert_response :ok + assert_equal 'application/xml', response.content_type + assert_select 'issue' do + assert_select 'watchers', Issue.find(1).watchers.count + assert_select 'watchers' do + assert_select 'user[id=3]' + end + end + end + context "POST /issues.xml" do should_allow_api_authentication( :post, @@ -478,6 +493,18 @@ class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base end end + test "POST /issues.xml with watcher_user_ids should create issue with watchers" do + assert_difference('Issue.count') do + post '/issues.xml', + {:issue => {:project_id => 1, :subject => 'Watchers', + :tracker_id => 2, :status_id => 3, :watcher_user_ids => [3, 1]}}, credentials('jsmith') + assert_response :created + end + issue = Issue.order('id desc').first + assert_equal 2, issue.watchers.size + assert_equal [1, 3], issue.watcher_user_ids.sort + end + context "POST /issues.xml with failure" do should "have an errors tag" do assert_no_difference('Issue.count') do @@ -720,6 +747,30 @@ class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base end end + test "POST /issues/:id/watchers.xml should add watcher" do + assert_difference 'Watcher.count' do + post '/issues/1/watchers.xml', {:user_id => 3}, credentials('jsmith') + + assert_response :ok + assert_equal '', response.body + end + watcher = Watcher.order('id desc').first + assert_equal Issue.find(1), watcher.watchable + assert_equal User.find(3), watcher.user + end + + test "DELETE /issues/:id/watchers/:user_id.xml should remove watcher" do + Watcher.create!(:user_id => 3, :watchable => Issue.find(1)) + + assert_difference 'Watcher.count', -1 do + delete '/issues/1/watchers/3.xml', {}, credentials('jsmith') + + assert_response :ok + assert_equal '', response.body + end + assert_equal false, Issue.find(1).watched_by?(User.find(3)) + end + def test_create_issue_with_uploaded_file set_tmp_attachments_directory # upload the file diff --git a/test/integration/routing/watchers_test.rb b/test/integration/routing/watchers_test.rb index 1b8b629b7..3c203f4bc 100644 --- a/test/integration/routing/watchers_test.rb +++ b/test/integration/routing/watchers_test.rb @@ -47,5 +47,15 @@ class RoutingWatchersTest < ActionController::IntegrationTest { :method => 'post', :path => "/watchers/unwatch" }, { :controller => 'watchers', :action => 'unwatch' } ) + assert_routing( + { :method => 'post', :path => "/issues/12/watchers.xml" }, + { :controller => 'watchers', :action => 'create', + :object_type => 'issue', :object_id => '12', :format => 'xml' } + ) + assert_routing( + { :method => 'delete', :path => "/issues/12/watchers/3.xml" }, + { :controller => 'watchers', :action => 'destroy', + :object_type => 'issue', :object_id => '12', :user_id => '3', :format => 'xml'} + ) end end