Feature #7555 ยป private_messages.patch
| app/models/message.rb (working copy) | ||
|---|---|---|
| 36 | 36 |
:author_key => :author_id |
| 37 | 37 |
acts_as_watchable |
| 38 | 38 |
|
| 39 |
attr_protected :locked, :sticky |
|
| 39 |
attr_protected :locked, :sticky, :is_private
|
|
| 40 | 40 |
validates_presence_of :board, :subject, :content |
| 41 | 41 |
validates_length_of :subject, :maximum => 255 |
| 42 | 42 |
|
| 43 | 43 |
after_create :add_author_as_watcher |
| 44 | 44 |
|
| 45 | 45 |
def visible?(user=User.current) |
| 46 |
!user.nil? && user.allowed_to?(:view_messages, project) |
|
| 46 |
# Check private conditions first |
|
| 47 |
if is_private? |
|
| 48 |
!user.nil? && user.allowed_to?(:view_private_messages, project) |
|
| 49 |
else |
|
| 50 |
!user.nil? && user.allowed_to?(:view_messages, project) |
|
| 51 |
end |
|
| 47 | 52 |
end |
| 48 | 53 |
|
| 49 | 54 |
def validate_on_create |
| ... | ... | |
| 78 | 83 |
sticky == 1 |
| 79 | 84 |
end |
| 80 | 85 |
|
| 86 |
def is_private=(arg) |
|
| 87 |
write_attribute :is_private , (arg == true || arg.to_s == '1' ? 1 : 0) |
|
| 88 |
end |
|
| 89 | ||
| 90 |
def is_private? |
|
| 91 |
is_private == 1 |
|
| 92 |
end |
|
| 93 | ||
| 81 | 94 |
def project |
| 82 | 95 |
board.project |
| 83 | 96 |
end |
| 84 | 97 | |
| 98 |
# @TODO probably should check private message perms here? |
|
| 85 | 99 |
def editable_by?(usr) |
| 86 | 100 |
usr && usr.logged? && (usr.allowed_to?(:edit_messages, project) || (self.author == usr && usr.allowed_to?(:edit_own_messages, project))) |
| 87 | 101 |
end |
| app/models/board.rb (working copy) | ||
|---|---|---|
| 19 | 19 |
belongs_to :project |
| 20 | 20 |
has_many :topics, :class_name => 'Message', :conditions => "#{Message.table_name}.parent_id IS NULL", :order => "#{Message.table_name}.created_on DESC"
|
| 21 | 21 |
has_many :messages, :dependent => :destroy, :order => "#{Message.table_name}.created_on DESC"
|
| 22 |
belongs_to :last_message, :class_name => 'Message', :foreign_key => :last_message_id |
|
| 22 | ||
| 23 |
# Should messages be restricted by privacy settings? |
|
| 24 |
conditions = "#{Message.table_name}.is_private IN (0, " + (User.current.allowed_to?(:view_private_messages, @project) ? "1" : "0") + ")"
|
|
| 25 |
has_one :last_message, |
|
| 26 |
:class_name => 'Message', |
|
| 27 |
:conditions => "#{Message.table_name}.parent_id IS NULL AND #{conditions}",
|
|
| 28 |
:order => "#{Message.table_name}.created_on DESC"
|
|
| 29 | ||
| 23 | 30 |
acts_as_list :scope => :project_id |
| 24 | 31 |
acts_as_watchable |
| 25 |
|
|
| 32 | ||
| 26 | 33 |
validates_presence_of :name, :description |
| 27 | 34 |
validates_length_of :name, :maximum => 30 |
| 28 | 35 |
validates_length_of :description, :maximum => 255 |
| 29 | 36 |
|
| 30 | 37 |
def visible?(user=User.current) |
| 38 |
# We do not check for private messages here because view_private_messages |
|
| 39 |
# should be a sub-permission of view_messages |
|
| 31 | 40 |
!user.nil? && user.allowed_to?(:view_messages, project) |
| 32 | 41 |
end |
| 33 | 42 |
|
| 34 | 43 |
def to_s |
| 35 | 44 |
name |
| 36 | 45 |
end |
| 37 |
|
|
| 46 | ||
| 47 |
# Ensures the message count reflects the topics the user has rights to see |
|
| 48 |
def permissioned_topics_count! |
|
| 49 |
if User.current.allowed_to?(:view_private_messages, project) |
|
| 50 |
conditions = "#{Message.table_name}.is_private IN (0, 1)"
|
|
| 51 |
else |
|
| 52 |
conditions = "#{Message.table_name}.is_private = 0";
|
|
| 53 |
end |
|
| 54 |
permissioned_topics = messages.find :all, :conditions => "board_id=#{id} AND #{Message.table_name}.parent_id IS NULL AND #{conditions}"
|
|
| 55 |
return permissioned_topics.count |
|
| 56 |
end |
|
| 57 | ||
| 58 |
# Ensures the message count reflects the messages the user has rights to see |
|
| 59 |
def permissioned_messages_count! |
|
| 60 |
if User.current.allowed_to?(:view_private_messages, project) |
|
| 61 |
conditions = "#{Message.table_name}.is_private IN (0, 1)"
|
|
| 62 |
else |
|
| 63 |
conditions = "#{Message.table_name}.is_private = 0";
|
|
| 64 |
end |
|
| 65 |
permissioned_messages = messages.find :all, :conditions => "board_id=#{id} AND #{conditions}"
|
|
| 66 |
return permissioned_messages.count |
|
| 67 |
end |
|
| 68 | ||
| 38 | 69 |
def reset_counters! |
| 39 | 70 |
self.class.reset_counters!(id) |
| 40 | 71 |
end |
| ... | ... | |
| 47 | 78 |
" last_message_id = (SELECT MAX(id) FROM #{Message.table_name} WHERE board_id=#{board_id})",
|
| 48 | 79 |
["id = ?", board_id]) |
| 49 | 80 |
end |
| 50 |
end |
|
| 81 |
end |
|
| app/controllers/messages_controller.rb (working copy) | ||
|---|---|---|
| 33 | 33 |
|
| 34 | 34 |
# Show a topic and its replies |
| 35 | 35 |
def show |
| 36 |
return render_403 if !@message.visible? |
|
| 37 |
|
|
| 36 | 38 |
page = params[:page] |
| 37 | 39 |
# Find the page of the requested reply |
| 38 | 40 |
if params[:r] && page.nil? |
| 39 | 41 |
offset = @topic.children.count(:conditions => ["#{Message.table_name}.id < ?", params[:r].to_i])
|
| 40 | 42 |
page = 1 + offset / REPLIES_PER_PAGE |
| 41 | 43 |
end |
| 42 |
|
|
| 44 | ||
| 45 |
# We are not concerned about privacy here because the reply of a private |
|
| 46 |
# message will be private as well |
|
| 43 | 47 |
@reply_count = @topic.children.count |
| 44 | 48 |
@reply_pages = Paginator.new self, @reply_count, REPLIES_PER_PAGE, page |
| 45 | 49 |
@replies = @topic.children.find(:all, :include => [:author, :attachments, {:board => :project}],
|
| ... | ... | |
| 59 | 63 |
if params[:message] && User.current.allowed_to?(:edit_messages, @project) |
| 60 | 64 |
@message.locked = params[:message]['locked'] |
| 61 | 65 |
@message.sticky = params[:message]['sticky'] |
| 66 |
@message.is_private = params[:message]['is_private'] |
|
| 62 | 67 |
end |
| 63 | 68 |
if request.post? && @message.save |
| 64 | 69 |
call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
|
| ... | ... | |
| 73 | 78 |
@reply = Message.new(params[:reply]) |
| 74 | 79 |
@reply.author = User.current |
| 75 | 80 |
@reply.board = @board |
| 81 |
# Inherit privacy settings from original post |
|
| 82 |
@reply.is_private = @topic.is_private |
|
| 76 | 83 |
@topic.children << @reply |
| 77 | 84 |
if !@reply.new_record? |
| 78 | 85 |
call_hook(:controller_messages_reply_after_save, { :params => params, :message => @reply})
|
| ... | ... | |
| 88 | 95 |
if params[:message] |
| 89 | 96 |
@message.locked = params[:message]['locked'] |
| 90 | 97 |
@message.sticky = params[:message]['sticky'] |
| 98 |
@message.is_private = params[:message]['is_private'] |
|
| 91 | 99 |
end |
| 92 | 100 |
if request.post? && @message.update_attributes(params[:message]) |
| 93 | 101 |
attachments = Attachment.attach_files(@message, params[:attachments]) |
| app/controllers/boards_controller.rb (working copy) | ||
|---|---|---|
| 37 | 37 |
end |
| 38 | 38 | |
| 39 | 39 |
def show |
| 40 | ||
| 41 |
# Can the user see private messages? |
|
| 42 |
if User.current.allowed_to?(:view_private_messages, @board.project) |
|
| 43 |
conditions = "#{Message.table_name}.is_private IN (0, 1)"
|
|
| 44 |
else |
|
| 45 |
conditions = "#{Message.table_name}.is_private = 0";
|
|
| 46 |
end |
|
| 47 | ||
| 40 | 48 |
respond_to do |format| |
| 41 | 49 |
format.html {
|
| 42 | 50 |
sort_init 'updated_on', 'desc' |
| ... | ... | |
| 49 | 57 |
@topics = @board.topics.find :all, :order => ["#{Message.table_name}.sticky DESC", sort_clause].compact.join(', '),
|
| 50 | 58 |
:include => [:author, {:last_reply => :author}],
|
| 51 | 59 |
:limit => @topic_pages.items_per_page, |
| 52 |
:offset => @topic_pages.current.offset |
|
| 60 |
:offset => @topic_pages.current.offset, |
|
| 61 |
:conditions => [conditions] |
|
| 53 | 62 |
@message = Message.new |
| 54 | 63 |
render :action => 'show', :layout => !request.xhr? |
| 55 | 64 |
} |
| app/views/messages/show.rhtml (working copy) | ||
|---|---|---|
| 8 | 8 |
<%= link_to(l(:button_delete), {:action => 'destroy', :id => @topic}, :method => :post, :confirm => l(:text_are_you_sure), :class => 'icon icon-del') if @message.destroyable_by?(User.current) %>
|
| 9 | 9 |
</div> |
| 10 | 10 | |
| 11 |
<h2><%= avatar(@topic.author, :size => "24") %><%=h @topic.subject %></h2> |
|
| 11 |
<h2><%= avatar(@topic.author, :size => "24") %><%=h @topic.subject %> |
|
| 12 |
<% if @message.is_private? %><span class="private-message"><%= l(:text_message_is_private) %></span><% end %> |
|
| 13 |
</h2> |
|
| 12 | 14 | |
| 13 | 15 |
<div class="message"> |
| 14 | 16 |
<p><span class="author"><%= authoring @topic.created_on, @topic.author %></span></p> |
| app/views/messages/_form.rhtml (working copy) | ||
|---|---|---|
| 9 | 9 |
<% if !replying && User.current.allowed_to?(:edit_messages, @project) %> |
| 10 | 10 |
<label><%= f.check_box :sticky %><%= l(:label_board_sticky) %></label> |
| 11 | 11 |
<label><%= f.check_box :locked %><%= l(:label_board_locked) %></label> |
| 12 |
<label><%= f.check_box :is_private %><%= l(:label_message_private) %></label> |
|
| 12 | 13 |
<% end %> |
| 13 | 14 |
</p> |
| 14 | 15 | |
| app/views/boards/show.rhtml (working copy) | ||
|---|---|---|
| 42 | 42 |
<tbody> |
| 43 | 43 |
<% @topics.each do |topic| %> |
| 44 | 44 |
<tr class="message <%= cycle 'odd', 'even' %> <%= topic.sticky? ? 'sticky' : '' %> <%= topic.locked? ? 'locked' : '' %>"> |
| 45 |
<td class="subject"><%= link_to h(topic.subject), { :controller => 'messages', :action => 'show', :board_id => @board, :id => topic } %></td>
|
|
| 45 |
<td class="subject"><%= link_to h(topic.subject), { :controller => 'messages', :action => 'show', :board_id => @board, :id => topic } %>
|
|
| 46 |
<% if topic.is_private? %><span class="private-message"><%= l(:label_board_message_is_private) %></span><% end %> |
|
| 47 |
</td> |
|
| 46 | 48 |
<td class="author" align="center"><%= topic.author %></td> |
| 47 | 49 |
<td class="created_on" align="center"><%= format_time(topic.created_on) %></td> |
| 48 | 50 |
<td class="replies" align="center"><%= topic.replies_count %></td> |
| app/views/boards/index.rhtml (working copy) | ||
|---|---|---|
| 14 | 14 |
<%= link_to h(board.name), {:action => 'show', :id => board}, :class => "board" %><br />
|
| 15 | 15 |
<%=h board.description %> |
| 16 | 16 |
</td> |
| 17 |
<td align="center"><%= board.topics_count %></td>
|
|
| 18 |
<td align="center"><%= board.messages_count %></td>
|
|
| 17 |
<td align="center"><%= board.permissioned_topics_count! %></td>
|
|
| 18 |
<td align="center"><%= board.permissioned_messages_count! %></td>
|
|
| 19 | 19 |
<td> |
| 20 | 20 |
<small> |
| 21 | 21 |
<% if board.last_message %> |
| 22 | 22 |
<%= authoring board.last_message.created_on, board.last_message.author %><br /> |
| 23 |
<%= link_to_message board.last_message %>
|
|
| 24 |
<% end %> |
|
| 23 |
<%= link_to_message board.last_message %> |
|
| 24 |
<% end %>
|
|
| 25 | 25 |
</small> |
| 26 | 26 |
</td> |
| 27 | 27 |
</tr> |
| db/migrate/20110405_add_message_private.rb (revision 0) | ||
|---|---|---|
| 1 |
class AddMessagePrivate < ActiveRecord::Migration |
|
| 2 |
def self.up |
|
| 3 |
add_column :messages, :is_private, :integer, :default => 0 |
|
| 4 |
end |
|
| 5 | ||
| 6 |
def self.down |
|
| 7 |
remove_column :messages, :is_private |
|
| 8 |
end |
|
| 9 |
end |
|
| config/locales/en.yml (working copy) | ||
|---|---|---|
| 793 | 793 |
label_project_copy_notifications: Send email notifications during the project copy |
| 794 | 794 |
label_principal_search: "Search for user or group:" |
| 795 | 795 |
label_user_search: "Search for user:" |
| 796 |
|
|
| 796 |
label_message_private: "Private post" |
|
| 797 |
label_board_message_is_private: "(Private post)" |
|
| 798 | ||
| 797 | 799 |
button_login: Login |
| 798 | 800 |
button_submit: Submit |
| 799 | 801 |
button_save: Save |
| ... | ... | |
| 908 | 910 |
text_own_membership_delete_confirmation: "You are about to remove some or all of your permissions and may no longer be able to edit this project after that.\nAre you sure you want to continue?" |
| 909 | 911 |
text_zoom_in: Zoom in |
| 910 | 912 |
text_zoom_out: Zoom out |
| 913 |
text_message_is_private: "(Private message)" |
|
| 911 | 914 |
|
| 912 | 915 |
default_role_manager: Manager |
| 913 | 916 |
default_role_developer: Developer |
| ... | ... | |
| 935 | 938 |
enumeration_doc_categories: Document categories |
| 936 | 939 |
enumeration_activities: Activities (time tracking) |
| 937 | 940 |
enumeration_system_activity: System Activity |
| 938 | ||
| lib/redmine.rb (working copy) | ||
|---|---|---|
| 129 | 129 |
map.project_module :boards do |map| |
| 130 | 130 |
map.permission :manage_boards, {:boards => [:new, :edit, :destroy]}, :require => :member
|
| 131 | 131 |
map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :public => true
|
| 132 |
map.permission :view_private_messages, {:boards => [:index, :show], :messages => [:show]}, :public => false
|
|
| 132 | 133 |
map.permission :add_messages, {:messages => [:new, :reply, :quote]}
|
| 133 | 134 |
map.permission :edit_messages, {:messages => :edit}, :require => :member
|
| 134 | 135 |
map.permission :edit_own_messages, {:messages => :edit}, :require => :loggedin
|