diff options
author | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2016-04-09 07:37:49 +0000 |
---|---|---|
committer | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2016-04-09 07:37:49 +0000 |
commit | 5adc1ddde9b4a353b2d2a499004c2025d6e8847a (patch) | |
tree | 157cb8f2a52a443b0b91beb2d43d70733e0e62cc | |
parent | c8c694840d3c16450371141be609fd6b6e023667 (diff) | |
download | redmine-5adc1ddde9b4a353b2d2a499004c2025d6e8847a.tar.gz redmine-5adc1ddde9b4a353b2d2a499004c2025d6e8847a.zip |
Allow addition of watchers via bulk edit context menu (#5754).
git-svn-id: http://svn.redmine.org/redmine/trunk@15311 e93f8b46-1217-0410-a6f0-8f06a7374b81
-rw-r--r-- | app/controllers/context_menus_controller.rb | 1 | ||||
-rw-r--r-- | app/controllers/watchers_controller.rb | 53 | ||||
-rw-r--r-- | app/views/context_menus/issues.html.erb | 12 | ||||
-rw-r--r-- | app/views/watchers/_new.html.erb | 18 | ||||
-rw-r--r-- | app/views/watchers/create.js.erb | 7 | ||||
-rw-r--r-- | app/views/watchers/destroy.js.erb | 4 | ||||
-rw-r--r-- | app/views/watchers/new.js.erb | 2 | ||||
-rw-r--r-- | config/routes.rb | 2 | ||||
-rw-r--r-- | public/javascripts/context_menu.js | 2 | ||||
-rw-r--r-- | test/functional/watchers_controller_test.rb | 24 |
10 files changed, 90 insertions, 35 deletions
diff --git a/app/controllers/context_menus_controller.rb b/app/controllers/context_menus_controller.rb index 19661f9dc..1e5652d09 100644 --- a/app/controllers/context_menus_controller.rb +++ b/app/controllers/context_menus_controller.rb @@ -32,6 +32,7 @@ class ContextMenusController < ApplicationController @can = {:edit => User.current.allowed_to?(:edit_issues, @projects), :log_time => (@project && User.current.allowed_to?(:log_time, @project)), :copy => User.current.allowed_to?(:copy_issues, @projects) && Issue.allowed_target_projects.any?, + :add_watchers => User.current.allowed_to?(:add_issue_watchers, @projects), :delete => User.current.allowed_to?(:delete_issues, @projects) } if @project diff --git a/app/controllers/watchers_controller.rb b/app/controllers/watchers_controller.rb index 36c8c5c24..37c03cdaa 100644 --- a/app/controllers/watchers_controller.rb +++ b/app/controllers/watchers_controller.rb @@ -42,7 +42,9 @@ class WatchersController < ApplicationController end users = User.active.visible.where(:id => user_ids.flatten.compact.uniq) users.each do |user| - Watcher.create(:watchable => @watched, :user => user) + @watchables.each do |watchable| + Watcher.create(:watchable => watchable, :user => user) + end end respond_to do |format| format.html { redirect_to_referer_or {render :text => 'Watcher added.', :layout => true}} @@ -62,7 +64,10 @@ class WatchersController < ApplicationController end def destroy - @watched.set_watcher(User.find(params[:user_id]), false) + user = User.find(params[:user_id]) + @watchables.each do |watchable| + watchable.set_watcher(user, false) + end respond_to do |format| format.html { redirect_to :back } format.js @@ -81,30 +86,21 @@ class WatchersController < ApplicationController def find_project if params[:object_type] && params[:object_id] - klass = Object.const_get(params[:object_type].camelcase) - return false unless klass.respond_to?('watched_by') - @watched = klass.find(params[:object_id]) - @project = @watched.project + @watchables = find_objets_from_params + @projects = @watchables.map(&:project).uniq + if @projects.size == 1 + @project = @projects.first + end elsif params[:project_id] @project = Project.visible.find_by_param(params[:project_id]) end - rescue - render_404 end def find_watchables - klass = Object.const_get(params[:object_type].camelcase) rescue nil - if klass && klass.respond_to?('watched_by') - @watchables = klass.where(:id => Array.wrap(params[:object_id])).to_a - raise Unauthorized if @watchables.any? {|w| - if w.respond_to?(:visible?) - !w.visible? - elsif w.respond_to?(:project) && w.project - !w.project.visible? - end - } + @watchables = find_objets_from_params + unless @watchables.present? + render_404 end - render_404 unless @watchables.present? end def set_watcher(watchables, user, watching) @@ -125,9 +121,24 @@ class WatchersController < ApplicationController scope = User.all.limit(100) end users = scope.active.visible.sorted.like(params[:q]).to_a - if @watched - users -= @watched.watcher_users + if @watchables + users -= @watchables.map(&:watcher_users).flatten end users end + + def find_objets_from_params + klass = Object.const_get(params[:object_type].camelcase) rescue nil + return unless klass && klass.respond_to?('watched_by') + + objects = klass.where(:id => Array.wrap(params[:object_id])).to_a + raise Unauthorized if objects.any? do |w| + if w.respond_to?(:visible?) + !w.visible? + elsif w.respond_to?(:project) && w.project + !w.project.visible? + end + end + objects + end end diff --git a/app/views/context_menus/issues.html.erb b/app/views/context_menus/issues.html.erb index 48be73b02..df3d73e9b 100644 --- a/app/views/context_menus/issues.html.erb +++ b/app/views/context_menus/issues.html.erb @@ -117,6 +117,18 @@ </li> <% end %> +<% if @can[:add_watchers] %> + <li class="folder"> + <a href="#" class="submenu"><%= l(:label_issue_watchers) %></a> + <ul> + <li><%= context_menu_link l(:button_add), + new_watchers_path(:object_type => 'issue', :object_id => @issue_ids), + :remote => true, + :class => 'icon-add' %></li> + </ul> + </li> +<% end %> + <% if User.current.logged? %> <li><%= watcher_link(@issues, User.current) %></li> <% end %> diff --git a/app/views/watchers/_new.html.erb b/app/views/watchers/_new.html.erb index 6282d59aa..3640c6993 100644 --- a/app/views/watchers/_new.html.erb +++ b/app/views/watchers/_new.html.erb @@ -1,19 +1,23 @@ <h3 class="title"><%= l(:permission_add_issue_watchers) %></h3> -<%= form_tag({:controller => 'watchers', - :action => (watched ? 'create' : 'append'), - :object_type => (watched && watched.class.name.underscore), - :object_id => watched, - :project_id => @project}, +<%= form_tag(watchables.present? ? watchers_path : watchers_append_path, :remote => true, :method => :post, :id => 'new-watcher-form') do %> + <% if watchables.present? %> + <%= hidden_field_tag 'object_type', watchables.first.class.name.underscore %> + <% watchables.each do |watchable| %> + <%= hidden_field_tag 'object_id[]', watchable.id %> + <% end %> + <% end %> + <%= hidden_field_tag 'project_id', @project.id if @project %> + <p><%= label_tag 'user_search', l(:label_user_search) %><%= text_field_tag 'user_search', nil %></p> <%= javascript_tag "observeSearchfield('user_search', 'users_for_watcher', '#{ escape_javascript url_for(:controller => 'watchers', :action => 'autocomplete_for_user', - :object_type => (watched && watched.class.name.underscore), - :object_id => watched, + :object_type => (watchables.present? ? watchables.first.class.name.underscore : nil), + :object_id => (watchables.present? && watchables.size == 1 ? watchables.first.id : nil), :project_id => @project) }')" %> <div id="users_for_watcher"> diff --git a/app/views/watchers/create.js.erb b/app/views/watchers/create.js.erb index ba8e580af..dd9d8502f 100644 --- a/app/views/watchers/create.js.erb +++ b/app/views/watchers/create.js.erb @@ -1,2 +1,5 @@ -$('#ajax-modal').html('<%= escape_javascript(render(:partial => 'watchers/new', :locals => {:watched => @watched, :users => @users})) %>'); -$('#watchers').html('<%= escape_javascript(render(:partial => 'watchers/watchers', :locals => {:watched => @watched})) %>'); +$('#ajax-modal').html('<%= escape_javascript(render(:partial => 'watchers/new', :locals => {:watchables => @watchables, :users => @users})) %>'); + +<% if @watchables.size == 1 %> +$('#watchers').html('<%= escape_javascript(render(:partial => 'watchers/watchers', :locals => {:watched => @watchables.first})) %>'); +<% end %> diff --git a/app/views/watchers/destroy.js.erb b/app/views/watchers/destroy.js.erb index e4d856549..413797dc2 100644 --- a/app/views/watchers/destroy.js.erb +++ b/app/views/watchers/destroy.js.erb @@ -1 +1,3 @@ -$('#watchers').html('<%= escape_javascript(render(:partial => 'watchers/watchers', :locals => {:watched => @watched})) %>'); +<% if @watchables.size == 1 %> +$('#watchers').html('<%= escape_javascript(render(:partial => 'watchers/watchers', :locals => {:watched => @watchables.first})) %>'); +<% end %> diff --git a/app/views/watchers/new.js.erb b/app/views/watchers/new.js.erb index 8ff5dacc9..a4a91a765 100644 --- a/app/views/watchers/new.js.erb +++ b/app/views/watchers/new.js.erb @@ -1,3 +1,3 @@ -$('#ajax-modal').html('<%= escape_javascript(render :partial => 'watchers/new', :locals => {:watched => @watched, :users => @users}) %>'); +$('#ajax-modal').html('<%= escape_javascript(render :partial => 'watchers/new', :locals => {:watchables => @watchables, :users => @users}) %>'); showModal('ajax-modal', '400px'); $('#ajax-modal').addClass('new-watcher'); diff --git a/config/routes.rb b/config/routes.rb index 4533c3cce..e80895207 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -91,7 +91,7 @@ Rails.application.routes.draw do post 'watchers/watch', :to => 'watchers#watch', :as => 'watch' delete 'watchers/watch', :to => 'watchers#unwatch' - get 'watchers/new', :to => 'watchers#new' + get 'watchers/new', :to => 'watchers#new', :as => 'new_watchers' post 'watchers', :to => 'watchers#create' post 'watchers/append', :to => 'watchers#append' delete 'watchers', :to => 'watchers#destroy' diff --git a/public/javascripts/context_menu.js b/public/javascripts/context_menu.js index 12371078e..1b7da8991 100644 --- a/public/javascripts/context_menu.js +++ b/public/javascripts/context_menu.js @@ -67,7 +67,7 @@ function contextMenuClick(event) { // click is outside the rows if (target.is('a') && (target.hasClass('disabled') || target.hasClass('submenu'))) { event.preventDefault(); - } else if (target.is('.toggle-selection')) { + } else if (target.is('.toggle-selection') || target.is('.ui-dialog *') || $('#ajax-modal').is(':visible')) { // nop } else { contextMenuUnselectAll(); diff --git a/test/functional/watchers_controller_test.rb b/test/functional/watchers_controller_test.rb index 3cbaf841a..d6220710b 100644 --- a/test/functional/watchers_controller_test.rb +++ b/test/functional/watchers_controller_test.rb @@ -133,6 +133,13 @@ class WatchersControllerTest < ActionController::TestCase assert_match /ajax-modal/, response.body end + def test_new_with_multiple_objects + @request.session[:user_id] = 2 + xhr :get, :new, :object_type => 'issue', :object_id => ['1', '2'] + assert_response :success + assert_match /ajax-modal/, response.body + end + def test_new_for_new_record_with_project_id @request.session[:user_id] = 2 xhr :get, :new, :project_id => 1 @@ -161,7 +168,7 @@ class WatchersControllerTest < ActionController::TestCase assert Issue.find(2).watched_by?(User.find(4)) end - def test_create_multiple + def test_create_with_mutiple_users @request.session[:user_id] = 2 assert_difference('Watcher.count', 2) do xhr :post, :create, :object_type => 'issue', :object_id => '2', @@ -174,6 +181,21 @@ class WatchersControllerTest < ActionController::TestCase assert Issue.find(2).watched_by?(User.find(7)) end + def test_create_with_mutiple_objects + @request.session[:user_id] = 2 + assert_difference('Watcher.count', 4) do + xhr :post, :create, :object_type => 'issue', :object_id => ['1', '2'], + :watcher => {:user_ids => ['4', '7']} + assert_response :success + assert_match /watchers/, response.body + assert_match /ajax-modal/, response.body + end + assert Issue.find(1).watched_by?(User.find(4)) + assert Issue.find(2).watched_by?(User.find(4)) + assert Issue.find(1).watched_by?(User.find(7)) + assert Issue.find(2).watched_by?(User.find(7)) + end + def test_autocomplete_on_watchable_creation @request.session[:user_id] = 2 xhr :get, :autocomplete_for_user, :q => 'mi', :project_id => 'ecookbook' |