From 29b3614bcb759214bb1aba77c27ac11c8ef6b15b Mon Sep 17 00:00:00 2001 From: Jean-Philippe Lang Date: Sat, 24 Nov 2007 12:25:07 +0000 Subject: [PATCH] Forums enhancements: * messages can now be edited/deleted (explicit permissions need to be given) * topics can be locked so that no reply can be added (only by users allowed to edit messages) * topics can be marked as sticky so that they always appear at the top of the list (only by users allowed to edit messages) git-svn-id: http://redmine.rubyforge.org/svn/trunk@926 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/boards_controller.rb | 2 +- app/controllers/messages_controller.rb | 51 +++++++++++++++++--- app/helpers/application_helper.rb | 4 +- app/models/message.rb | 18 +++++++ app/views/boards/index.rhtml | 2 +- app/views/boards/show.rhtml | 18 +++---- app/views/messages/_form.rhtml | 8 ++- app/views/messages/edit.rhtml | 6 +++ app/views/messages/show.rhtml | 27 +++++++---- db/migrate/082_add_messages_locked.rb | 9 ++++ db/migrate/083_add_messages_sticky.rb | 9 ++++ lib/redmine.rb | 2 + public/images/sticky.png | Bin 0 -> 461 bytes public/stylesheets/application.css | 5 ++ test/fixtures/boards.yml | 6 +-- test/fixtures/messages.yml | 38 +++++++++++++-- test/functional/boards_controller_test.rb | 50 +++++++++++++++++++ test/functional/messages_controller_test.rb | 49 +++++++++++++++++++ test/unit/message_test.rb | 26 ++++++++++ 19 files changed, 294 insertions(+), 36 deletions(-) create mode 100644 app/views/messages/edit.rhtml create mode 100644 db/migrate/082_add_messages_locked.rb create mode 100644 db/migrate/083_add_messages_sticky.rb create mode 100644 public/images/sticky.png create mode 100644 test/functional/boards_controller_test.rb create mode 100644 test/functional/messages_controller_test.rb diff --git a/app/controllers/boards_controller.rb b/app/controllers/boards_controller.rb index 3a8b021a3..200792370 100644 --- a/app/controllers/boards_controller.rb +++ b/app/controllers/boards_controller.rb @@ -41,7 +41,7 @@ class BoardsController < ApplicationController @topic_count = @board.topics.count @topic_pages = Paginator.new self, @topic_count, 25, params['page'] - @topics = @board.topics.find :all, :order => sort_clause, + @topics = @board.topics.find :all, :order => "#{Message.table_name}.sticky DESC, #{sort_clause}", :include => [:author, {:last_reply => :author}], :limit => @topic_pages.items_per_page, :offset => @topic_pages.current.offset diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index 9352c4af4..46c9adadd 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -17,22 +17,30 @@ class MessagesController < ApplicationController layout 'base' - before_filter :find_project, :authorize + before_filter :find_board, :only => :new + before_filter :find_message, :except => :new + before_filter :authorize verify :method => :post, :only => [ :reply, :destroy ], :redirect_to => { :action => :show } helper :attachments include AttachmentsHelper + # Show a topic and its replies def show @reply = Message.new(:subject => "RE: #{@message.subject}") render :action => "show", :layout => false if request.xhr? end + # Create a new topic def new @message = Message.new(params[:message]) @message.author = User.current - @message.board = @board + @message.board = @board + if params[:message] && User.current.allowed_to?(:edit_messages, @project) + @message.locked = params[:message]['locked'] + @message.sticky = params[:message]['sticky'] + end if request.post? && @message.save params[:attachments].each { |file| Attachment.create(:container => @message, :file => file, :author => User.current) if file.size > 0 @@ -41,24 +49,55 @@ class MessagesController < ApplicationController end end + # Reply to a topic def reply @reply = Message.new(params[:reply]) @reply.author = User.current @reply.board = @board - @message.children << @reply + @topic.children << @reply if !@reply.new_record? params[:attachments].each { |file| Attachment.create(:container => @reply, :file => file, :author => User.current) if file.size > 0 } if params[:attachments] and params[:attachments].is_a? Array end - redirect_to :action => 'show', :id => @message + redirect_to :action => 'show', :id => @topic + end + + # Edit a message + def edit + if params[:message] && User.current.allowed_to?(:edit_messages, @project) + @message.locked = params[:message]['locked'] + @message.sticky = params[:message]['sticky'] + end + if request.post? && @message.update_attributes(params[:message]) + params[:attachments].each { |file| + Attachment.create(:container => @message, :file => file, :author => User.current) if file.size > 0 + } if params[:attachments] and params[:attachments].is_a? Array + flash[:notice] = l(:notice_successful_update) + redirect_to :action => 'show', :id => @topic + end + end + + # Delete a messages + def destroy + @message.destroy + redirect_to @message.parent.nil? ? + { :controller => 'boards', :action => 'show', :project_id => @project, :id => @board } : + { :action => 'show', :id => @message.parent } end private - def find_project + def find_message + find_board + @message = @board.messages.find(params[:id], :include => :parent) + @topic = @message.root + rescue ActiveRecord::RecordNotFound + render_404 + end + + def find_board @board = Board.find(params[:board_id], :include => :project) @project = @board.project - @message = @board.topics.find(params[:id]) if params[:id] rescue ActiveRecord::RecordNotFound render_404 end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 9c8e9c67d..f4746c627 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -34,7 +34,7 @@ module ApplicationHelper # Display a link to user's account page def link_to_user(user) - link_to user.name, :controller => 'account', :action => 'show', :id => user + user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous' end def link_to_issue(issue) @@ -92,7 +92,7 @@ module ApplicationHelper def authoring(created, author) time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created)) - l(:label_added_time_by, author.name, time_tag) + l(:label_added_time_by, author || 'Anonymous', time_tag) end def day_name(day) diff --git a/app/models/message.rb b/app/models/message.rb index 909c06a9e..038665cce 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -30,9 +30,15 @@ class Message < ActiveRecord::Base :description => :content, :url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id, :id => o.id}} + attr_protected :locked, :sticky validates_presence_of :subject, :content validates_length_of :subject, :maximum => 255 + def validate_on_create + # Can not reply to a locked topic + errors.add_to_base 'Topic is locked' if root.locked? + end + def after_create board.update_attribute(:last_message_id, self.id) board.increment! :messages_count @@ -43,6 +49,18 @@ class Message < ActiveRecord::Base end end + def after_destroy + # The following line is required so that the previous counter + # updates (due to children removal) are not overwritten + board.reload + board.decrement! :messages_count + board.decrement! :topics_count unless parent + end + + def sticky? + sticky == 1 + end + def project board.project end diff --git a/app/views/boards/index.rhtml b/app/views/boards/index.rhtml index 3291d0194..cd4e85e9a 100644 --- a/app/views/boards/index.rhtml +++ b/app/views/boards/index.rhtml @@ -19,7 +19,7 @@ <% if board.last_message %> - <%= board.last_message.author.name %>, <%= format_time(board.last_message.created_on) %>
+ <%= authoring board.last_message.created_on, board.last_message.author %>
<%= link_to_message board.last_message %> <% end %>
diff --git a/app/views/boards/show.rhtml b/app/views/boards/show.rhtml index 0af89fdb7..8bcf960b2 100644 --- a/app/views/boards/show.rhtml +++ b/app/views/boards/show.rhtml @@ -18,7 +18,7 @@

<%=h @board.name %>

<% if @topics.any? %> - +
@@ -28,18 +28,16 @@ <% @topics.each do |topic| %> - - - - - - + + + + + <% end %> diff --git a/app/views/messages/_form.rhtml b/app/views/messages/_form.rhtml index 25d88cd44..c2f7fb569 100644 --- a/app/views/messages/_form.rhtml +++ b/app/views/messages/_form.rhtml @@ -3,7 +3,13 @@


-<%= f.text_field :subject, :required => true, :size => 120 %>

+<%= f.text_field :subject, :required => true, :size => 120 %> + +<% if User.current.allowed_to?(:edit_messages, @project) %> + + +<% end %> +

<%= f.text_area :content, :required => true, :cols => 80, :rows => 15, :class => 'wiki-edit', :id => 'message_content' %>

<%= wikitoolbar_for 'message_content' %> diff --git a/app/views/messages/edit.rhtml b/app/views/messages/edit.rhtml new file mode 100644 index 000000000..808b6ea27 --- /dev/null +++ b/app/views/messages/edit.rhtml @@ -0,0 +1,6 @@ +

<%= link_to h(@board.name), :controller => 'boards', :action => 'show', :project_id => @project, :id => @board %> » <%=h @message.subject %>

+ +<% form_for :message, @message, :url => {:action => 'edit'}, :html => {:multipart => true} do |f| %> + <%= render :partial => 'form', :locals => {:f => f} %> + <%= submit_tag l(:button_save) %> +<% end %> diff --git a/app/views/messages/show.rhtml b/app/views/messages/show.rhtml index e39c09d50..bb7e2b7f3 100644 --- a/app/views/messages/show.rhtml +++ b/app/views/messages/show.rhtml @@ -1,28 +1,37 @@ -

<%= link_to h(@board.name), :controller => 'boards', :action => 'show', :project_id => @project, :id => @board %> » <%=h @message.subject %>

+
+ <%= link_to_if_authorized l(:button_edit), {:action => 'edit', :id => @topic}, :class => 'icon icon-edit' %> + <%= link_to_if_authorized l(:button_delete), {:action => 'destroy', :id => @topic}, :method => :post, :confirm => l(:text_are_you_sure), :class => 'icon icon-del' %> +
+ +

<%= link_to h(@board.name), :controller => 'boards', :action => 'show', :project_id => @project, :id => @board %> » <%=h @topic.subject %>

-

<%= authoring @message.created_on, @message.author %>

+

<%= authoring @topic.created_on, @topic.author %>

-<%= textilizable(@message.content, :attachments => @message.attachments) %> +<%= textilizable(@topic.content, :attachments => @topic.attachments) %>
-<%= link_to_attachments @message.attachments, :no_author => true %> +<%= link_to_attachments @topic.attachments, :no_author => true %>

-

<%= l(:label_reply_plural) %>

-<% @message.children.each do |message| %> +<% @topic.children.each do |message| %> "> +
+ <%= link_to_if_authorized l(:button_edit), {:action => 'edit', :id => message}, :class => 'icon icon-edit' %> + <%= link_to_if_authorized l(:button_delete), {:action => 'destroy', :id => message}, :method => :post, :confirm => l(:text_are_you_sure), :class => 'icon icon-del' %> +
+

<%=h message.subject %> - <%= authoring message.created_on, message.author %>

<%= textilizable message.content %>
<%= link_to_attachments message.attachments, :no_author => true %> +
<% end %> -
-<% if authorize_for('messages', 'reply') %> +<% if !@topic.locked? && authorize_for('messages', 'reply') %>

<%= toggle_link l(:button_reply), "reply", :focus => 'message_content' %>

<%= l(:field_subject) %> <%= l(:field_author) %>
<%= link_to h(topic.subject), :controller => 'messages', :action => 'show', :board_id => @board, :id => topic %><%= link_to_user topic.author %><%= format_time(topic.created_on) %><%= topic.replies_count %> - +
<%= link_to h(topic.subject), { :controller => 'messages', :action => 'show', :board_id => @board, :id => topic }, :class => 'icon' %><%= topic.author %><%= format_time(topic.created_on) %><%= topic.replies_count %> <% if topic.last_reply %> - <%= topic.last_reply.author.name %>, <%= format_time(topic.last_reply.created_on) %>
+ <%= authoring topic.last_reply.created_on, topic.last_reply.author %>
<%= link_to_message topic.last_reply %> <% end %> -