From e0a034164f3409336f686cfa381da8a5db15c40e Mon Sep 17 00:00:00 2001 From: Jean-Philippe Lang Date: Sun, 23 Nov 2014 13:36:01 +0000 Subject: [PATCH] Move wiki page to other project (#5450). git-svn-id: http://svn.redmine.org/redmine/trunk@13643 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/wiki_controller.rb | 11 ++- app/helpers/wiki_helper.rb | 10 +++ app/models/wiki.rb | 14 +++- app/models/wiki_page.rb | 76 ++++++++++++++----- app/models/wiki_redirect.rb | 17 ++++- app/views/wiki/rename.html.erb | 5 ++ ...add_wiki_redirects_redirects_to_wiki_id.rb | 11 +++ test/functional/wiki_controller_test.rb | 33 ++++++++ test/unit/wiki_page_test.rb | 20 +++++ test/unit/wiki_redirect_test.rb | 37 +++++++-- test/unit/wiki_test.rb | 14 ++++ 11 files changed, 216 insertions(+), 32 deletions(-) create mode 100644 db/migrate/20141122124142_add_wiki_redirects_redirects_to_wiki_id.rb diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index 9b3981655..5ce55f2ce 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -198,9 +198,10 @@ class WikiController < ApplicationController @page.redirect_existing_links = true # used to display the *original* title if some AR validation errors occur @original_title = @page.pretty_title - if request.post? && @page.update_attributes(params[:wiki_page]) + @page.safe_attributes = params[:wiki_page] + if request.post? && @page.save flash[:notice] = l(:notice_successful_update) - redirect_to project_wiki_page_path(@project, @page.title) + redirect_to project_wiki_page_path(@page.project, @page.title) end end @@ -344,7 +345,11 @@ private end def redirect_to_page(page) - redirect_to :action => action_name, :project_id => page.wiki.project, :id => page.title + if page.project && page.project.visible? + redirect_to :action => action_name, :project_id => page.project, :id => page.title + else + render_404 + end end # Returns true if the current user is allowed to edit the page, otherwise false diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb index 1e15c81fa..21b50fd6b 100644 --- a/app/helpers/wiki_helper.rb +++ b/app/helpers/wiki_helper.rb @@ -35,6 +35,16 @@ module WikiHelper s end + def wiki_page_wiki_options_for_select(page) + projects = Project.allowed_to(:rename_wiki_pages).joins(:wiki).preload(:wiki).to_a + projects << page.project unless projects.include?(page.project) + + project_tree_options_for_select(projects, :selected => page.project) do |project| + wiki_id = project.wiki.try(:id) + {:value => wiki_id, :selected => wiki_id == page.wiki_id} + end + end + def wiki_page_breadcrumb(page) breadcrumb(page.ancestors.reverse.collect {|parent| link_to(h(parent.pretty_title), {:controller => 'wiki', :action => 'show', :id => parent.title, :project_id => parent.project, :version => nil}) diff --git a/app/models/wiki.rb b/app/models/wiki.rb index b1ce29743..ce06a2a6e 100644 --- a/app/models/wiki.rb +++ b/app/models/wiki.rb @@ -19,7 +19,7 @@ class Wiki < ActiveRecord::Base include Redmine::SafeAttributes belongs_to :project has_many :pages, lambda {order('title')}, :class_name => 'WikiPage', :dependent => :destroy - has_many :redirects, :class_name => 'WikiRedirect', :dependent => :delete_all + has_many :redirects, :class_name => 'WikiRedirect' acts_as_watchable @@ -27,6 +27,8 @@ class Wiki < ActiveRecord::Base validates_format_of :start_page, :with => /\A[^,\.\/\?\;\|\:]*\z/ attr_protected :id + before_destroy :delete_redirects + safe_attributes 'start_page' def visible?(user=User.current) @@ -52,11 +54,11 @@ class Wiki < ActiveRecord::Base title = start_page if title.blank? title = Wiki.titleize(title) page = pages.where("LOWER(title) = LOWER(?)", title).first - if !page && !(options[:with_redirect] == false) + if page.nil? && options[:with_redirect] != false # search for a redirect redirect = redirects.where("LOWER(title) = LOWER(?)", title).first if redirect - page = find_page(redirect.redirects_to, :with_redirect => false) + page = redirect.target_page @page_found_with_redirect = true end end @@ -68,6 +70,12 @@ class Wiki < ActiveRecord::Base @page_found_with_redirect end + # Deletes all redirects from/to the wiki + def delete_redirects + WikiRedirect.where(:wiki_id => id).delete_all + WikiRedirect.where(:redirects_to_wiki_id => id).delete_all + end + # Finds a page by title # The given string can be of one of the forms: "title" or "project:title" # Examples: diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 8aed835ad..9d68de2b6 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -46,8 +46,9 @@ class WikiPage < ActiveRecord::Base attr_protected :id validate :validate_parent_title - before_destroy :remove_redirects - before_save :handle_redirects + before_destroy :delete_redirects + before_save :handle_rename_or_move + after_save :handle_children_move # eager load information about last updates, without loading text scope :with_updated_on, lambda { @@ -58,7 +59,7 @@ class WikiPage < ActiveRecord::Base # Wiki pages that are protected by default DEFAULT_PROTECTED_PAGES = %w(sidebar) - safe_attributes 'parent_id', 'parent_title', + safe_attributes 'parent_id', 'parent_title', 'title', 'redirect_existing_links', 'wiki_id', :if => lambda {|page, user| page.new_record? || user.allowed_to?(:rename_wiki_pages, page.project)} def initialize(attributes=nil, *args) @@ -74,30 +75,67 @@ class WikiPage < ActiveRecord::Base def title=(value) value = Wiki.titleize(value) - @previous_title = read_attribute(:title) if @previous_title.blank? write_attribute(:title, value) end - def handle_redirects - self.title = Wiki.titleize(title) - # Manage redirects if the title has changed - if !@previous_title.blank? && (@previous_title != title) && !new_record? + def safe_attributes=(attrs, user=User.current) + return unless attrs.is_a?(Hash) + attrs = attrs.deep_dup + + # Project and Tracker must be set before since new_statuses_allowed_to depends on it. + if (w_id = attrs.delete('wiki_id')) && safe_attribute?('wiki_id') + if (w = Wiki.find_by_id(w_id)) && w.project && user.allowed_to?(:rename_wiki_pages, w.project) + self.wiki = w + end + end + + super attrs, user + end + + # Manages redirects if page is renamed or moved + def handle_rename_or_move + if !new_record? && (title_changed? || wiki_id_changed?) # Update redirects that point to the old title - wiki.redirects.where(:redirects_to => @previous_title).each do |r| + WikiRedirect.where(:redirects_to => title_was, :redirects_to_wiki_id => wiki_id_was).each do |r| r.redirects_to = title - r.title == r.redirects_to ? r.destroy : r.save + r.redirects_to_wiki_id = wiki_id + (r.title == r.redirects_to && r.wiki_id == r.redirects_to_wiki_id) ? r.destroy : r.save end # Remove redirects for the new title - wiki.redirects.where(:title => title).each(&:destroy) + WikiRedirect.where(:wiki_id => wiki_id, :title => title).delete_all # Create a redirect to the new title - wiki.redirects << WikiRedirect.new(:title => @previous_title, :redirects_to => title) unless redirect_existing_links == "0" - @previous_title = nil + unless redirect_existing_links == "0" + WikiRedirect.create( + :wiki_id => wiki_id_was, :title => title_was, + :redirects_to_wiki_id => wiki_id, :redirects_to => title + ) + end + end + if !new_record? && wiki_id_changed? && parent.present? + unless parent.wiki_id == wiki_id + self.parent_id = nil + end end end + private :handle_rename_or_move + + # Moves child pages if page was moved + def handle_children_move + if !new_record? && wiki_id_changed? + children.each do |child| + child.wiki_id = wiki_id + child.redirect_existing_links = redirect_existing_links + unless child.save + WikiPage.where(:id => child.id).update_all :parent_nil => nil + end + end + end + end + private :handle_children_move - def remove_redirects - # Remove redirects to this page - wiki.redirects.where(:redirects_to => title).each(&:destroy) + # Deletes redirects to this page + def delete_redirects + WikiRedirect.where(:redirects_to_wiki_id => wiki_id, :redirects_to => title).delete_all end def pretty_title @@ -136,7 +174,7 @@ class WikiPage < ActiveRecord::Base end def project - wiki.project + wiki.try(:project) end def text @@ -196,7 +234,9 @@ class WikiPage < ActiveRecord::Base def validate_parent_title errors.add(:parent_title, :invalid) if !@parent_title.blank? && parent.nil? errors.add(:parent_title, :circular_dependency) if parent && (parent == self || parent.ancestors.include?(self)) - errors.add(:parent_title, :not_same_project) if parent && (parent.wiki_id != wiki_id) + if parent_id_changed? && parent && (parent.wiki_id != wiki_id) + errors.add(:parent_title, :not_same_project) + end end end diff --git a/app/models/wiki_redirect.rb b/app/models/wiki_redirect.rb index 166453138..3c4f08f3d 100644 --- a/app/models/wiki_redirect.rb +++ b/app/models/wiki_redirect.rb @@ -18,7 +18,22 @@ class WikiRedirect < ActiveRecord::Base belongs_to :wiki - validates_presence_of :title, :redirects_to + validates_presence_of :wiki_id, :title, :redirects_to validates_length_of :title, :redirects_to, :maximum => 255 attr_protected :id + + before_save :set_redirects_to_wiki_id + + def target_page + wiki = Wiki.find_by_id(redirects_to_wiki_id) + if wiki + wiki.find_page(redirects_to, :with_redirect => false) + end + end + + private + + def set_redirects_to_wiki_id + self.redirects_to_wiki_id ||= wiki_id + end end diff --git a/app/views/wiki/rename.html.erb b/app/views/wiki/rename.html.erb index 85319520d..c87641faf 100644 --- a/app/views/wiki/rename.html.erb +++ b/app/views/wiki/rename.html.erb @@ -16,6 +16,11 @@ @wiki.pages.includes(:parent).to_a - @page.self_and_descendants, @page.parent), :label => :field_parent_title %>

+ +<% if @page.safe_attribute? 'wiki_id' %> +

<%= f.select :wiki_id, wiki_page_wiki_options_for_select(@page), :label => :label_project %>

+<% end %> + <%= submit_tag l(:button_rename) %> <% end %> diff --git a/db/migrate/20141122124142_add_wiki_redirects_redirects_to_wiki_id.rb b/db/migrate/20141122124142_add_wiki_redirects_redirects_to_wiki_id.rb new file mode 100644 index 000000000..6dfbd0e14 --- /dev/null +++ b/db/migrate/20141122124142_add_wiki_redirects_redirects_to_wiki_id.rb @@ -0,0 +1,11 @@ +class AddWikiRedirectsRedirectsToWikiId < ActiveRecord::Migration + def self.up + add_column :wiki_redirects, :redirects_to_wiki_id, :integer + WikiRedirect.update_all "redirects_to_wiki_id = wiki_id" + change_column :wiki_redirects, :redirects_to_wiki_id, :integer, :null => false + end + + def self.down + remove_column :wiki_redirects, :redirects_to_wiki_id + end +end diff --git a/test/functional/wiki_controller_test.rb b/test/functional/wiki_controller_test.rb index d16a1559c..ebebf343d 100644 --- a/test/functional/wiki_controller_test.rb +++ b/test/functional/wiki_controller_test.rb @@ -660,6 +660,39 @@ class WikiControllerTest < ActionController::TestCase assert_nil WikiPage.find_by_title('Child_1').parent end + def test_get_rename_should_show_target_projects_list + @request.session[:user_id] = 2 + project = Project.find(5) + project.enable_module! :wiki + + get :rename, :project_id => 1, :id => 'Another_page' + assert_response :success + assert_template 'rename' + + assert_select 'select[name=?]', 'wiki_page[wiki_id]' do + assert_select 'option', 2 + assert_select 'option[value=?][selected=selected]', '1', :text => /eCookbook/ + assert_select 'option[value=?]', project.wiki.id.to_s, :text => /#{project.name}/ + end + end + + def test_rename_with_move + @request.session[:user_id] = 2 + project = Project.find(5) + project.enable_module! :wiki + + post :rename, :project_id => 1, :id => 'Another_page', + :wiki_page => { + :wiki_id => project.wiki.id.to_s, + :title => 'Another renamed page', + :redirect_existing_links => 1 + } + assert_redirected_to '/projects/private-child/wiki/Another_renamed_page' + + page = WikiPage.find(2) + assert_equal project.wiki.id, page.wiki_id + end + def test_destroy_a_page_without_children_should_not_ask_confirmation @request.session[:user_id] = 2 delete :destroy, :project_id => 1, :id => 'Child_2' diff --git a/test/unit/wiki_page_test.rb b/test/unit/wiki_page_test.rb index a8c56678d..d3453583c 100644 --- a/test/unit/wiki_page_test.rb +++ b/test/unit/wiki_page_test.rb @@ -101,6 +101,26 @@ class WikiPageTest < ActiveSupport::TestCase assert page.save end + def test_move_child_should_clear_parent + parent = WikiPage.create!(:wiki_id => 1, :title => 'Parent') + child = WikiPage.create!(:wiki_id => 1, :title => 'Child', :parent => parent) + + child.wiki_id = 2 + child.save! + assert_equal nil, child.reload.parent_id + end + + def test_move_parent_should_move_child_page + parent = WikiPage.create!(:wiki_id => 1, :title => 'Parent') + child = WikiPage.create!(:wiki_id => 1, :title => 'Child', :parent => parent) + parent.reload + + parent.wiki_id = 2 + parent.save! + assert_equal 2, child.reload.wiki_id + assert_equal parent, child.parent + end + def test_destroy page = WikiPage.find(1) page.destroy diff --git a/test/unit/wiki_redirect_test.rb b/test/unit/wiki_redirect_test.rb index 3873da600..f8fffbbd7 100644 --- a/test/unit/wiki_redirect_test.rb +++ b/test/unit/wiki_redirect_test.rb @@ -25,15 +25,38 @@ class WikiRedirectTest < ActiveSupport::TestCase @original = WikiPage.create(:wiki => @wiki, :title => 'Original title') end - def test_create_redirect + def test_create_redirect_on_rename @original.title = 'New title' - assert @original.save - @original.reload + @original.save! - assert_equal 'New_title', @original.title - assert @wiki.redirects.find_by_title('Original_title') - assert @wiki.find_page('Original title') - assert @wiki.find_page('ORIGINAL title') + redirect = @wiki.redirects.find_by_title('Original_title') + assert_not_nil redirect + assert_equal 1, redirect.redirects_to_wiki_id + assert_equal 'New_title', redirect.redirects_to + assert_equal @original, redirect.target_page + end + + def test_create_redirect_on_move + @original.wiki_id = 2 + @original.save! + + redirect = @wiki.redirects.find_by_title('Original_title') + assert_not_nil redirect + assert_equal 2, redirect.redirects_to_wiki_id + assert_equal 'Original_title', redirect.redirects_to + assert_equal @original, redirect.target_page + end + + def test_create_redirect_on_rename_and_move + @original.title = 'New title' + @original.wiki_id = 2 + @original.save! + + redirect = @wiki.redirects.find_by_title('Original_title') + assert_not_nil redirect + assert_equal 2, redirect.redirects_to_wiki_id + assert_equal 'New_title', redirect.redirects_to + assert_equal @original, redirect.target_page end def test_update_redirect diff --git a/test/unit/wiki_test.rb b/test/unit/wiki_test.rb index a852b2393..22573221b 100644 --- a/test/unit/wiki_test.rb +++ b/test/unit/wiki_test.rb @@ -97,4 +97,18 @@ class WikiTest < ActiveSupport::TestCase assert_kind_of WikiPage, @wiki.sidebar assert_equal 'Sidebar', @wiki.sidebar.title end + + def test_destroy_should_remove_redirects_from_the_wiki + WikiRedirect.create!(:wiki_id => 1, :title => 'Foo', :redirects_to_wiki_id => 2, :redirects_to => 'Bar') + + Wiki.find(1).destroy + assert_equal 0, WikiRedirect.where(:wiki_id => 1).count + end + + def test_destroy_should_remove_redirects_to_the_wiki + WikiRedirect.create!(:wiki_id => 2, :title => 'Foo', :redirects_to_wiki_id => 1, :redirects_to => 'Bar') + + Wiki.find(1).destroy + assert_equal 0, WikiRedirect.where(:redirects_to_wiki_id => 1).count + end end -- 2.39.5