summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/controllers/wiki_controller.rb12
-rw-r--r--app/models/wiki.rb15
-rw-r--r--app/models/wiki_page.rb30
-rw-r--r--app/models/wiki_redirect.rb23
-rw-r--r--app/views/wiki/rename.rhtml11
-rw-r--r--app/views/wiki/show.rhtml5
-rw-r--r--db/migrate/067_create_wiki_redirects.rb15
-rw-r--r--lang/bg.yml2
-rw-r--r--lang/de.yml2
-rw-r--r--lang/en.yml2
-rw-r--r--lang/es.yml2
-rw-r--r--lang/fr.yml2
-rw-r--r--lang/it.yml2
-rw-r--r--lang/ja.yml2
-rw-r--r--lang/nl.yml2
-rw-r--r--lang/pt-br.yml2
-rw-r--r--lang/pt.yml2
-rw-r--r--lang/sv.yml2
-rw-r--r--lang/zh.yml2
-rw-r--r--lib/redmine.rb1
-rw-r--r--test/unit/wiki_redirect_test.rb73
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