mirror of
				https://github.com/redmine/redmine.git
				synced 2025-11-03 20:06:24 +01:00 
			
		
		
		
	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
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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?
 | 
			
		||||
    @watchables = find_objets_from_params
 | 
			
		||||
    unless @watchables.present?
 | 
			
		||||
      render_404
 | 
			
		||||
    end
 | 
			
		||||
      }
 | 
			
		||||
    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
 | 
			
		||||
 
 | 
			
		||||
@@ -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 %>
 | 
			
		||||
 
 | 
			
		||||
@@ -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">
 | 
			
		||||
 
 | 
			
		||||
@@ -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 %>
 | 
			
		||||
 
 | 
			
		||||
@@ -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 %>
 | 
			
		||||
 
 | 
			
		||||
@@ -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');
 | 
			
		||||
 
 | 
			
		||||
@@ -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'
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
 
 | 
			
		||||
@@ -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'
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user