@@ -0,0 +1,11 | |||
|
1 | class AddWikiRedirectsRedirectsToWikiId < ActiveRecord::Migration | |
|
2 | def self.up | |
|
3 | add_column :wiki_redirects, :redirects_to_wiki_id, :integer | |
|
4 | WikiRedirect.update_all "redirects_to_wiki_id = wiki_id" | |
|
5 | change_column :wiki_redirects, :redirects_to_wiki_id, :integer, :null => false | |
|
6 | end | |
|
7 | ||
|
8 | def self.down | |
|
9 | remove_column :wiki_redirects, :redirects_to_wiki_id | |
|
10 | end | |
|
11 | end |
@@ -198,9 +198,10 class WikiController < ApplicationController | |||
|
198 | 198 | @page.redirect_existing_links = true |
|
199 | 199 | # used to display the *original* title if some AR validation errors occur |
|
200 | 200 | @original_title = @page.pretty_title |
|
201 |
|
|
|
201 | @page.safe_attributes = params[:wiki_page] | |
|
202 | if request.post? && @page.save | |
|
202 | 203 | flash[:notice] = l(:notice_successful_update) |
|
203 | redirect_to project_wiki_page_path(@project, @page.title) | |
|
204 | redirect_to project_wiki_page_path(@page.project, @page.title) | |
|
204 | 205 | end |
|
205 | 206 | end |
|
206 | 207 | |
@@ -344,7 +345,11 private | |||
|
344 | 345 | end |
|
345 | 346 | |
|
346 | 347 | def redirect_to_page(page) |
|
347 | redirect_to :action => action_name, :project_id => page.wiki.project, :id => page.title | |
|
348 | if page.project && page.project.visible? | |
|
349 | redirect_to :action => action_name, :project_id => page.project, :id => page.title | |
|
350 | else | |
|
351 | render_404 | |
|
352 | end | |
|
348 | 353 | end |
|
349 | 354 | |
|
350 | 355 | # Returns true if the current user is allowed to edit the page, otherwise false |
@@ -35,6 +35,16 module WikiHelper | |||
|
35 | 35 | s |
|
36 | 36 | end |
|
37 | 37 | |
|
38 | def wiki_page_wiki_options_for_select(page) | |
|
39 | projects = Project.allowed_to(:rename_wiki_pages).joins(:wiki).preload(:wiki).to_a | |
|
40 | projects << page.project unless projects.include?(page.project) | |
|
41 | ||
|
42 | project_tree_options_for_select(projects, :selected => page.project) do |project| | |
|
43 | wiki_id = project.wiki.try(:id) | |
|
44 | {:value => wiki_id, :selected => wiki_id == page.wiki_id} | |
|
45 | end | |
|
46 | end | |
|
47 | ||
|
38 | 48 | def wiki_page_breadcrumb(page) |
|
39 | 49 | breadcrumb(page.ancestors.reverse.collect {|parent| |
|
40 | 50 | link_to(h(parent.pretty_title), {:controller => 'wiki', :action => 'show', :id => parent.title, :project_id => parent.project, :version => nil}) |
@@ -19,7 +19,7 class Wiki < ActiveRecord::Base | |||
|
19 | 19 | include Redmine::SafeAttributes |
|
20 | 20 | belongs_to :project |
|
21 | 21 | has_many :pages, lambda {order('title')}, :class_name => 'WikiPage', :dependent => :destroy |
|
22 |
has_many :redirects, :class_name => 'WikiRedirect' |
|
|
22 | has_many :redirects, :class_name => 'WikiRedirect' | |
|
23 | 23 | |
|
24 | 24 | acts_as_watchable |
|
25 | 25 | |
@@ -27,6 +27,8 class Wiki < ActiveRecord::Base | |||
|
27 | 27 | validates_format_of :start_page, :with => /\A[^,\.\/\?\;\|\:]*\z/ |
|
28 | 28 | attr_protected :id |
|
29 | 29 | |
|
30 | before_destroy :delete_redirects | |
|
31 | ||
|
30 | 32 | safe_attributes 'start_page' |
|
31 | 33 | |
|
32 | 34 | def visible?(user=User.current) |
@@ -52,11 +54,11 class Wiki < ActiveRecord::Base | |||
|
52 | 54 | title = start_page if title.blank? |
|
53 | 55 | title = Wiki.titleize(title) |
|
54 | 56 | page = pages.where("LOWER(title) = LOWER(?)", title).first |
|
55 |
if |
|
|
57 | if page.nil? && options[:with_redirect] != false | |
|
56 | 58 | # search for a redirect |
|
57 | 59 | redirect = redirects.where("LOWER(title) = LOWER(?)", title).first |
|
58 | 60 | if redirect |
|
59 | page = find_page(redirect.redirects_to, :with_redirect => false) | |
|
61 | page = redirect.target_page | |
|
60 | 62 | @page_found_with_redirect = true |
|
61 | 63 | end |
|
62 | 64 | end |
@@ -68,6 +70,12 class Wiki < ActiveRecord::Base | |||
|
68 | 70 | @page_found_with_redirect |
|
69 | 71 | end |
|
70 | 72 | |
|
73 | # Deletes all redirects from/to the wiki | |
|
74 | def delete_redirects | |
|
75 | WikiRedirect.where(:wiki_id => id).delete_all | |
|
76 | WikiRedirect.where(:redirects_to_wiki_id => id).delete_all | |
|
77 | end | |
|
78 | ||
|
71 | 79 | # Finds a page by title |
|
72 | 80 | # The given string can be of one of the forms: "title" or "project:title" |
|
73 | 81 | # Examples: |
@@ -46,8 +46,9 class WikiPage < ActiveRecord::Base | |||
|
46 | 46 | attr_protected :id |
|
47 | 47 | |
|
48 | 48 | validate :validate_parent_title |
|
49 |
before_destroy : |
|
|
50 |
before_save |
|
|
49 | before_destroy :delete_redirects | |
|
50 | before_save :handle_rename_or_move | |
|
51 | after_save :handle_children_move | |
|
51 | 52 | |
|
52 | 53 | # eager load information about last updates, without loading text |
|
53 | 54 | scope :with_updated_on, lambda { |
@@ -58,7 +59,7 class WikiPage < ActiveRecord::Base | |||
|
58 | 59 | # Wiki pages that are protected by default |
|
59 | 60 | DEFAULT_PROTECTED_PAGES = %w(sidebar) |
|
60 | 61 | |
|
61 | safe_attributes 'parent_id', 'parent_title', | |
|
62 | safe_attributes 'parent_id', 'parent_title', 'title', 'redirect_existing_links', 'wiki_id', | |
|
62 | 63 | :if => lambda {|page, user| page.new_record? || user.allowed_to?(:rename_wiki_pages, page.project)} |
|
63 | 64 | |
|
64 | 65 | def initialize(attributes=nil, *args) |
@@ -74,30 +75,67 class WikiPage < ActiveRecord::Base | |||
|
74 | 75 | |
|
75 | 76 | def title=(value) |
|
76 | 77 | value = Wiki.titleize(value) |
|
77 | @previous_title = read_attribute(:title) if @previous_title.blank? | |
|
78 | 78 | write_attribute(:title, value) |
|
79 | 79 | end |
|
80 | 80 | |
|
81 | def handle_redirects | |
|
82 | self.title = Wiki.titleize(title) | |
|
83 | # Manage redirects if the title has changed | |
|
84 | if !@previous_title.blank? && (@previous_title != title) && !new_record? | |
|
81 | def safe_attributes=(attrs, user=User.current) | |
|
82 | return unless attrs.is_a?(Hash) | |
|
83 | attrs = attrs.deep_dup | |
|
84 | ||
|
85 | # Project and Tracker must be set before since new_statuses_allowed_to depends on it. | |
|
86 | if (w_id = attrs.delete('wiki_id')) && safe_attribute?('wiki_id') | |
|
87 | if (w = Wiki.find_by_id(w_id)) && w.project && user.allowed_to?(:rename_wiki_pages, w.project) | |
|
88 | self.wiki = w | |
|
89 | end | |
|
90 | end | |
|
91 | ||
|
92 | super attrs, user | |
|
93 | end | |
|
94 | ||
|
95 | # Manages redirects if page is renamed or moved | |
|
96 | def handle_rename_or_move | |
|
97 | if !new_record? && (title_changed? || wiki_id_changed?) | |
|
85 | 98 | # Update redirects that point to the old title |
|
86 |
|
|
|
99 | WikiRedirect.where(:redirects_to => title_was, :redirects_to_wiki_id => wiki_id_was).each do |r| | |
|
87 | 100 | r.redirects_to = title |
|
88 | r.title == r.redirects_to ? r.destroy : r.save | |
|
101 | r.redirects_to_wiki_id = wiki_id | |
|
102 | (r.title == r.redirects_to && r.wiki_id == r.redirects_to_wiki_id) ? r.destroy : r.save | |
|
89 | 103 | end |
|
90 | 104 | # Remove redirects for the new title |
|
91 |
|
|
|
105 | WikiRedirect.where(:wiki_id => wiki_id, :title => title).delete_all | |
|
92 | 106 | # Create a redirect to the new title |
|
93 | wiki.redirects << WikiRedirect.new(:title => @previous_title, :redirects_to => title) unless redirect_existing_links == "0" | |
|
94 | @previous_title = nil | |
|
107 | unless redirect_existing_links == "0" | |
|
108 | WikiRedirect.create( | |
|
109 | :wiki_id => wiki_id_was, :title => title_was, | |
|
110 | :redirects_to_wiki_id => wiki_id, :redirects_to => title | |
|
111 | ) | |
|
112 | end | |
|
113 | end | |
|
114 | if !new_record? && wiki_id_changed? && parent.present? | |
|
115 | unless parent.wiki_id == wiki_id | |
|
116 | self.parent_id = nil | |
|
117 | end | |
|
95 | 118 | end |
|
96 | 119 | end |
|
120 | private :handle_rename_or_move | |
|
121 | ||
|
122 | # Moves child pages if page was moved | |
|
123 | def handle_children_move | |
|
124 | if !new_record? && wiki_id_changed? | |
|
125 | children.each do |child| | |
|
126 | child.wiki_id = wiki_id | |
|
127 | child.redirect_existing_links = redirect_existing_links | |
|
128 | unless child.save | |
|
129 | WikiPage.where(:id => child.id).update_all :parent_nil => nil | |
|
130 | end | |
|
131 | end | |
|
132 | end | |
|
133 | end | |
|
134 | private :handle_children_move | |
|
97 | 135 | |
|
98 | def remove_redirects | |
|
99 | # Remove redirects to this page | |
|
100 | wiki.redirects.where(:redirects_to => title).each(&:destroy) | |
|
136 | # Deletes redirects to this page | |
|
137 | def delete_redirects | |
|
138 | WikiRedirect.where(:redirects_to_wiki_id => wiki_id, :redirects_to => title).delete_all | |
|
101 | 139 | end |
|
102 | 140 | |
|
103 | 141 | def pretty_title |
@@ -136,7 +174,7 class WikiPage < ActiveRecord::Base | |||
|
136 | 174 | end |
|
137 | 175 | |
|
138 | 176 | def project |
|
139 | wiki.project | |
|
177 | wiki.try(:project) | |
|
140 | 178 | end |
|
141 | 179 | |
|
142 | 180 | def text |
@@ -196,7 +234,9 class WikiPage < ActiveRecord::Base | |||
|
196 | 234 | def validate_parent_title |
|
197 | 235 | errors.add(:parent_title, :invalid) if !@parent_title.blank? && parent.nil? |
|
198 | 236 | errors.add(:parent_title, :circular_dependency) if parent && (parent == self || parent.ancestors.include?(self)) |
|
199 |
|
|
|
237 | if parent_id_changed? && parent && (parent.wiki_id != wiki_id) | |
|
238 | errors.add(:parent_title, :not_same_project) | |
|
239 | end | |
|
200 | 240 | end |
|
201 | 241 | end |
|
202 | 242 |
@@ -18,7 +18,22 | |||
|
18 | 18 | class WikiRedirect < ActiveRecord::Base |
|
19 | 19 | belongs_to :wiki |
|
20 | 20 | |
|
21 | validates_presence_of :title, :redirects_to | |
|
21 | validates_presence_of :wiki_id, :title, :redirects_to | |
|
22 | 22 | validates_length_of :title, :redirects_to, :maximum => 255 |
|
23 | 23 | attr_protected :id |
|
24 | ||
|
25 | before_save :set_redirects_to_wiki_id | |
|
26 | ||
|
27 | def target_page | |
|
28 | wiki = Wiki.find_by_id(redirects_to_wiki_id) | |
|
29 | if wiki | |
|
30 | wiki.find_page(redirects_to, :with_redirect => false) | |
|
31 | end | |
|
32 | end | |
|
33 | ||
|
34 | private | |
|
35 | ||
|
36 | def set_redirects_to_wiki_id | |
|
37 | self.redirects_to_wiki_id ||= wiki_id | |
|
38 | end | |
|
24 | 39 | end |
@@ -16,6 +16,11 | |||
|
16 | 16 | @wiki.pages.includes(:parent).to_a - @page.self_and_descendants, |
|
17 | 17 | @page.parent), |
|
18 | 18 | :label => :field_parent_title %></p> |
|
19 | ||
|
20 | <% if @page.safe_attribute? 'wiki_id' %> | |
|
21 | <p><%= f.select :wiki_id, wiki_page_wiki_options_for_select(@page), :label => :label_project %></p> | |
|
22 | <% end %> | |
|
23 | ||
|
19 | 24 | </div> |
|
20 | 25 | <%= submit_tag l(:button_rename) %> |
|
21 | 26 | <% end %> |
@@ -660,6 +660,39 class WikiControllerTest < ActionController::TestCase | |||
|
660 | 660 | assert_nil WikiPage.find_by_title('Child_1').parent |
|
661 | 661 | end |
|
662 | 662 | |
|
663 | def test_get_rename_should_show_target_projects_list | |
|
664 | @request.session[:user_id] = 2 | |
|
665 | project = Project.find(5) | |
|
666 | project.enable_module! :wiki | |
|
667 | ||
|
668 | get :rename, :project_id => 1, :id => 'Another_page' | |
|
669 | assert_response :success | |
|
670 | assert_template 'rename' | |
|
671 | ||
|
672 | assert_select 'select[name=?]', 'wiki_page[wiki_id]' do | |
|
673 | assert_select 'option', 2 | |
|
674 | assert_select 'option[value=?][selected=selected]', '1', :text => /eCookbook/ | |
|
675 | assert_select 'option[value=?]', project.wiki.id.to_s, :text => /#{project.name}/ | |
|
676 | end | |
|
677 | end | |
|
678 | ||
|
679 | def test_rename_with_move | |
|
680 | @request.session[:user_id] = 2 | |
|
681 | project = Project.find(5) | |
|
682 | project.enable_module! :wiki | |
|
683 | ||
|
684 | post :rename, :project_id => 1, :id => 'Another_page', | |
|
685 | :wiki_page => { | |
|
686 | :wiki_id => project.wiki.id.to_s, | |
|
687 | :title => 'Another renamed page', | |
|
688 | :redirect_existing_links => 1 | |
|
689 | } | |
|
690 | assert_redirected_to '/projects/private-child/wiki/Another_renamed_page' | |
|
691 | ||
|
692 | page = WikiPage.find(2) | |
|
693 | assert_equal project.wiki.id, page.wiki_id | |
|
694 | end | |
|
695 | ||
|
663 | 696 | def test_destroy_a_page_without_children_should_not_ask_confirmation |
|
664 | 697 | @request.session[:user_id] = 2 |
|
665 | 698 | delete :destroy, :project_id => 1, :id => 'Child_2' |
@@ -101,6 +101,26 class WikiPageTest < ActiveSupport::TestCase | |||
|
101 | 101 | assert page.save |
|
102 | 102 | end |
|
103 | 103 | |
|
104 | def test_move_child_should_clear_parent | |
|
105 | parent = WikiPage.create!(:wiki_id => 1, :title => 'Parent') | |
|
106 | child = WikiPage.create!(:wiki_id => 1, :title => 'Child', :parent => parent) | |
|
107 | ||
|
108 | child.wiki_id = 2 | |
|
109 | child.save! | |
|
110 | assert_equal nil, child.reload.parent_id | |
|
111 | end | |
|
112 | ||
|
113 | def test_move_parent_should_move_child_page | |
|
114 | parent = WikiPage.create!(:wiki_id => 1, :title => 'Parent') | |
|
115 | child = WikiPage.create!(:wiki_id => 1, :title => 'Child', :parent => parent) | |
|
116 | parent.reload | |
|
117 | ||
|
118 | parent.wiki_id = 2 | |
|
119 | parent.save! | |
|
120 | assert_equal 2, child.reload.wiki_id | |
|
121 | assert_equal parent, child.parent | |
|
122 | end | |
|
123 | ||
|
104 | 124 | def test_destroy |
|
105 | 125 | page = WikiPage.find(1) |
|
106 | 126 | page.destroy |
@@ -25,15 +25,38 class WikiRedirectTest < ActiveSupport::TestCase | |||
|
25 | 25 | @original = WikiPage.create(:wiki => @wiki, :title => 'Original title') |
|
26 | 26 | end |
|
27 | 27 | |
|
28 | def test_create_redirect | |
|
28 | def test_create_redirect_on_rename | |
|
29 | 29 | @original.title = 'New title' |
|
30 |
|
|
|
31 | @original.reload | |
|
30 | @original.save! | |
|
32 | 31 | |
|
33 | assert_equal 'New_title', @original.title | |
|
34 | assert @wiki.redirects.find_by_title('Original_title') | |
|
35 | assert @wiki.find_page('Original title') | |
|
36 | assert @wiki.find_page('ORIGINAL title') | |
|
32 | redirect = @wiki.redirects.find_by_title('Original_title') | |
|
33 | assert_not_nil redirect | |
|
34 | assert_equal 1, redirect.redirects_to_wiki_id | |
|
35 | assert_equal 'New_title', redirect.redirects_to | |
|
36 | assert_equal @original, redirect.target_page | |
|
37 | end | |
|
38 | ||
|
39 | def test_create_redirect_on_move | |
|
40 | @original.wiki_id = 2 | |
|
41 | @original.save! | |
|
42 | ||
|
43 | redirect = @wiki.redirects.find_by_title('Original_title') | |
|
44 | assert_not_nil redirect | |
|
45 | assert_equal 2, redirect.redirects_to_wiki_id | |
|
46 | assert_equal 'Original_title', redirect.redirects_to | |
|
47 | assert_equal @original, redirect.target_page | |
|
48 | end | |
|
49 | ||
|
50 | def test_create_redirect_on_rename_and_move | |
|
51 | @original.title = 'New title' | |
|
52 | @original.wiki_id = 2 | |
|
53 | @original.save! | |
|
54 | ||
|
55 | redirect = @wiki.redirects.find_by_title('Original_title') | |
|
56 | assert_not_nil redirect | |
|
57 | assert_equal 2, redirect.redirects_to_wiki_id | |
|
58 | assert_equal 'New_title', redirect.redirects_to | |
|
59 | assert_equal @original, redirect.target_page | |
|
37 | 60 | end |
|
38 | 61 | |
|
39 | 62 | def test_update_redirect |
@@ -97,4 +97,18 class WikiTest < ActiveSupport::TestCase | |||
|
97 | 97 | assert_kind_of WikiPage, @wiki.sidebar |
|
98 | 98 | assert_equal 'Sidebar', @wiki.sidebar.title |
|
99 | 99 | end |
|
100 | ||
|
101 | def test_destroy_should_remove_redirects_from_the_wiki | |
|
102 | WikiRedirect.create!(:wiki_id => 1, :title => 'Foo', :redirects_to_wiki_id => 2, :redirects_to => 'Bar') | |
|
103 | ||
|
104 | Wiki.find(1).destroy | |
|
105 | assert_equal 0, WikiRedirect.where(:wiki_id => 1).count | |
|
106 | end | |
|
107 | ||
|
108 | def test_destroy_should_remove_redirects_to_the_wiki | |
|
109 | WikiRedirect.create!(:wiki_id => 2, :title => 'Foo', :redirects_to_wiki_id => 1, :redirects_to => 'Bar') | |
|
110 | ||
|
111 | Wiki.find(1).destroy | |
|
112 | assert_equal 0, WikiRedirect.where(:redirects_to_wiki_id => 1).count | |
|
113 | end | |
|
100 | 114 | end |
General Comments 0
You need to be logged in to leave comments.
Login now