##// END OF EJS Templates
Adds watch/unwatch functionality at forum topic level (#1912)....
Jean-Philippe Lang -
r1876:16e09bfd7744
parent child
Show More
@@ -0,0 +1,14
1 class SetTopicAuthorsAsWatchers < ActiveRecord::Migration
2 def self.up
3 # Sets active users who created/replied a topic as watchers of the topic
4 # so that the new watch functionality at topic level doesn't affect notifications behaviour
5 Message.connection.execute("INSERT INTO watchers (watchable_type, watchable_id, user_id)" +
6 " SELECT DISTINCT 'Message', COALESCE(messages.parent_id, messages.id), messages.author_id FROM messages, users" +
7 " WHERE messages.author_id = users.id AND users.status = 1")
8 end
9
10 def self.down
11 # Removes all message watchers
12 Watcher.delete_all("watchable_type = 'Message'")
13 end
14 end
@@ -24,7 +24,7 class MessagesController < ApplicationController
24 verify :method => :post, :only => [ :reply, :destroy ], :redirect_to => { :action => :show }
24 verify :method => :post, :only => [ :reply, :destroy ], :redirect_to => { :action => :show }
25 verify :xhr => true, :only => :quote
25 verify :xhr => true, :only => :quote
26
26
27
27 helper :watchers
28 helper :attachments
28 helper :attachments
29 include AttachmentsHelper
29 include AttachmentsHelper
30
30
@@ -33,11 +33,14 class Message < ActiveRecord::Base
33 {:id => o.parent_id, :anchor => "message-#{o.id}"})}
33 {:id => o.parent_id, :anchor => "message-#{o.id}"})}
34
34
35 acts_as_activity_provider :find_options => {:include => [{:board => :project}, :author]}
35 acts_as_activity_provider :find_options => {:include => [{:board => :project}, :author]}
36 acts_as_watchable
36
37
37 attr_protected :locked, :sticky
38 attr_protected :locked, :sticky
38 validates_presence_of :subject, :content
39 validates_presence_of :subject, :content
39 validates_length_of :subject, :maximum => 255
40 validates_length_of :subject, :maximum => 255
40
41
42 after_create :add_author_as_watcher
43
41 def validate_on_create
44 def validate_on_create
42 # Can not reply to a locked topic
45 # Can not reply to a locked topic
43 errors.add_to_base 'Topic is locked' if root.locked? && self != root
46 errors.add_to_base 'Topic is locked' if root.locked? && self != root
@@ -68,4 +71,10 class Message < ActiveRecord::Base
68 def project
71 def project
69 board.project
72 board.project
70 end
73 end
74
75 private
76
77 def add_author_as_watcher
78 Watcher.create(:watchable => self.root, :user => author)
79 end
71 end
80 end
@@ -17,8 +17,9
17
17
18 class MessageObserver < ActiveRecord::Observer
18 class MessageObserver < ActiveRecord::Observer
19 def after_create(message)
19 def after_create(message)
20 # send notification to the authors of the thread
20 recipients = []
21 recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author && m.author.active?}
21 # send notification to the topic watchers
22 recipients += message.root.watcher_recipients
22 # send notification to the board watchers
23 # send notification to the board watchers
23 recipients += message.board.watcher_recipients
24 recipients += message.board.watcher_recipients
24 # send notification to project members who want to be notified
25 # send notification to project members who want to be notified
@@ -2,6 +2,7
2 link_to(h(@board.name), {:controller => 'boards', :action => 'show', :project_id => @project, :id => @board}) %>
2 link_to(h(@board.name), {:controller => 'boards', :action => 'show', :project_id => @project, :id => @board}) %>
3
3
4 <div class="contextual">
4 <div class="contextual">
5 <%= watcher_tag(@topic, User.current) %>
5 <%= link_to_remote_if_authorized l(:button_quote), { :url => {:action => 'quote', :id => @topic} }, :class => 'icon icon-comment' %>
6 <%= link_to_remote_if_authorized l(:button_quote), { :url => {:action => 'quote', :id => @topic} }, :class => 'icon icon-comment' %>
6 <%= link_to_if_authorized l(:button_edit), {:action => 'edit', :id => @topic}, :class => 'icon icon-edit' %>
7 <%= link_to_if_authorized l(:button_edit), {:action => 'edit', :id => @topic}, :class => 'icon icon-edit' %>
7 <%= link_to_if_authorized l(:button_delete), {:action => 'destroy', :id => @topic}, :method => :post, :confirm => l(:text_are_you_sure), :class => 'icon icon-del' %>
8 <%= link_to_if_authorized l(:button_delete), {:action => 'destroy', :id => @topic}, :method => :post, :confirm => l(:text_are_you_sure), :class => 'icon icon-del' %>
@@ -3,4 +3,8 watchers_001:
3 watchable_type: Issue
3 watchable_type: Issue
4 watchable_id: 2
4 watchable_id: 2
5 user_id: 3
5 user_id: 3
6 watchers_002:
7 watchable_type: Message
8 watchable_id: 1
9 user_id: 1
6 No newline at end of file
10
@@ -1,7 +1,7
1 require File.dirname(__FILE__) + '/../test_helper'
1 require File.dirname(__FILE__) + '/../test_helper'
2
2
3 class MessageTest < Test::Unit::TestCase
3 class MessageTest < Test::Unit::TestCase
4 fixtures :projects, :boards, :messages
4 fixtures :projects, :boards, :messages, :users, :watchers
5
5
6 def setup
6 def setup
7 @board = Board.find(1)
7 @board = Board.find(1)
@@ -20,6 +20,8 class MessageTest < Test::Unit::TestCase
20 # messages count incremented
20 # messages count incremented
21 assert_equal messages_count+1, @board[:messages_count]
21 assert_equal messages_count+1, @board[:messages_count]
22 assert_equal message, @board.last_message
22 assert_equal message, @board.last_message
23 # author should be watching the message
24 assert message.watched_by?(@user)
23 end
25 end
24
26
25 def test_reply
27 def test_reply
@@ -28,7 +30,8 class MessageTest < Test::Unit::TestCase
28 @message = Message.find(1)
30 @message = Message.find(1)
29 replies_count = @message.replies_count
31 replies_count = @message.replies_count
30
32
31 reply = Message.new(:board => @board, :subject => 'Test reply', :content => 'Test reply content', :parent => @message, :author => @user)
33 reply_author = User.find(2)
34 reply = Message.new(:board => @board, :subject => 'Test reply', :content => 'Test reply content', :parent => @message, :author => reply_author)
32 assert reply.save
35 assert reply.save
33 @board.reload
36 @board.reload
34 # same topics count
37 # same topics count
@@ -40,13 +43,18 class MessageTest < Test::Unit::TestCase
40 # replies count incremented
43 # replies count incremented
41 assert_equal replies_count+1, @message[:replies_count]
44 assert_equal replies_count+1, @message[:replies_count]
42 assert_equal reply, @message.last_reply
45 assert_equal reply, @message.last_reply
46 # author should be watching the message
47 assert @message.watched_by?(reply_author)
43 end
48 end
44
49
45 def test_destroy_topic
50 def test_destroy_topic
46 message = Message.find(1)
51 message = Message.find(1)
47 board = message.board
52 board = message.board
48 topics_count, messages_count = board.topics_count, board.messages_count
53 topics_count, messages_count = board.topics_count, board.messages_count
49 assert message.destroy
54
55 assert_difference('Watcher.count', -1) do
56 assert message.destroy
57 end
50 board.reload
58 board.reload
51
59
52 # Replies deleted
60 # Replies deleted
@@ -54,6 +62,7 class MessageTest < Test::Unit::TestCase
54 # Checks counters
62 # Checks counters
55 assert_equal topics_count - 1, board.topics_count
63 assert_equal topics_count - 1, board.topics_count
56 assert_equal messages_count - 3, board.messages_count
64 assert_equal messages_count - 3, board.messages_count
65 # Watchers removed
57 end
66 end
58
67
59 def test_destroy_reply
68 def test_destroy_reply
General Comments 0
You need to be logged in to leave comments. Login now