diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb
index e9d060870..6e747ecda 100644
--- a/app/controllers/wiki_controller.rb
+++ b/app/controllers/wiki_controller.rb
@@ -35,7 +35,7 @@ class WikiController < ApplicationController
default_search_scope :wiki_pages
before_filter :find_wiki, :authorize
before_filter :find_existing_or_new_page, :only => [:show, :edit, :update]
- before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
+ before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy, :destroy_version]
helper :attachments
include AttachmentsHelper
@@ -237,6 +237,14 @@ class WikiController < ApplicationController
redirect_to :action => 'index', :project_id => @project
end
+ def destroy_version
+ return render_403 unless editable?
+
+ @content = @page.content_for_version(params[:version])
+ @content.destroy
+ redirect_to_referer_or :action => 'history', :id => @page.title, :project_id => @project
+ end
+
# Export wiki to a single pdf or html file
def export
@pages = @wiki.pages.all(:order => 'title', :include => [:content, :attachments], :limit => 75)
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index e3126a358..048b7ae6f 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -147,6 +147,10 @@ module ApplicationHelper
end
end
+ def wiki_page_path(page, options={})
+ url_for({:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title}.merge(options))
+ end
+
def thumbnail_tag(attachment)
link_to image_tag(url_for(:controller => 'attachments', :action => 'thumbnail', :id => attachment)),
{:controller => 'attachments', :action => 'show', :id => attachment, :filename => attachment.filename},
diff --git a/app/models/wiki_content.rb b/app/models/wiki_content.rb
index ec1cd8234..7c5704902 100644
--- a/app/models/wiki_content.rb
+++ b/app/models/wiki_content.rb
@@ -73,6 +73,8 @@ class WikiContent < ActiveRecord::Base
"LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id " +
"LEFT JOIN #{Project.table_name} ON #{Project.table_name}.id = #{Wiki.table_name}.project_id"}
+ after_destroy :page_update_after_destroy
+
def text=(plain)
case Setting.wiki_compression
when 'gzip'
@@ -128,5 +130,18 @@ class WikiContent < ActiveRecord::Base
includes(:author).
where("wiki_content_id = ? AND version > ?", wiki_content_id, version).first
end
+
+ private
+
+ # Updates page's content if the latest version is removed
+ # or destroys the page if it was the only version
+ def page_update_after_destroy
+ latest = page.content.versions.reorder("#{self.class.table_name}.version DESC").first
+ if latest && page.content.version != latest.version
+ raise ActiveRecord::Rollback unless page.content.revert_to!(latest)
+ elsif latest.nil?
+ raise ActiveRecord::Rollback unless page.destroy
+ end
+ end
end
end
diff --git a/app/views/wiki/history.html.erb b/app/views/wiki/history.html.erb
index 005219e04..b03af266a 100644
--- a/app/views/wiki/history.html.erb
+++ b/app/views/wiki/history.html.erb
@@ -28,7 +28,10 @@
<%= format_time(ver.updated_on) %> |
<%= link_to_user ver.author %> |
- <%= link_to l(:button_annotate), :action => 'annotate', :id => @page.title, :version => ver.version %> |
+
+ <%= link_to l(:button_annotate), :action => 'annotate', :id => @page.title, :version => ver.version %>
+ <%= delete_link wiki_page_path(@page, :version => ver.version) if User.current.allowed_to?(:delete_wiki_pages, @page.project) && @version_count > 1 %>
+ |
<% line_num += 1 %>
<% end %>
diff --git a/config/routes.rb b/config/routes.rb
index b78cb3dff..ca9d77590 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -160,6 +160,7 @@ RedmineApp::Application.routes.draw do
end
match 'wiki', :controller => 'wiki', :action => 'show', :via => :get
get 'wiki/:id/:version', :to => 'wiki#show'
+ delete 'wiki/:id/:version', :to => 'wiki#destroy_version'
get 'wiki/:id/:version/annotate', :to => 'wiki#annotate'
get 'wiki/:id/:version/diff', :to => 'wiki#diff'
end
diff --git a/lib/plugins/acts_as_versioned/lib/acts_as_versioned.rb b/lib/plugins/acts_as_versioned/lib/acts_as_versioned.rb
index 5ac65e35a..7510462bd 100644
--- a/lib/plugins/acts_as_versioned/lib/acts_as_versioned.rb
+++ b/lib/plugins/acts_as_versioned/lib/acts_as_versioned.rb
@@ -374,7 +374,7 @@ module ActiveRecord #:nodoc:
# Clones a model. Used when saving a new version or reverting a model's version.
def clone_versioned_model(orig_model, new_model)
self.versioned_attributes.each do |key|
- new_model.send("#{key}=", orig_model.send(key)) if orig_model.has_attribute?(key)
+ new_model.send("#{key}=", orig_model.send(key)) if orig_model.respond_to?(key)
end
if self.class.columns_hash.include?(self.class.inheritance_column)
diff --git a/lib/redmine.rb b/lib/redmine.rb
index e120aa802..3901b8e5d 100644
--- a/lib/redmine.rb
+++ b/lib/redmine.rb
@@ -118,7 +118,7 @@ Redmine::AccessControl.map do |map|
map.project_module :wiki do |map|
map.permission :manage_wiki, {:wikis => [:edit, :destroy]}, :require => :member
map.permission :rename_wiki_pages, {:wiki => :rename}, :require => :member
- map.permission :delete_wiki_pages, {:wiki => :destroy}, :require => :member
+ map.permission :delete_wiki_pages, {:wiki => [:destroy, :destroy_version]}, :require => :member
map.permission :view_wiki_pages, {:wiki => [:index, :show, :special, :date_index]}, :read => true
map.permission :export_wiki_pages, {:wiki => [:export]}, :read => true
map.permission :view_wiki_edits, {:wiki => [:history, :diff, :annotate]}, :read => true
diff --git a/test/functional/wiki_controller_test.rb b/test/functional/wiki_controller_test.rb
index abfb33f11..720a9b6f8 100644
--- a/test/functional/wiki_controller_test.rb
+++ b/test/functional/wiki_controller_test.rb
@@ -499,6 +499,7 @@ class WikiControllerTest < ActionController::TestCase
end
def test_history
+ @request.session[:user_id] = 2
get :history, :project_id => 'ecookbook', :id => 'CookBook_documentation'
assert_response :success
assert_template 'history'
@@ -508,17 +509,24 @@ class WikiControllerTest < ActionController::TestCase
assert_select "input[type=submit][name=commit]"
assert_select 'td' do
assert_select 'a[href=?]', '/projects/ecookbook/wiki/CookBook_documentation/2', :text => '2'
- assert_select 'a[href=?]', '/projects/ecookbook/wiki/CookBook_documentation/2/annotate'
+ assert_select 'a[href=?]', '/projects/ecookbook/wiki/CookBook_documentation/2/annotate', :text => 'Annotate'
+ assert_select 'a[href=?]', '/projects/ecookbook/wiki/CookBook_documentation/2', :text => 'Delete'
end
end
def test_history_with_one_version
- get :history, :project_id => 1, :id => 'Another_page'
+ @request.session[:user_id] = 2
+ get :history, :project_id => 'ecookbook', :id => 'Another_page'
assert_response :success
assert_template 'history'
assert_not_nil assigns(:versions)
assert_equal 1, assigns(:versions).size
assert_select "input[type=submit][name=commit]", false
+ assert_select 'td' do
+ assert_select 'a[href=?]', '/projects/ecookbook/wiki/Another_page/1', :text => '1'
+ assert_select 'a[href=?]', '/projects/ecookbook/wiki/Another_page/1/annotate', :text => 'Annotate'
+ assert_select 'a[href=?]', '/projects/ecookbook/wiki/Another_page/1', :text => 'Delete', :count => 0
+ end
end
def test_diff
@@ -681,6 +689,18 @@ class WikiControllerTest < ActionController::TestCase
assert_equal WikiPage.find(1), WikiPage.find_by_id(5).parent
end
+ def test_destroy_version
+ @request.session[:user_id] = 2
+ assert_difference 'WikiContent::Version.count', -1 do
+ assert_no_difference 'WikiContent.count' do
+ assert_no_difference 'WikiPage.count' do
+ delete :destroy_version, :project_id => 'ecookbook', :id => 'CookBook_documentation', :version => 2
+ assert_redirected_to '/projects/ecookbook/wiki/CookBook_documentation/history'
+ end
+ end
+ end
+ end
+
def test_index
get :index, :project_id => 'ecookbook'
assert_response :success
diff --git a/test/integration/routing/wiki_test.rb b/test/integration/routing/wiki_test.rb
index 2afc29eea..e7f6fa0f7 100644
--- a/test/integration/routing/wiki_test.rb
+++ b/test/integration/routing/wiki_test.rb
@@ -38,6 +38,11 @@ class RoutingWikiTest < ActionController::IntegrationTest
{ :controller => 'wiki', :action => 'diff', :project_id => '1',
:id => 'CookBook_documentation' }
)
+ assert_routing(
+ { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/2" },
+ { :controller => 'wiki', :action => 'show', :project_id => '1',
+ :id => 'CookBook_documentation', :version => '2' }
+ )
assert_routing(
{ :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/2/diff" },
{ :controller => 'wiki', :action => 'diff', :project_id => '1',
@@ -117,5 +122,10 @@ class RoutingWikiTest < ActionController::IntegrationTest
{ :controller => 'wiki', :action => 'destroy', :project_id => '22',
:id => 'ladida' }
)
+ assert_routing(
+ { :method => 'delete', :path => "/projects/22/wiki/ladida/3" },
+ { :controller => 'wiki', :action => 'destroy_version', :project_id => '22',
+ :id => 'ladida', :version => '3' }
+ )
end
end
diff --git a/test/unit/wiki_content_version_test.rb b/test/unit/wiki_content_version_test.rb
new file mode 100644
index 000000000..fa7ba82c7
--- /dev/null
+++ b/test/unit/wiki_content_version_test.rb
@@ -0,0 +1,68 @@
+# Redmine - project management software
+# Copyright (C) 2006-2012 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.expand_path('../../test_helper', __FILE__)
+
+class WikiContentTest < ActiveSupport::TestCase
+ fixtures :projects, :users, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
+
+ def setup
+ end
+
+ def test_destroy
+ v = WikiContent::Version.find(2)
+
+ assert_difference 'WikiContent::Version.count', -1 do
+ v.destroy
+ end
+ end
+
+ def test_destroy_last_version_should_revert_content
+ v = WikiContent::Version.find(3)
+
+ assert_no_difference 'WikiPage.count' do
+ assert_no_difference 'WikiContent.count' do
+ assert_difference 'WikiContent::Version.count', -1 do
+ assert v.destroy
+ end
+ end
+ end
+ c = WikiContent.find(1)
+ v = c.versions.last
+ assert_equal 2, c.version
+ assert_equal v.version, c.version
+ assert_equal v.comments, c.comments
+ assert_equal v.text, c.text
+ assert_equal v.author, c.author
+ assert_equal v.updated_on, c.updated_on
+ end
+
+ def test_destroy_all_versions_should_delete_page
+ WikiContent::Version.find(1).destroy
+ WikiContent::Version.find(2).destroy
+ v = WikiContent::Version.find(3)
+
+ assert_difference 'WikiPage.count', -1 do
+ assert_difference 'WikiContent.count', -1 do
+ assert_difference 'WikiContent::Version.count', -1 do
+ assert v.destroy
+ end
+ end
+ end
+ assert_nil WikiPage.find_by_id(1)
+ end
+end