@@ -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 | @page.redirect_existing_links = true |
|
198 | @page.redirect_existing_links = true | |
199 | # used to display the *original* title if some AR validation errors occur |
|
199 | # used to display the *original* title if some AR validation errors occur | |
200 | @original_title = @page.pretty_title |
|
200 | @original_title = @page.pretty_title | |
201 |
|
|
201 | @page.safe_attributes = params[:wiki_page] | |
|
202 | if request.post? && @page.save | |||
202 | flash[:notice] = l(:notice_successful_update) |
|
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 | end |
|
205 | end | |
205 | end |
|
206 | end | |
206 |
|
207 | |||
@@ -344,7 +345,11 private | |||||
344 | end |
|
345 | end | |
345 |
|
346 | |||
346 | def redirect_to_page(page) |
|
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 | end |
|
353 | end | |
349 |
|
354 | |||
350 | # Returns true if the current user is allowed to edit the page, otherwise false |
|
355 | # Returns true if the current user is allowed to edit the page, otherwise false |
@@ -35,6 +35,16 module WikiHelper | |||||
35 | s |
|
35 | s | |
36 | end |
|
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 | def wiki_page_breadcrumb(page) |
|
48 | def wiki_page_breadcrumb(page) | |
39 | breadcrumb(page.ancestors.reverse.collect {|parent| |
|
49 | breadcrumb(page.ancestors.reverse.collect {|parent| | |
40 | link_to(h(parent.pretty_title), {:controller => 'wiki', :action => 'show', :id => parent.title, :project_id => parent.project, :version => nil}) |
|
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 | include Redmine::SafeAttributes |
|
19 | include Redmine::SafeAttributes | |
20 | belongs_to :project |
|
20 | belongs_to :project | |
21 | has_many :pages, lambda {order('title')}, :class_name => 'WikiPage', :dependent => :destroy |
|
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 | acts_as_watchable |
|
24 | acts_as_watchable | |
25 |
|
25 | |||
@@ -27,6 +27,8 class Wiki < ActiveRecord::Base | |||||
27 | validates_format_of :start_page, :with => /\A[^,\.\/\?\;\|\:]*\z/ |
|
27 | validates_format_of :start_page, :with => /\A[^,\.\/\?\;\|\:]*\z/ | |
28 | attr_protected :id |
|
28 | attr_protected :id | |
29 |
|
29 | |||
|
30 | before_destroy :delete_redirects | |||
|
31 | ||||
30 | safe_attributes 'start_page' |
|
32 | safe_attributes 'start_page' | |
31 |
|
33 | |||
32 | def visible?(user=User.current) |
|
34 | def visible?(user=User.current) | |
@@ -52,11 +54,11 class Wiki < ActiveRecord::Base | |||||
52 | title = start_page if title.blank? |
|
54 | title = start_page if title.blank? | |
53 | title = Wiki.titleize(title) |
|
55 | title = Wiki.titleize(title) | |
54 | page = pages.where("LOWER(title) = LOWER(?)", title).first |
|
56 | page = pages.where("LOWER(title) = LOWER(?)", title).first | |
55 |
if |
|
57 | if page.nil? && options[:with_redirect] != false | |
56 | # search for a redirect |
|
58 | # search for a redirect | |
57 | redirect = redirects.where("LOWER(title) = LOWER(?)", title).first |
|
59 | redirect = redirects.where("LOWER(title) = LOWER(?)", title).first | |
58 | if redirect |
|
60 | if redirect | |
59 | page = find_page(redirect.redirects_to, :with_redirect => false) |
|
61 | page = redirect.target_page | |
60 | @page_found_with_redirect = true |
|
62 | @page_found_with_redirect = true | |
61 | end |
|
63 | end | |
62 | end |
|
64 | end | |
@@ -68,6 +70,12 class Wiki < ActiveRecord::Base | |||||
68 | @page_found_with_redirect |
|
70 | @page_found_with_redirect | |
69 | end |
|
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 | # Finds a page by title |
|
79 | # Finds a page by title | |
72 | # The given string can be of one of the forms: "title" or "project:title" |
|
80 | # The given string can be of one of the forms: "title" or "project:title" | |
73 | # Examples: |
|
81 | # Examples: |
@@ -46,8 +46,9 class WikiPage < ActiveRecord::Base | |||||
46 | attr_protected :id |
|
46 | attr_protected :id | |
47 |
|
47 | |||
48 | validate :validate_parent_title |
|
48 | validate :validate_parent_title | |
49 |
before_destroy : |
|
49 | before_destroy :delete_redirects | |
50 |
before_save |
|
50 | before_save :handle_rename_or_move | |
|
51 | after_save :handle_children_move | |||
51 |
|
52 | |||
52 | # eager load information about last updates, without loading text |
|
53 | # eager load information about last updates, without loading text | |
53 | scope :with_updated_on, lambda { |
|
54 | scope :with_updated_on, lambda { | |
@@ -58,7 +59,7 class WikiPage < ActiveRecord::Base | |||||
58 | # Wiki pages that are protected by default |
|
59 | # Wiki pages that are protected by default | |
59 | DEFAULT_PROTECTED_PAGES = %w(sidebar) |
|
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 | :if => lambda {|page, user| page.new_record? || user.allowed_to?(:rename_wiki_pages, page.project)} |
|
63 | :if => lambda {|page, user| page.new_record? || user.allowed_to?(:rename_wiki_pages, page.project)} | |
63 |
|
64 | |||
64 | def initialize(attributes=nil, *args) |
|
65 | def initialize(attributes=nil, *args) | |
@@ -74,30 +75,67 class WikiPage < ActiveRecord::Base | |||||
74 |
|
75 | |||
75 | def title=(value) |
|
76 | def title=(value) | |
76 | value = Wiki.titleize(value) |
|
77 | value = Wiki.titleize(value) | |
77 | @previous_title = read_attribute(:title) if @previous_title.blank? |
|
|||
78 | write_attribute(:title, value) |
|
78 | write_attribute(:title, value) | |
79 | end |
|
79 | end | |
80 |
|
80 | |||
81 | def handle_redirects |
|
81 | def safe_attributes=(attrs, user=User.current) | |
82 | self.title = Wiki.titleize(title) |
|
82 | return unless attrs.is_a?(Hash) | |
83 | # Manage redirects if the title has changed |
|
83 | attrs = attrs.deep_dup | |
84 | if !@previous_title.blank? && (@previous_title != title) && !new_record? |
|
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 | # Update redirects that point to the old title |
|
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 | r.redirects_to = title |
|
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 | end |
|
103 | end | |
90 | # Remove redirects for the new title |
|
104 | # Remove redirects for the new title | |
91 |
|
|
105 | WikiRedirect.where(:wiki_id => wiki_id, :title => title).delete_all | |
92 | # Create a redirect to the new title |
|
106 | # Create a redirect to the new title | |
93 | wiki.redirects << WikiRedirect.new(:title => @previous_title, :redirects_to => title) unless redirect_existing_links == "0" |
|
107 | unless redirect_existing_links == "0" | |
94 | @previous_title = nil |
|
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 | end |
|
118 | end | |
96 | end |
|
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 |
|
136 | # Deletes redirects to this page | |
99 | # Remove redirects to this page |
|
137 | def delete_redirects | |
100 | wiki.redirects.where(:redirects_to => title).each(&:destroy) |
|
138 | WikiRedirect.where(:redirects_to_wiki_id => wiki_id, :redirects_to => title).delete_all | |
101 | end |
|
139 | end | |
102 |
|
140 | |||
103 | def pretty_title |
|
141 | def pretty_title | |
@@ -136,7 +174,7 class WikiPage < ActiveRecord::Base | |||||
136 | end |
|
174 | end | |
137 |
|
175 | |||
138 | def project |
|
176 | def project | |
139 | wiki.project |
|
177 | wiki.try(:project) | |
140 | end |
|
178 | end | |
141 |
|
179 | |||
142 | def text |
|
180 | def text | |
@@ -196,7 +234,9 class WikiPage < ActiveRecord::Base | |||||
196 | def validate_parent_title |
|
234 | def validate_parent_title | |
197 | errors.add(:parent_title, :invalid) if !@parent_title.blank? && parent.nil? |
|
235 | errors.add(:parent_title, :invalid) if !@parent_title.blank? && parent.nil? | |
198 | errors.add(:parent_title, :circular_dependency) if parent && (parent == self || parent.ancestors.include?(self)) |
|
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 | end |
|
240 | end | |
201 | end |
|
241 | end | |
202 |
|
242 |
@@ -18,7 +18,22 | |||||
18 | class WikiRedirect < ActiveRecord::Base |
|
18 | class WikiRedirect < ActiveRecord::Base | |
19 | belongs_to :wiki |
|
19 | belongs_to :wiki | |
20 |
|
20 | |||
21 | validates_presence_of :title, :redirects_to |
|
21 | validates_presence_of :wiki_id, :title, :redirects_to | |
22 | validates_length_of :title, :redirects_to, :maximum => 255 |
|
22 | validates_length_of :title, :redirects_to, :maximum => 255 | |
23 | attr_protected :id |
|
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 | end |
|
39 | end |
@@ -16,6 +16,11 | |||||
16 | @wiki.pages.includes(:parent).to_a - @page.self_and_descendants, |
|
16 | @wiki.pages.includes(:parent).to_a - @page.self_and_descendants, | |
17 | @page.parent), |
|
17 | @page.parent), | |
18 | :label => :field_parent_title %></p> |
|
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 | </div> |
|
24 | </div> | |
20 | <%= submit_tag l(:button_rename) %> |
|
25 | <%= submit_tag l(:button_rename) %> | |
21 | <% end %> |
|
26 | <% end %> |
@@ -660,6 +660,39 class WikiControllerTest < ActionController::TestCase | |||||
660 | assert_nil WikiPage.find_by_title('Child_1').parent |
|
660 | assert_nil WikiPage.find_by_title('Child_1').parent | |
661 | end |
|
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 | def test_destroy_a_page_without_children_should_not_ask_confirmation |
|
696 | def test_destroy_a_page_without_children_should_not_ask_confirmation | |
664 | @request.session[:user_id] = 2 |
|
697 | @request.session[:user_id] = 2 | |
665 | delete :destroy, :project_id => 1, :id => 'Child_2' |
|
698 | delete :destroy, :project_id => 1, :id => 'Child_2' |
@@ -101,6 +101,26 class WikiPageTest < ActiveSupport::TestCase | |||||
101 | assert page.save |
|
101 | assert page.save | |
102 | end |
|
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 | def test_destroy |
|
124 | def test_destroy | |
105 | page = WikiPage.find(1) |
|
125 | page = WikiPage.find(1) | |
106 | page.destroy |
|
126 | page.destroy |
@@ -25,15 +25,38 class WikiRedirectTest < ActiveSupport::TestCase | |||||
25 | @original = WikiPage.create(:wiki => @wiki, :title => 'Original title') |
|
25 | @original = WikiPage.create(:wiki => @wiki, :title => 'Original title') | |
26 | end |
|
26 | end | |
27 |
|
27 | |||
28 | def test_create_redirect |
|
28 | def test_create_redirect_on_rename | |
29 | @original.title = 'New title' |
|
29 | @original.title = 'New title' | |
30 |
|
|
30 | @original.save! | |
31 | @original.reload |
|
|||
32 |
|
31 | |||
33 | assert_equal 'New_title', @original.title |
|
32 | redirect = @wiki.redirects.find_by_title('Original_title') | |
34 | assert @wiki.redirects.find_by_title('Original_title') |
|
33 | assert_not_nil redirect | |
35 | assert @wiki.find_page('Original title') |
|
34 | assert_equal 1, redirect.redirects_to_wiki_id | |
36 | assert @wiki.find_page('ORIGINAL title') |
|
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 | end |
|
60 | end | |
38 |
|
61 | |||
39 | def test_update_redirect |
|
62 | def test_update_redirect |
@@ -97,4 +97,18 class WikiTest < ActiveSupport::TestCase | |||||
97 | assert_kind_of WikiPage, @wiki.sidebar |
|
97 | assert_kind_of WikiPage, @wiki.sidebar | |
98 | assert_equal 'Sidebar', @wiki.sidebar.title |
|
98 | assert_equal 'Sidebar', @wiki.sidebar.title | |
99 | end |
|
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 | end |
|
114 | end |
General Comments 0
You need to be logged in to leave comments.
Login now