]> source.dussan.org Git - redmine.git/commitdiff
Move wiki page to other project (#5450).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 23 Nov 2014 13:36:01 +0000 (13:36 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 23 Nov 2014 13:36:01 +0000 (13:36 +0000)
git-svn-id: http://svn.redmine.org/redmine/trunk@13643 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/controllers/wiki_controller.rb
app/helpers/wiki_helper.rb
app/models/wiki.rb
app/models/wiki_page.rb
app/models/wiki_redirect.rb
app/views/wiki/rename.html.erb
db/migrate/20141122124142_add_wiki_redirects_redirects_to_wiki_id.rb [new file with mode: 0644]
test/functional/wiki_controller_test.rb
test/unit/wiki_page_test.rb
test/unit/wiki_redirect_test.rb
test/unit/wiki_test.rb

index 9b3981655972d1fde4e985d0490ece91cf6269ae..5ce55f2ce0009633fadbb0f6090dcda0b4cc6202 100644 (file)
@@ -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
index 1e15c81fa2c07b002cb781c73d4c4ae00a647f74..21b50fd6bab2919e7f9ce4ef6965cc75d0b661c3 100644 (file)
@@ -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})
index b1ce2974332a26e5366001be72403e1cb5454d33..ce06a2a6e8df4becd84e4737c9c357061327dd4a 100644 (file)
@@ -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:
index 8aed835adc159dccea7a9f3388295cb39cab96e3..9d68de2b6ae464e15b724904108f72451f2f0c01 100644 (file)
@@ -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
 
index 1664531384251cbd749382ce42512b2ef6bdf8de..3c4f08f3d03200fbb93d6b46389aec282e157a58 100644 (file)
 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
index 85319520d037e80f165b6b0df56429673fd37f0d..c87641faffede5eccee1c27b65448ca2ed7e9d3f 100644 (file)
                     @wiki.pages.includes(:parent).to_a - @page.self_and_descendants,
                     @page.parent),
                 :label => :field_parent_title %></p>
+
+<% if @page.safe_attribute? 'wiki_id' %>
+<p><%= f.select :wiki_id, wiki_page_wiki_options_for_select(@page), :label => :label_project %></p>
+<% end %>
+
 </div>
 <%= 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 (file)
index 0000000..6dfbd0e
--- /dev/null
@@ -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
index d16a1559cdd9e6901a2397485ef195ba6d7cb957..ebebf343d29e5d4abf9c265e794aefdc63326165 100644 (file)
@@ -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'
index a8c56678d68e6346c621f774a8c68334312431ed..d3453583c2fcfe9a9946d8b6af7812081c97c186 100644 (file)
@@ -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
index 3873da600590deaf1931f7a8aae757601d6b7fa1..f8fffbbd7428f71855450b75ca8daa7f14bcf391 100644 (file)
@@ -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
index a852b23937f0bf5cb24f147b45ae3e06d15867ef..22573221b0d368c0a605d2da37825fd5e2cf5fcb 100644 (file)
@@ -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