Project

General

Profile

Feature #5577 » category_watchers_patch-20120602-branches_1.4-stable_r9748.patch

Nayuta Taga, 2012-06-02 10:40

View differences:

test/functional/watchers_controller_test.rb (working copy)
127 127
        assert_select 'input[name=?][value=4]', 'issue[watcher_user_ids][]'
128 128
        assert_select 'input[name=?][value=7]', 'issue[watcher_user_ids][]'
129 129
      end
130
      assert @response.body =~ /issue_new_setupDefaultWatchersEvents/
130 131
    end
131 132
  end
132 133

  
......
139 140
    end
140 141
    assert !Issue.find(2).watched_by?(User.find(3))
141 142
  end
143

  
144
  def test_show
145
    watchable_type = 'Issue'
146
    watchable_id = 2
147
    
148
    @request.session[:user_id] = 2
149
    xhr :get, :show, :watchable_type => watchable_type.underscore, :watchable_id => watchable_id.to_s, :format => 'json'
150
    assert_response :success
151
    r = ActiveSupport::JSON.decode(@response.body)
152
    assert r
153
    assert_equal r.length, 2
154
    assert_equal watchable_type, r[0]['watchable_type']
155
    assert_equal watchable_type, r[1]['watchable_type']
156
    assert_equal watchable_id, r[0]['watchable_id']
157
    assert_equal watchable_id, r[1]['watchable_id']
158
    fixtures_user_ids = [ watchers(:watchers_001).user_id,
159
                          watchers(:watchers_002).user_id ].sort
160
    r_user_ids = [r[0]['user_id'], r[1]['user_id']].sort
161
    assert_equal fixtures_user_ids, r_user_ids
162
  end
142 163
end
test/functional/issue_categories_controller_test.rb (working copy)
37 37
    get :new, :project_id => '1'
38 38
    assert_response :success
39 39
    assert_template 'new'
40
    assert_tag 'input', :attributes => {:name => 'issue_category[watcher_user_ids][]', :value => '2'}
41
    assert_tag 'input', :attributes => {:name => 'issue_category[watcher_user_ids][]', :value => '3'}
40 42
  end
41 43

  
42 44
  def test_create
......
50 52
    assert_equal 1, category.project_id
51 53
  end
52 54

  
55
  def test_create_with_watchers
56
    @request.session[:user_id] = 2 # manager
57
    assert_difference 'Watcher.count', 2 do
58
      post :create, :project_id => '1', :issue_category => {:name => 'New category with watchers', :watcher_user_ids => ['2', '3']}
59
    end
60
    assert_redirected_to '/projects/ecookbook/settings/categories'
61
    category = IssueCategory.find_by_name('New category with watchers')
62
    assert_not_nil category
63
    assert_equal 1, category.project_id
64
    # Watchers added
65
    assert_equal [2, 3], category.watcher_user_ids.sort
66
    assert category.watched_by?(User.find(3))
67
  end
68

  
53 69
  def test_create_failure
54 70
    @request.session[:user_id] = 2
55 71
    post :create, :project_id => '1', :issue_category => {:name => ''}
......
69 85
    assert_select_rjs :replace, 'issue_category_id' do
70 86
      assert_select "option[value=#{category.id}][selected=selected]"
71 87
    end
88
    assert @response.body =~ /issue_new_setupDefaultWatchersEvents/
72 89
  end
73 90

  
74 91
  def test_create_from_issue_form_with_failure
......
86 103
    get :edit, :id => 2
87 104
    assert_response :success
88 105
    assert_template 'edit'
106
    assert_tag 'input', :attributes => {:name => 'issue_category[watcher_user_ids][]', :value => '2'}
107
    assert_tag 'input', :attributes => {:name => 'issue_category[watcher_user_ids][]', :value => '3'}
89 108
  end
90 109

  
91 110
  def test_update
......
96 115
    assert_equal 'Testing', IssueCategory.find(2).name
97 116
  end
98 117

  
118
  def test_update_with_watchers
119
    assert_difference 'Watcher.count', 2 do
120
      put :update, :id => 2, :issue_category => { :name => 'Testing', :watcher_user_ids => ['2', '3']}
121
    end
122
    assert_redirected_to '/projects/ecookbook/settings/categories'
123
    category = IssueCategory.find(2)
124
    assert_equal 'Testing', category.name
125
    # Watchers added
126
    assert_equal [2, 3], category.watcher_user_ids.sort
127
    assert category.watched_by?(User.find(3))
128
  end
129

  
99 130
  def test_update_failure
100 131
    put :update, :id => 2, :issue_category => { :name => '' }
101 132
    assert_response :success
test/integration/routing/watchers_test.rb (working copy)
47 47
        { :method => 'post', :path => "/watchers/unwatch" },
48 48
        { :controller => 'watchers', :action => 'unwatch' }
49 49
      )
50
    assert_routing(
51
        { :method => 'post', :path => "/watchers/show/issue_category/1.json" },
52
        { :controller => 'watchers', :action => 'show', :watchable_type => 'issue_category', :watchable_id => '1', :format => 'json' }
53
      )
50 54
  end
51 55
end
app/helpers/issue_categories_helper.rb (working copy)
18 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 19

  
20 20
module IssueCategoriesHelper
21

  
22
  def url_for_issue_category_watchers_json(watchable_id)
23
    url_for :controller => 'watchers', :action => 'show', :watchable_type => 'issue_category', :watchable_id => watchable_id, :format => 'json'
24
  end
25

  
21 26
end
app/helpers/watchers_helper.rb (working copy)
64 64
    (lis.empty? ? "" : "<ul>#{ lis.join("\n") }</ul>").html_safe
65 65
  end
66 66

  
67
  def watchers_checkboxes(object, users, checked=nil)
67
  def watchers_checkboxes(object, object_type, users, checked=nil)
68
    name = (object_type ? object_type.name.underscore : 'issue')
69
    
68 70
    users.map do |user|
69 71
      c = checked.nil? ? object.watched_by?(user) : checked
70
      tag = check_box_tag 'issue[watcher_user_ids][]', user.id, c, :id => nil
71
      content_tag 'label', "#{tag} #{h(user)}", :id => "issue_watcher_user_ids_#{user.id}", :class => "floating"
72
      tag = check_box_tag "#{name}[watcher_user_ids][]", user.id, c, :id => nil
73
      content_tag 'label', "#{tag} #{h(user)}", :id => "#{name}_watcher_user_ids_#{user.id}", :class => "floating"
72 74
    end.join
73 75
  end
74 76
end
app/models/issue_category.rb (working copy)
21 21
  belongs_to :assigned_to, :class_name => 'Principal', :foreign_key => 'assigned_to_id'
22 22
  has_many :issues, :foreign_key => 'category_id', :dependent => :nullify
23 23

  
24
  acts_as_watchable
25

  
24 26
  validates_presence_of :name
25 27
  validates_uniqueness_of :name, :scope => [:project_id]
26 28
  validates_length_of :name, :maximum => 30
app/controllers/issues_controller.rb (working copy)
52 52
  helper :timelog
53 53
  helper :gantt
54 54
  include Redmine::Export::PDF
55
  helper :issue_categories
55 56

  
56 57
  def index
57 58
    retrieve_query
app/controllers/watchers_controller.rb (working copy)
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 17

  
18 18
class WatchersController < ApplicationController
19
  before_filter :find_project
19
  helper :issue_categories
20
  before_filter :find_project, :except => [:show]
20 21
  before_filter :require_login, :check_project_privacy, :only => [:watch, :unwatch]
21 22
  before_filter :authorize, :only => [:new, :destroy]
22 23

  
......
32 33
    set_watcher(User.current, false)
33 34
  end
34 35

  
36
  def show
37
    respond_to do |format|
38
      format.api do
39
        result =  Watcher.find(:all, :conditions => [ "watchable_type = ? and watchable_id = ?", params['watchable_type'].camelcase, params['watchable_id'].to_i])
40
        render :json => result
41
      end
42
    end
43
  end
44

  
35 45
  def new
36 46
    respond_to do |format|
37 47
      format.js do
38 48
        render :update do |page|
39
          page.replace_html 'ajax-modal', :partial => 'watchers/new', :locals => {:watched => @watched}
49
          page.replace_html 'ajax-modal', :partial => 'watchers/new', :locals => {:watched => @watched, :watched_type => @watched_type}
40 50
          page << "showModal('ajax-modal', '400px');"
41 51
          page << "$('ajax-modal').addClassName('new-watcher');"
42 52
        end
......
55 65
      format.html { redirect_to :back }
56 66
      format.js do
57 67
        render :update do |page|
58
          page.replace_html 'ajax-modal', :partial => 'watchers/new', :locals => {:watched => @watched}
68
          page.replace_html 'ajax-modal', :partial => 'watchers/new', :locals => {:watched => @watched, :watched_type => @watched_type}
59 69
          page.replace_html 'watchers', :partial => 'watchers/watchers', :locals => {:watched => @watched}
60 70
        end
61 71
      end
......
70 80
      users = User.active.find_all_by_id(user_ids)
71 81
      respond_to do |format|
72 82
        format.js do
83
          name = (@watched_type ? @watched_type.name.underscore : 'issue')
73 84
          render :update do |page|
74 85
            users.each do |user|
75
              page.select("#issue_watcher_user_ids_#{user.id}").each do |item|
86
              page.select("##{name}_watcher_user_ids_#{user.id}").each do |item|
76 87
                page.remove item
77 88
              end
78 89
            end
79
            page.insert_html :bottom, 'watchers_inputs', :text => watchers_checkboxes(nil, users, true)
90
            page.insert_html :bottom, 'watchers_inputs', :text => watchers_checkboxes(nil, @watched_type, users, true)
91
            if name == 'issue'
92
              page << %|issue_new_setupDefaultWatchersEvents('#{j(url_for_issue_category_watchers_json('WATCHABLE_ID'))}')|
93
            end
80 94
          end
81 95
        end
82 96
      end
......
113 127
    elsif params[:project_id]
114 128
      @project = Project.visible.find(params[:project_id])
115 129
    end
130
    if !@watched && params[:object_type]
131
      klass = Object.const_get(params[:object_type].camelcase)
132
      @watched_type = klass if klass.respond_to?('watched_by')
133
    end
116 134
  rescue
117 135
    render_404
118 136
  end
app/controllers/issue_categories_controller.rb (working copy)
23 23
  before_filter :find_project_by_project_id, :only => [:index, :new, :create]
24 24
  before_filter :authorize
25 25
  accept_api_auth :index, :show, :create, :update, :destroy
26
  helper :watchers
26 27
  
27 28
  def index
28 29
    respond_to do |format|
......
41 42
  def new
42 43
    @category = @project.issue_categories.build
43 44
    @category.safe_attributes = params[:issue_category]
45
    assign_watchers_from_params
44 46
  end
45 47

  
46 48
  def create
47 49
    @category = @project.issue_categories.build
48 50
    @category.safe_attributes = params[:issue_category]
51
    assign_watchers_from_params
49 52
    if @category.save
50 53
      respond_to do |format|
51 54
        format.html do
......
56 59
          # IE doesn't support the replace_html rjs method for select box options
57 60
          render(:update) {|page| page.replace "issue_category_id",
58 61
            content_tag('select', content_tag('option') + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]')
62
            page << %|issue_new_setupDefaultWatchersEvents('#{j(url_for_issue_category_watchers_json('WATCHABLE_ID'))}')|
59 63
          }
60 64
        end
61 65
        format.api { render :action => 'show', :status => :created, :location => issue_category_path(@category) }
......
76 80

  
77 81
  def update
78 82
    @category.safe_attributes = params[:issue_category]
83
    assign_watchers_from_params
79 84
    if @category.save
80 85
      respond_to do |format|
81 86
        format.html {
......
116 121
    super
117 122
    @category = @object
118 123
  end
124

  
125
  def assign_watchers_from_params
126
    if params[:issue_category].is_a?(Hash)
127
      @category.watcher_user_ids = params[:issue_category]['watcher_user_ids']
128
    end
129
  end
119 130
end
app/views/watchers/_new.html.erb (working copy)
2 2

  
3 3
<% form_remote_tag :url => {:controller => 'watchers',
4 4
                            :action => (watched ? 'create' : 'append'),
5
                            :object_type => watched.class.name.underscore,
5
                            :object_type => (watched_type ? watched_type.name.underscore : watched.class.name.underscore),
6 6
                            :object_id => watched},
7 7
                     :method => :post,
8 8
                     :html => {:id => 'new-watcher-form'} do %>
app/views/issue_categories/_form.html.erb (working copy)
3 3
<div class="box tabular">
4 4
<p><%= f.text_field :name, :size => 30, :required => true %></p>
5 5
<p><%= f.select :assigned_to_id, principals_options_for_select(@project.assignable_users, @category.assigned_to), :include_blank => true %></p>
6
<p id="watchers_form"><label><%= l(:label_issue_category_watchers) %></label>
7
<span id="watchers_inputs">
8
  <%= watchers_checkboxes(@category, IssueCategory, @project.users) %>
9
</span>
10
<span class="search_for_watchers">
11
<%= link_to_remote l(:label_search_for_watchers),
12
             :url => {:controller => 'watchers', :action => 'new', :project_id => @project, :object_type => 'issue_category'},
13
             :method => 'get' %>
14
</span>
15
</p>
6 16
</div>
app/views/issues/new.html.erb (working copy)
23 23
    <% if @issue.safe_attribute? 'watcher_user_ids' -%>
24 24
      <p id="watchers_form"><label><%= l(:label_issue_watchers) %></label>
25 25
      <span id="watchers_inputs">
26
        <%= watchers_checkboxes(@issue, @available_watchers) %>
26
        <%= watchers_checkboxes(@issue, Issue, @available_watchers) %>
27 27
      </span>
28 28
      <span class="search_for_watchers">
29 29
      <%= link_to_remote l(:label_search_for_watchers),
30
                   :url => {:controller => 'watchers', :action => 'new', :project_id => @issue.project},
30
                   :url => {:controller => 'watchers', :action => 'new', :project_id => @issue.project, :object_type => 'issue'},
31 31
                   :method => 'get' %>
32 32
      </span>
33
      <%= javascript_tag "issue_new_setupDefaultWatchersEvents('#{j(url_for_issue_category_watchers_json('WATCHABLE_ID'))}')" %>
33 34
      </p>
34 35
    <% end %>
35 36
  </div>
config/locales/en.yml (working copy)
502 502
  label_issue_category: Issue category
503 503
  label_issue_category_plural: Issue categories
504 504
  label_issue_category_new: New category
505
  label_issue_category_watchers: Default watchers
505 506
  label_custom_field: Custom field
506 507
  label_custom_field_plural: Custom fields
507 508
  label_custom_field_new: New custom field
config/locales/ja.yml (working copy)
510 510
  label_issue_category: チケットのカテゴリ
511 511
  label_issue_category_plural: チケットのカテゴリ
512 512
  label_issue_category_new: 新しいカテゴリ
513
  label_issue_category_watchers: デフォルトウォッチャー
513 514
  label_custom_field: カスタムフィールド
514 515
  label_custom_field_plural: カスタムフィールド
515 516
  label_custom_field_new: 新しいカスタムフィールドを作成
config/routes.rb (working copy)
136 136
              :conditions => {:method => :post}
137 137
  map.connect 'watchers/autocomplete_for_user', :controller=> 'watchers', :action => 'autocomplete_for_user',
138 138
              :conditions => {:method => :get}
139
  map.connect 'watchers/show/:watchable_type/:watchable_id.:format', :controller => 'watchers', :action => 'show',
140
              :conditions => {:method => :post}
139 141

  
140 142
  # TODO: port to be part of the resources route(s)
141 143
  map.with_options :conditions => {:method => :get} do |project_views|
public/javascripts/application.js (working copy)
534 534
  });
535 535
}
536 536

  
537
function issue_new_resetDefaultWatchersByCategory() {
538
  $$('#watchers_inputs input').each(function(el){
539
    el.checked = false;
540
    el.parentNode.removeClassName('watchers_default');
541
  });
542
}
543

  
544
function issue_new_setDefaultWatchersByCategory(url_for_issue_category_watchers_json) {
545
  issue_category_id = $('issue_category_id');
546
  if (!issue_category_id) {
547
    return;
548
  }
549
  if (0 < issue_category_id.value.length) {
550
    url = url_for_issue_category_watchers_json.replace(/WATCHABLE_ID/, issue_category_id.value);
551
    new Ajax.Request(url, {
552
      onSuccess: function (request) {
553
        eval("result="+ request.responseText);
554
        issue_new_resetDefaultWatchersByCategory();
555
        $$('#watchers_inputs input').each(function(el){
556
          user_id = parseInt(el.value);
557
          result.each(function(a){
558
            if (user_id === a.user_id) {
559
              el.checked = true;
560
              el.parentNode.addClassName('watchers_default');
561
              throw $break;
562
            }
563
          });
564
        });
565
      }
566
    });
567
  } else {
568
    issue_new_resetDefaultWatchersByCategory();
569
  }
570
}
571

  
572
function issue_new_setupDefaultWatchersEvents(url_for_issue_category_watchers_json) {
573
  issue_category_id = $('issue_category_id');
574
  if (!issue_category_id) {
575
    return;
576
  }
577
  function f() {
578
    issue_new_setDefaultWatchersByCategory(
579
      url_for_issue_category_watchers_json);
580
  }
581
  Event.observe(issue_category_id, 'change', f);
582
  f();
583
}
584

  
537 585
Event.observe(window, 'load', hideOnLoad);
538 586
Event.observe(window, 'load', addFormObserversForDoubleSubmit);
public/stylesheets/application.css (working copy)
266 266
#watchers img.gravatar {margin: 0 4px 2px 0;}
267 267

  
268 268
span#watchers_inputs {overflow:auto; display:block;}
269
span#watchers_inputs .watchers_default {color: #080; font-weight:bold;}
269 270
span.search_for_watchers {display:block;}
270 271
span.search_for_watchers, span.add_attachment {font-size:80%; line-height:2.5em;}
271 272
span.search_for_watchers a, span.add_attachment a {padding-left:16px; background: url(../images/bullet_add.png) no-repeat 0 50%; }
(6-6/6)