diff options
-rw-r--r-- | app/controllers/wiki_controller.rb | 12 | ||||
-rw-r--r-- | app/models/wiki.rb | 15 | ||||
-rw-r--r-- | app/models/wiki_page.rb | 30 | ||||
-rw-r--r-- | app/models/wiki_redirect.rb | 23 | ||||
-rw-r--r-- | app/views/wiki/rename.rhtml | 11 | ||||
-rw-r--r-- | app/views/wiki/show.rhtml | 5 | ||||
-rw-r--r-- | db/migrate/067_create_wiki_redirects.rb | 15 | ||||
-rw-r--r-- | lang/bg.yml | 2 | ||||
-rw-r--r-- | lang/de.yml | 2 | ||||
-rw-r--r-- | lang/en.yml | 2 | ||||
-rw-r--r-- | lang/es.yml | 2 | ||||
-rw-r--r-- | lang/fr.yml | 2 | ||||
-rw-r--r-- | lang/it.yml | 2 | ||||
-rw-r--r-- | lang/ja.yml | 2 | ||||
-rw-r--r-- | lang/nl.yml | 2 | ||||
-rw-r--r-- | lang/pt-br.yml | 2 | ||||
-rw-r--r-- | lang/pt.yml | 2 | ||||
-rw-r--r-- | lang/sv.yml | 2 | ||||
-rw-r--r-- | lang/zh.yml | 2 | ||||
-rw-r--r-- | lib/redmine.rb | 1 | ||||
-rw-r--r-- | test/unit/wiki_redirect_test.rb | 73 |
21 files changed, 201 insertions, 8 deletions
diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index e9212a1c7..95f792c5e 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -75,6 +75,18 @@ class WikiController < ApplicationController flash[:error] = l(:notice_locking_conflict) end + # rename a page + def rename + @page = @wiki.find_page(params[:page]) + @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]) + flash[:notice] = l(:notice_successful_update) + redirect_to :action => 'index', :id => @project, :page => @page.title + end + end + # show page history def history @page = @wiki.find_page(params[:page]) diff --git a/app/models/wiki.rb b/app/models/wiki.rb index ed473c7c0..b6cd661ff 100644 --- a/app/models/wiki.rb +++ b/app/models/wiki.rb @@ -18,6 +18,7 @@ class Wiki < ActiveRecord::Base belongs_to :project has_many :pages, :class_name => 'WikiPage', :dependent => :destroy + has_many :redirects, :class_name => 'WikiRedirect', :dependent => :delete_all validates_presence_of :start_page validates_format_of :start_page, :with => /^[^,\.\/\?\;\|\:]*$/ @@ -25,14 +26,20 @@ class Wiki < ActiveRecord::Base # find the page with the given title # if page doesn't exist, return a new page def find_or_new_page(title) - title = Wiki.titleize(title || start_page) - find_page(title) || WikiPage.new(:wiki => self, :title => title) + find_page(title) || WikiPage.new(:wiki => self, :title => Wiki.titleize(title)) end # find the page with the given title - def find_page(title) + def find_page(title, options = {}) title = start_page if title.blank? - pages.find_by_title(Wiki.titleize(title)) + title = Wiki.titleize(title) + page = pages.find_by_title(title) + if !page && !(options[:with_redirect] == false) + # search for a redirect + redirect = redirects.find_by_title(title) + page = find_page(redirect.redirects_to, :with_redirect => false) if redirect + end + page end # turn a string into a valid page title diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 074d36daa..1ef8b7db4 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -22,13 +22,39 @@ class WikiPage < ActiveRecord::Base has_one :content, :class_name => 'WikiContent', :foreign_key => 'page_id', :dependent => :destroy has_many :attachments, :as => :container, :dependent => :destroy + attr_accessor :redirect_existing_links + validates_presence_of :title validates_format_of :title, :with => /^[^,\.\/\?\;\|\s]*$/ validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false validates_associated :content - + + def title=(value) + value = Wiki.titleize(value) + @previous_title = read_attribute(:title) if @previous_title.blank? + write_attribute(:title, value) + end + def before_save - self.title = Wiki.titleize(title) + self.title = Wiki.titleize(title) + # Manage redirects if the title has changed + if !@previous_title.blank? && (@previous_title != title) && !new_record? + # Update redirects that point to the old title + wiki.redirects.find_all_by_redirects_to(@previous_title).each do |r| + r.redirects_to = title + r.title == r.redirects_to ? r.destroy : r.save + end + # Remove redirects for the new title + wiki.redirects.find_all_by_title(title).each(&:destroy) + # 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 + end + end + + def before_destroy + # Remove redirects to this page + wiki.redirects.find_all_by_redirects_to(title).each(&:destroy) end def pretty_title diff --git a/app/models/wiki_redirect.rb b/app/models/wiki_redirect.rb new file mode 100644 index 000000000..adc2b24c1 --- /dev/null +++ b/app/models/wiki_redirect.rb @@ -0,0 +1,23 @@ +# redMine - project management software +# Copyright (C) 2006-2007 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. + +class WikiRedirect < ActiveRecord::Base + belongs_to :wiki + + validates_presence_of :title, :redirects_to + validates_length_of :title, :redirects_to, :maximum => 255 +end diff --git a/app/views/wiki/rename.rhtml b/app/views/wiki/rename.rhtml new file mode 100644 index 000000000..0c069f43d --- /dev/null +++ b/app/views/wiki/rename.rhtml @@ -0,0 +1,11 @@ +<h2><%= l(:button_rename) %>: <%= @original_title %></h2> + +<%= error_messages_for 'page' %> + +<% labelled_tabular_form_for :wiki_page, @page, :url => { :action => 'rename' } do |f| %> +<div class="box"> +<p><%= f.text_field :title, :required => true, :size => 255 %></p> +<p><%= f.check_box :redirect_existing_links %></p> +</div> +<%= submit_tag l(:button_rename) %> +<% end %> diff --git a/app/views/wiki/show.rhtml b/app/views/wiki/show.rhtml index 4041ec018..de28ff1e2 100644 --- a/app/views/wiki/show.rhtml +++ b/app/views/wiki/show.rhtml @@ -1,5 +1,6 @@ <div class="contextual"> <%= link_to_if_authorized(l(:button_edit), {:action => 'edit', :page => @page.title}, :class => 'icon icon-edit') if @content.version == @page.content.version %> +<%= link_to_if_authorized(l(:button_rename), {:action => 'rename', :page => @page.title}, :class => 'icon icon-move') if @content.version == @page.content.version %> <%= link_to_if_authorized(l(:button_delete), {:action => 'destroy', :page => @page.title}, :method => :post, :confirm => l(:text_are_you_sure), :class => 'icon icon-del') %> <%= link_to_if_authorized(l(:button_rollback), {:action => 'edit', :page => @page.title, :version => @content.version }, :class => 'icon icon-cancel') if @content.version < @page.content.version %> <%= link_to(l(:label_history), {:action => 'history', :page => @page.title}, :class => 'icon icon-history') %> @@ -26,8 +27,8 @@ <div class="contextual"> <%= l(:label_export_to) %> -<%= link_to 'HTML', {:export => 'html', :version => @content.version}, :class => 'icon icon-html' %>, -<%= link_to 'TXT', {:export => 'txt', :version => @content.version}, :class => 'icon icon-txt' %> +<%= link_to 'HTML', {:page => @page.title, :export => 'html', :version => @content.version}, :class => 'icon icon-html' %>, +<%= link_to 'TXT', {:page => @page.title, :export => 'txt', :version => @content.version}, :class => 'icon icon-txt' %> </div> <% if authorize_for('wiki', 'add_attachment') %> diff --git a/db/migrate/067_create_wiki_redirects.rb b/db/migrate/067_create_wiki_redirects.rb new file mode 100644 index 000000000..dda6ba6d5 --- /dev/null +++ b/db/migrate/067_create_wiki_redirects.rb @@ -0,0 +1,15 @@ +class CreateWikiRedirects < ActiveRecord::Migration + def self.up + create_table :wiki_redirects do |t| + t.column :wiki_id, :integer, :null => false + t.column :title, :string + t.column :redirects_to, :string + t.column :created_on, :datetime, :null => false + end + add_index :wiki_redirects, [:wiki_id, :title], :name => :wiki_redirects_wiki_id_title + end + + def self.down + drop_table :wiki_redirects + end +end diff --git a/lang/bg.yml b/lang/bg.yml index f34c3d451..57846c3de 100644 --- a/lang/bg.yml +++ b/lang/bg.yml @@ -157,6 +157,7 @@ field_is_filter: Използва се за филтър field_issue_to_id: Related issue field_delay: Delay field_assignable: Issues can be assigned to this role +field_redirect_existing_links: Redirect existing links setting_app_title: Заглавие setting_app_subtitle: Описание @@ -446,6 +447,7 @@ button_reply: Reply button_archive: Archive button_unarchive: Unarchive button_reset: Reset +button_rename: Rename status_active: активен status_registered: регистриран diff --git a/lang/de.yml b/lang/de.yml index f40b8e1cc..a367c0886 100644 --- a/lang/de.yml +++ b/lang/de.yml @@ -157,6 +157,7 @@ field_is_filter: Used as a filter field_issue_to_id: Related issue field_delay: Delay field_assignable: Issues can be assigned to this role +field_redirect_existing_links: Redirect existing links setting_app_title: Applikation Titel setting_app_subtitle: Applikation Untertitel @@ -446,6 +447,7 @@ button_reply: Reply button_archive: Archive button_unarchive: Unarchive button_reset: Reset +button_rename: Rename status_active: aktiv status_registered: angemeldet diff --git a/lang/en.yml b/lang/en.yml index 0e3b74720..fd74c853a 100644 --- a/lang/en.yml +++ b/lang/en.yml @@ -157,6 +157,7 @@ field_is_filter: Used as a filter field_issue_to_id: Related issue field_delay: Delay field_assignable: Issues can be assigned to this role +field_redirect_existing_links: Redirect existing links setting_app_title: Application title setting_app_subtitle: Application subtitle @@ -446,6 +447,7 @@ button_reply: Reply button_archive: Archive button_unarchive: Unarchive button_reset: Reset +button_rename: Rename status_active: active status_registered: registered diff --git a/lang/es.yml b/lang/es.yml index b1a92fd44..366dfa01c 100644 --- a/lang/es.yml +++ b/lang/es.yml @@ -157,6 +157,7 @@ field_is_filter: Used as a filter field_issue_to_id: Related issue field_delay: Delay field_assignable: Issues can be assigned to this role +field_redirect_existing_links: Redirect existing links setting_app_title: Título del aplicación setting_app_subtitle: Subtítulo del aplicación @@ -446,6 +447,7 @@ button_reply: Reply button_archive: Archive button_unarchive: Unarchive button_reset: Reset +button_rename: Rename status_active: active status_registered: registered diff --git a/lang/fr.yml b/lang/fr.yml index 35fc48d69..b7ad0b0b4 100644 --- a/lang/fr.yml +++ b/lang/fr.yml @@ -157,6 +157,7 @@ field_is_filter: Utilisé comme filtre field_issue_to_id: Demande liée field_delay: Retard field_assignable: Demandes assignables à ce rôle +field_redirect_existing_links: Rediriger les liens existants setting_app_title: Titre de l'application setting_app_subtitle: Sous-titre de l'application @@ -446,6 +447,7 @@ button_reply: Répondre button_archive: Archiver button_unarchive: Désarchiver button_reset: Réinitialiser +button_rename: Renommer status_active: actif status_registered: enregistré diff --git a/lang/it.yml b/lang/it.yml index 996f6d862..b30d2ab9c 100644 --- a/lang/it.yml +++ b/lang/it.yml @@ -157,6 +157,7 @@ field_is_filter: Used as a filter field_issue_to_id: Related issue field_delay: Delay field_assignable: Issues can be assigned to this role +field_redirect_existing_links: Redirect existing links setting_app_title: Titolo applicazione setting_app_subtitle: Sottotitolo applicazione @@ -446,6 +447,7 @@ button_reply: Reply button_archive: Archive button_unarchive: Unarchive button_reset: Reset +button_rename: Rename status_active: attivo status_registered: registrato diff --git a/lang/ja.yml b/lang/ja.yml index bbadf5c91..a0d6c168b 100644 --- a/lang/ja.yml +++ b/lang/ja.yml @@ -158,6 +158,7 @@ field_is_filter: フィルタとして使う field_issue_to_id: 関連する問題 field_delay: 遅延 field_assignable: Issues can be assigned to this role +field_redirect_existing_links: Redirect existing links setting_app_title: アプリケーションのタイトル setting_app_subtitle: アプリケーションのサブタイトル @@ -447,6 +448,7 @@ button_reply: 返答 button_archive: 書庫に保存 button_unarchive: 書庫から戻す button_reset: Reset +button_rename: Rename status_active: 有効 status_registered: 登録 diff --git a/lang/nl.yml b/lang/nl.yml index b6163e29e..ba9817bbd 100644 --- a/lang/nl.yml +++ b/lang/nl.yml @@ -157,6 +157,7 @@ field_is_filter: Gebruikt als een filter field_issue_to_id: Gerelateerd issue field_delay: Vertraging field_assignable: Issues can be assigned to this role +field_redirect_existing_links: Redirect existing links setting_app_title: Applicatie titel setting_app_subtitle: Applicatie ondertitel @@ -446,6 +447,7 @@ button_reply: Antwoord button_archive: Archive button_unarchive: Unarchive button_reset: Reset +button_rename: Rename status_active: Actief status_registered: geregistreerd diff --git a/lang/pt-br.yml b/lang/pt-br.yml index 2667a51f9..15089070a 100644 --- a/lang/pt-br.yml +++ b/lang/pt-br.yml @@ -157,6 +157,7 @@ field_is_filter: Used as a filter field_issue_to_id: Related issue
field_delay: Delay
field_assignable: Issues can be assigned to this role
+field_redirect_existing_links: Redirect existing links
setting_app_title: Titulo da aplicacao
setting_app_subtitle: Sub-titulo da aplicacao
@@ -446,6 +447,7 @@ button_reply: Reply button_archive: Archive
button_unarchive: Unarchive
button_reset: Reset
+button_rename: Rename
status_active: ativo
status_registered: registrado
diff --git a/lang/pt.yml b/lang/pt.yml index 76f03657e..d4b0e0cad 100644 --- a/lang/pt.yml +++ b/lang/pt.yml @@ -157,6 +157,7 @@ field_is_filter: Usado como filtro field_issue_to_id: Tarefa relacionada field_delay: Atraso field_assignable: Issues can be assigned to this role +field_redirect_existing_links: Redirect existing links setting_app_title: Título da aplicação setting_app_subtitle: Sub-título da aplicação @@ -446,6 +447,7 @@ button_reply: Reply button_archive: Archive button_unarchive: Unarchive button_reset: Reset +button_rename: Rename status_active: ativo status_registered: registrado diff --git a/lang/sv.yml b/lang/sv.yml index c91dcfaa3..8e2c034dc 100644 --- a/lang/sv.yml +++ b/lang/sv.yml @@ -157,6 +157,7 @@ field_is_filter: Used as a filter field_issue_to_id: Related issue field_delay: Delay field_assignable: Issues can be assigned to this role +field_redirect_existing_links: Redirect existing links setting_app_title: Applikationstitel setting_app_subtitle: Applicationsunderrubrik @@ -446,6 +447,7 @@ button_reply: Reply button_archive: Archive button_unarchive: Unarchive button_reset: Reset +button_rename: Rename status_active: activ status_registered: registrerad diff --git a/lang/zh.yml b/lang/zh.yml index 3fc2319e3..e9f8cb7d9 100644 --- a/lang/zh.yml +++ b/lang/zh.yml @@ -160,6 +160,7 @@ field_is_filter: Used as a filter field_issue_to_id: Related issue field_delay: Delay field_assignable: Issues can be assigned to this role +field_redirect_existing_links: Redirect existing links setting_app_title: 应用程序标题 setting_app_subtitle: 应用程序子标题 @@ -448,6 +449,7 @@ button_reply: Reply button_archive: Archive button_unarchive: Unarchive button_reset: Reset +button_rename: Rename status_active: 激活 status_registered: 已注册 diff --git a/lib/redmine.rb b/lib/redmine.rb index 1d27dd420..a0da981e4 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -53,6 +53,7 @@ Redmine::AccessControl.map do |map| # Wiki map.permission :view_wiki_pages, :wiki => [:index, :history, :diff, :special] map.permission :edit_wiki_pages, :wiki => [:edit, :preview, :add_attachment, :destroy_attachment] + map.permission :rename_wiki_pages, {:wiki => :rename}, :require => :member map.permission :delete_wiki_pages, {:wiki => :destroy}, :require => :member # Message boards map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :public => true diff --git a/test/unit/wiki_redirect_test.rb b/test/unit/wiki_redirect_test.rb new file mode 100644 index 000000000..12f6b7d89 --- /dev/null +++ b/test/unit/wiki_redirect_test.rb @@ -0,0 +1,73 @@ +# redMine - project management software +# Copyright (C) 2006-2007 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 WikiRedirectTest < Test::Unit::TestCase + fixtures :projects, :wikis + + def setup + @wiki = Wiki.find(1) + @original = WikiPage.create(:wiki => @wiki, :title => 'Original title') + end + + def test_create_redirect + @original.title = 'New title' + assert @original.save + @original.reload + + assert_equal 'New_title', @original.title + assert @wiki.redirects.find_by_title('Original_title') + assert @wiki.find_page('Original title') + end + + def test_update_redirect + # create a redirect that point to this page + assert WikiRedirect.create(:wiki => @wiki, :title => 'An_old_page', :redirects_to => 'Original_title') + + @original.title = 'New title' + @original.save + # make sure the old page now points to the new page + assert_equal 'New_title', @wiki.find_page('An old page').title + end + + def test_reverse_rename + # create a redirect that point to this page + assert WikiRedirect.create(:wiki => @wiki, :title => 'An_old_page', :redirects_to => 'Original_title') + + @original.title = 'An old page' + @original.save + assert !@wiki.redirects.find_by_title_and_redirects_to('An_old_page', 'An_old_page') + assert @wiki.redirects.find_by_title_and_redirects_to('Original_title', 'An_old_page') + end + + def test_rename_to_already_redirected + assert WikiRedirect.create(:wiki => @wiki, :title => 'An_old_page', :redirects_to => 'Other_page') + + @original.title = 'An old page' + @original.save + # this redirect have to be removed since 'An old page' page now exists + assert !@wiki.redirects.find_by_title_and_redirects_to('An_old_page', 'Other_page') + end + + def test_redirects_removed_when_deleting_page + assert WikiRedirect.create(:wiki => @wiki, :title => 'An_old_page', :redirects_to => 'Original_title') + + @original.destroy + assert !@wiki.redirects.find(:first) + end +end |