]> source.dussan.org Git - redmine.git/commitdiff
Allow addition of watchers via bulk edit context menu (#5754).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Sat, 9 Apr 2016 07:37:49 +0000 (07:37 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Sat, 9 Apr 2016 07:37:49 +0000 (07:37 +0000)
git-svn-id: http://svn.redmine.org/redmine/trunk@15311 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/controllers/context_menus_controller.rb
app/controllers/watchers_controller.rb
app/views/context_menus/issues.html.erb
app/views/watchers/_new.html.erb
app/views/watchers/create.js.erb
app/views/watchers/destroy.js.erb
app/views/watchers/new.js.erb
config/routes.rb
public/javascripts/context_menu.js
test/functional/watchers_controller_test.rb

index 19661f9dc6839d1362829c0fe16715660118926b..1e5652d09a770e8319244b2dd1a510ee63a0e3e2 100644 (file)
@@ -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
index 36c8c5c24e5133e2dd2cc4c540f70ed032106f89..37c03cdaa6cf9326a80a074ab377a1bd6ae3b619 100644 (file)
@@ -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
index 48be73b021870e747010ec724d2f21c98093f98a..df3d73e9b2035be91f04c97d4bcddbd8b757af97 100644 (file)
     </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 %>
index 6282d59aa0da7b122c10cc484165ebcfd29e1888..3640c69931fa58070aa105bbe8389239fc58c67a 100644 (file)
@@ -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">
index ba8e580af470d1b148e30727207f6cc3a10697c4..dd9d8502f9bcba0144117954d4d9180a0fb0189a 100644 (file)
@@ -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 %>
index e4d856549a9ba65acbd610e9f22b68b7ea51b1f1..413797dc298d30765da11845c4e8a54b6cb782cb 100644 (file)
@@ -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 %>
index 8ff5dacc98253250e3b6eb6b7f32bbac53fd1c6f..a4a91a765c4984a6d83667474d1ce6bd3317b21a 100644 (file)
@@ -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');
index 4533c3cce486a19cd172d0fcdc216eeec155a914..e808952079467485886c68ec0e789974b913c1ca 100644 (file)
@@ -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'
index 12371078e299d6fe8ec847b84d44e367d8d0f053..1b7da899154840478281eb4b2b1bd7feb25db987 100644 (file)
@@ -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();
index 3cbaf841a4461e1b2847a068696d0a1758807e2d..d6220710bcd2265aeaa493e1c34ea6318a0dec59 100644 (file)
@@ -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'