From ca166b30e1b223c7f3aba3befc894a8597362592 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Lang Date: Sun, 5 Apr 2009 10:08:11 +0000 Subject: [PATCH] Adds the ability to move threads between project forums (#2452). 'Edit message' permission is required. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2649 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/boards_controller.rb | 1 + app/controllers/messages_controller.rb | 3 ++- app/models/board.rb | 13 ++++++++++ app/models/message.rb | 21 ++++++++-------- app/views/messages/_form.rhtml | 5 ++++ test/fixtures/boards.yml | 4 +-- test/unit/message_test.rb | 34 ++++++++++++++++++++++++++ 7 files changed, 68 insertions(+), 13 deletions(-) diff --git a/app/controllers/boards_controller.rb b/app/controllers/boards_controller.rb index 838e63a7c..8d53f81e4 100644 --- a/app/controllers/boards_controller.rb +++ b/app/controllers/boards_controller.rb @@ -46,6 +46,7 @@ class BoardsController < ApplicationController :include => [:author, {:last_reply => :author}], :limit => @topic_pages.items_per_page, :offset => @topic_pages.current.offset + @message = Message.new render :action => 'show', :layout => !request.xhr? end diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index af39efb21..9a7c34c37 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -73,7 +73,8 @@ class MessagesController < ApplicationController if request.post? && @message.update_attributes(params[:message]) attach_files(@message, params[:attachments]) flash[:notice] = l(:notice_successful_update) - redirect_to :action => 'show', :id => @topic + @message.reload + redirect_to :action => 'show', :board_id => @message.board, :id => @message.root end end diff --git a/app/models/board.rb b/app/models/board.rb index 26e2004d3..3bc18efec 100644 --- a/app/models/board.rb +++ b/app/models/board.rb @@ -26,4 +26,17 @@ class Board < ActiveRecord::Base validates_presence_of :name, :description validates_length_of :name, :maximum => 30 validates_length_of :description, :maximum => 255 + + def reset_counters! + self.class.reset_counters!(id) + end + + # Updates topics_count, messages_count and last_message_id attributes for +board_id+ + def self.reset_counters!(board_id) + board_id = board_id.to_i + update_all("topics_count = (SELECT COUNT(*) FROM #{Message.table_name} WHERE board_id=#{board_id} AND parent_id IS NULL)," + + " messages_count = (SELECT COUNT(*) FROM #{Message.table_name} WHERE board_id=#{board_id})," + + " last_message_id = (SELECT MAX(id) FROM #{Message.table_name} WHERE board_id=#{board_id})", + ["id = ?", board_id]) + end end diff --git a/app/models/message.rb b/app/models/message.rb index 716c53b0b..92bcb1275 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -37,7 +37,7 @@ class Message < ActiveRecord::Base acts_as_watchable attr_protected :locked, :sticky - validates_presence_of :subject, :content + validates_presence_of :board, :subject, :content validates_length_of :subject, :maximum => 255 after_create :add_author_as_watcher @@ -48,21 +48,22 @@ class Message < ActiveRecord::Base end def after_create - board.update_attribute(:last_message_id, self.id) - board.increment! :messages_count if parent parent.reload.update_attribute(:last_reply_id, self.id) - else - board.increment! :topics_count + end + board.reset_counters! + end + + def after_update + if board_id_changed? + Message.update_all("board_id = #{board_id}", ["id = ? OR parent_id = ?", root.id, root.id]) + Board.reset_counters!(board_id_was) + Board.reset_counters!(board_id) 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 + board.reset_counters! end def sticky? diff --git a/app/views/messages/_form.rhtml b/app/views/messages/_form.rhtml index 540811ec3..ab483bd5d 100644 --- a/app/views/messages/_form.rhtml +++ b/app/views/messages/_form.rhtml @@ -12,6 +12,11 @@ <% end %>

+<% if !replying && !@message.new_record? && User.current.allowed_to?(:edit_messages, @project) %> +


+ <%= f.select :board_id, @project.boards.collect {|b| [b.name, b.id]} %>

+<% end %> +

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

<%= wikitoolbar_for 'message_content' %> diff --git a/test/fixtures/boards.yml b/test/fixtures/boards.yml index b6b42aaa3..a4795c21e 100644 --- a/test/fixtures/boards.yml +++ b/test/fixtures/boards.yml @@ -6,8 +6,8 @@ boards_001: id: 1 description: Help board position: 1 - last_message_id: 5 - messages_count: 5 + last_message_id: 6 + messages_count: 6 boards_002: name: Discussion project_id: 1 diff --git a/test/unit/message_test.rb b/test/unit/message_test.rb index b907cfef3..bc9bd5fd3 100644 --- a/test/unit/message_test.rb +++ b/test/unit/message_test.rb @@ -1,3 +1,20 @@ +# Redmine - project management software +# Copyright (C) 2006-2009 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + require File.dirname(__FILE__) + '/../test_helper' class MessageTest < Test::Unit::TestCase @@ -47,6 +64,23 @@ class MessageTest < Test::Unit::TestCase assert @message.watched_by?(reply_author) end + def test_moving_message_should_update_counters + @message = Message.find(1) + assert_no_difference 'Message.count' do + # Previous board + assert_difference 'Board.find(1).topics_count', -1 do + assert_difference 'Board.find(1).messages_count', -(1 + @message.replies_count) do + # New board + assert_difference 'Board.find(2).topics_count' do + assert_difference 'Board.find(2).messages_count', (1 + @message.replies_count) do + @message.update_attributes(:board_id => 2) + end + end + end + end + end + end + def test_destroy_topic message = Message.find(1) board = message.board