diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb
index 2669c60e6..0151066fa 100644
--- a/app/controllers/wiki_controller.rb
+++ b/app/controllers/wiki_controller.rb
@@ -36,6 +36,7 @@ class WikiController < ApplicationController
before_action :find_wiki, :authorize
before_action :find_existing_or_new_page, :only => [:show, :edit]
before_action :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy, :destroy_version]
+ before_action :find_copy_from, :only => [:show, :update]
before_action :find_attachments, :only => [:preview]
accept_api_auth :index, :show, :update, :destroy
@@ -63,7 +64,11 @@ class WikiController < ApplicationController
end
def new
- @page = WikiPage.new(:wiki => @wiki, :title => params[:title])
+ @page = if params[:copy_to_project_id]
+ WikiPage.new(:wiki => Project.find(params[:copy_to_project_id]).wiki, :title => (params[:title] || params[:copy_from]))
+ else
+ WikiPage.new(:wiki => @wiki, :title => (params[:title] || params[:copy_from]))
+ end
unless User.current.allowed_to?(:edit_wiki_pages, @project)
render_403
return
@@ -72,12 +77,21 @@ class WikiController < ApplicationController
@page.title = '' unless editable?
@page.validate
if @page.errors[:title].blank?
- path = project_wiki_page_path(@project, @page.title, :parent => params[:parent])
+ if params[:copy_from]
+ copy_to_project = Project.find(params[:copy_to_project_id])
+ path = project_wiki_page_path(copy_to_project, @page.title, :parent => params[:parent], :copy_from => params[:copy_from], :copy_from_project_id => @project.id)
+ else
+ path = project_wiki_page_path(@project, @page.title, :parent => params[:parent])
+ end
respond_to do |format|
format.html {redirect_to path}
format.js {render :js => "window.location = #{path.to_json}"}
end
end
+ else
+ if params[:copy_from]
+ @page = @page.copy
+ end
end
end
@@ -136,6 +150,9 @@ class WikiController < ApplicationController
@content = @page.content_for_version(params[:version])
@content ||= WikiContent.new(:page => @page)
+ if @copy_from
+ @content.text = @copy_from.content.try(:text)
+ end
@content.text = initial_page_content(@page) if @content.text.blank?
# don't keep previous comment
@content.comments = nil
@@ -178,7 +195,7 @@ class WikiController < ApplicationController
end
@content.author = User.current
- if @page.save_with_content(@content)
+ if @page.save_with_content(@content, :copy_from => @copy_from, :copy_attachments => params[:copy_attachments], :copy_child_pages => params[:copy_child_pages])
attachments = Attachment.attach_files(@page, params[:attachments] || (params[:wiki_page] && params[:wiki_page][:uploads]))
render_attachment_warning_if_needed(@page)
call_hook(:controller_wiki_edit_after_save, {:params => params, :page => @page})
@@ -373,6 +390,14 @@ class WikiController < ApplicationController
end
end
+ def find_copy_from
+ @copy_from = if params[:copy_from_project_id] && params[:copy_from]
+ Project.find(params[:copy_from_project_id]).wiki.try(:find_page, params[:copy_from]) rescue nil
+ else
+ nil
+ end
+ end
+
def redirect_to_page(page)
if page.project && page.project.visible?
redirect_to :action => action_name, :project_id => page.project, :id => page.title
diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb
index 0b5f3b8f4..730e9c773 100644
--- a/app/helpers/wiki_helper.rb
+++ b/app/helpers/wiki_helper.rb
@@ -46,6 +46,14 @@ module WikiHelper
end
end
+ def wiki_page_project_options_for_select(page)
+ projects = Project.allowed_to(:add_wiki_pages).joins(:wiki).preload(:wiki).to_a
+ projects.select! {|p| p.enabled_module_names.include?('wiki') }
+ projects << page.project unless projects.include?(page.project)
+
+ project_tree_options_for_select(projects, :selected => page.project)
+ end
+
def wiki_page_breadcrumb(page)
breadcrumb(
page.ancestors.reverse.collect do |parent|
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index 0ee06673e..f33c787e4 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -243,13 +243,34 @@ class WikiPage < ActiveRecord::Base
# Saves the page and its content if text was changed
# Return true if the page was saved
- def save_with_content(content)
+ def save_with_content(content, options={ :copy_from => nil, :copy_attachments => false, :copy_child_pages => false})
ret = nil
transaction do
ret = save
if content.text_changed?
begin
self.content = content
+ copy_from, copy_attachments, copy_child_pages = options.values_at(:copy_from, :copy_attachments, :copy_child_pages)
+ if copy_from && copy_attachments
+ self.attachments = copy_from.attachments.collect do |attachment|
+ attachment.copy(:container => self)
+ end
+ end
+ if copy_from && copy_child_pages
+ self.children = copy_from.children.collect do |child_page|
+ page = child_page.copy(:parent => self)
+ unless page.save_with_content(child_page.content.try(:dup),
+ :copy_from => child_page,
+ :copy_attachments => copy_attachments,
+ :copy_child_pages => copy_child_pages)
+ page.errors.full_messages.each do |message|
+ self.errors.add :base, message
+ end
+ raise ActiveRecord::RecordNotSaved
+ end
+ page
+ end
+ end
rescue ActiveRecord::RecordNotSaved
ret = false
end
@@ -259,6 +280,28 @@ class WikiPage < ActiveRecord::Base
ret
end
+ def copy(options={ :parent => nil })
+ wiki_page = self.class.new
+ wiki_page.parent = options[:parent]
+ wiki_page.wiki = options[:parent].try(:wiki) || self.wiki
+ wiki_page.title = self.title
+ wiki_page.content = self.content.try(:dup)
+ suffix = 1
+ while wiki_page.validate == false && wiki_page.errors[:title].present?
+ wiki_page.title = self.title + "_#{suffix}"
+ suffix += 1
+ end
+ wiki_page
+ end
+
+ def has_attachments?(options={ :include_childrens => false })
+ if self.children && options[:include_childrens]
+ self.attachments.any? || self.children.any? { |c| c.has_attachments?(options) }
+ else
+ self.attachments.any?
+ end
+ end
+
def deleted_attachment_ids
Array(@deleted_attachment_ids).map(&:to_i)
end
diff --git a/app/views/wiki/_copy_modal.html.erb b/app/views/wiki/_copy_modal.html.erb
new file mode 100644
index 000000000..a23a6c77e
--- /dev/null
+++ b/app/views/wiki/_copy_modal.html.erb
@@ -0,0 +1,42 @@
+
<%=l(:label_wiki_page_copy)%>
+
+<%= labelled_form_for :page, @page,
+ :url => project_copy_wiki_page_path(@project),
+ :method => 'post',
+ :remote => true do |f| %>
+
+ <%= render_error_messages @page.errors.full_messages_for(:title) %>
+ <%= hidden_field_tag 'copy_from_project_id', @project.id %>
+ <%= hidden_field_tag 'copy_from', params[:copy_from] %>
+
+
+
+
+ <%= select_tag :copy_to_project_id, wiki_page_project_options_for_select(@page), :label => :label_project %>
+
+ <%= f.text_field :title, :name => 'title', :size => 60, :required => true %>
+ <%= l(:text_unallowed_characters) %>: , . / ? ; : |
+
+ <% if params[:parent].present? %>
+
+
+
+ <% end %>
+
+
+ <%= submit_tag l(:label_next), :name => nil %>
+ <%= submit_tag l(:button_cancel), :name => nil, :onclick => "hideModal(this);", :type => 'button' %>
+
+<% end %>
+<%= javascript_tag do %>
+$('#copy_to_project_id').change(function() {
+ if ($('#copy_from_project_id').val()==$(this).val()) {
+ $('#copy_parent_page').show();
+ }else{
+ $('#copy_parent_page').hide();
+ }
+});
+<% end %>
diff --git a/app/views/wiki/edit.html.erb b/app/views/wiki/edit.html.erb
index 7e9cf42af..c4d8d5d86 100644
--- a/app/views/wiki/edit.html.erb
+++ b/app/views/wiki/edit.html.erb
@@ -1,5 +1,7 @@
<%= wiki_page_breadcrumb(@page) %>
+<%= error_messages_for 'page' %>
+
<%= @page.pretty_title %>
<%= form_for @content, :as => :content,
@@ -11,6 +13,8 @@
<%= hidden_field_tag 'section_hash', @section_hash %>
<% end %>
<%= error_messages_for 'content' %>
+<%= hidden_field_tag 'copy_from_project_id', params[:copy_from_project_id] if params[:copy_from_project_id] %>
+<%= hidden_field_tag 'copy_from', params[:copy_from] if params[:copy_from] %>
<%= text_area_tag 'content[text]', @text, :cols => 100, :rows => 25, :accesskey => accesskey(:edit),
@@ -34,6 +38,18 @@
<% end %>
<%= f.text_field :comments, :size => 120, :maxlength => 1024 %>
+<% if @copy_from && @copy_from.has_attachments?(:include_childrens => true) %>
+
+
+ <%= check_box_tag 'copy_attachments', '1', true %>
+
+<% end %>
+<% if @copy_from && @copy_from.children.any? %>
+
+
+ <%= check_box_tag 'copy_child_pages', '1', true %>
+
+<% end %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 5a950a7ff..7673cbde5 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -857,6 +857,7 @@ en:
label_wiki_page: Wiki page
label_wiki_page_plural: Wiki pages
label_wiki_page_new: New wiki page
+ label_wiki_page_copy: Copy wiki page
label_index_by_title: Index by title
label_index_by_date: Index by date
label_current_version: Current version
@@ -1022,6 +1023,7 @@ en:
label_export_options: "%{export_format} export options"
label_copy_attachments: Copy attachments
label_copy_subtasks: Copy subtasks
+ label_copy_child_pages: Copy child pages
label_item_position: "%{position} of %{count}"
label_completed_versions: Completed versions
label_search_for_watchers: Search for watchers to add
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index c16a48224..8a39aed30 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -1063,6 +1063,7 @@ ja:
label_attribute_of_assigned_to: 担当者の %{name}
label_attribute_of_fixed_version: 対象バージョンの %{name}
label_copy_subtasks: 子チケットをコピー
+ label_copy_child_pages: 子ページをコピー
label_copied_to: コピー先
label_copied_from: コピー元
label_any_issues_in_project: 次のプロジェクト内のチケット
@@ -1199,6 +1200,7 @@ ja:
mail_body_settings_updated: ! '下記の設定が変更されました:'
field_remote_ip: IPアドレス
label_wiki_page_new: 新しいWikiページ
+ label_wiki_page_copy: Wikiページのコピー
label_relations: 関係
button_filter: フィルタ
mail_body_password_updated: パスワードが変更されました。
diff --git a/config/routes.rb b/config/routes.rb
index 00eadf679..e10b2a819 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -190,6 +190,7 @@ Rails.application.routes.draw do
end
match 'wiki/index', :controller => 'wiki', :action => 'index', :via => :get
+ match 'wiki/:copy_from/copy', :to => 'wiki#new', :as => 'copy_wiki_page', via: [:get, :post]
resources :wiki, :except => [:index, :create], :as => 'wiki_page' do
member do
get 'rename'
diff --git a/test/functional/wiki_controller_test.rb b/test/functional/wiki_controller_test.rb
index 3765ac867..2bdc9f98d 100644
--- a/test/functional/wiki_controller_test.rb
+++ b/test/functional/wiki_controller_test.rb
@@ -1279,4 +1279,73 @@ class WikiControllerTest < Redmine::ControllerTest
assert_response :success
assert_select 'head>meta[name="robots"]', false
end
+
+ def test_get_new_with_copy_from
+ @request.session[:user_id] = 2
+
+ get :new, :params => {:project_id => 'ecookbook', :copy_from => 'CookBook_documentation'}
+ assert_response :success
+ assert_select 'input[name=?][value=?]', 'title', 'CookBook_documentation_1'
+ end
+
+ def test_post_new_with_copy_from_should_redirect_to_edit_wity_copy_params
+ @request.session[:user_id] = 2
+
+ post :new, :params => {:project_id => 'ecookbook', :title => 'CookBook documentation 1', :copy_from => 'CookBook_documentation', :copy_to_project_id => 'ecookbook'}
+ assert_redirected_to '/projects/ecookbook/wiki/CookBook_documentation_1?copy_from=CookBook_documentation©_from_project_id=1'
+ end
+
+ def test_show_with_copy_from_should_copy_content
+ @request.session[:user_id] = 2
+
+ get :show, :params => {:project_id => 1, :id => 'CookBook_documentation_1', :copy_from => 'CookBook_documentation', :copy_from_project_id => 1}
+ assert_response :success
+ assert_select 'textarea[name=?]', 'content[text]', :text => /h1. CookBook documentation/
+ assert_select 'textarea[name=?]', 'content[text]', :text => /{{child_pages}}/
+ assert_select 'textarea[name=?]', 'content[text]', :text => /Some updated \[\[documentation\]\] here with gzipped history/
+ end
+
+ def test_update_with_copy_from_and_copy_attachments_should_copy_attachments
+ @request.session[:user_id] = 2
+
+ assert_difference 'WikiPage.count', 1 do
+ assert_difference 'WikiContent.count', 1 do
+ assert_difference 'Attachment.count', 2 do
+ post :update, :params => {
+ :project_id => 1,
+ :id => 'CookBook_documentation_1',
+ :content => {
+ :comments => 'Copy Wiki Page',
+ :text => 'h1. CookBook documentation',
+ :varsion => 0
+ },
+ :copy_from => 'CookBook_documentation',
+ :copy_from_project_id => 1,
+ :copy_attachments => '1'
+ }
+ end
+ end
+ end
+ end
+
+ def test_update_with_copy_from_and_copy_child_pages
+ @request.session[:user_id] = 2
+
+ assert_difference 'WikiPage.count', 4 do
+ assert_difference 'WikiContent.count', 4 do
+ post :update, :params => {
+ :project_id => 1,
+ :id => 'Another_page_1',
+ :content => {
+ :comments => 'Copy Wiki Page',
+ :text => 'h1. Another page',
+ :varsion => 0
+ },
+ :copy_from => 'Another_page',
+ :copy_from_project_id => 1,
+ :copy_child_pages => '1'
+ }
+ end
+ end
+ end
end
diff --git a/test/unit/wiki_page_test.rb b/test/unit/wiki_page_test.rb
index 9e7633f08..a61b8f00d 100644
--- a/test/unit/wiki_page_test.rb
+++ b/test/unit/wiki_page_test.rb
@@ -20,7 +20,7 @@
require_relative '../test_helper'
class WikiPageTest < ActiveSupport::TestCase
- fixtures :projects, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
+ fixtures :projects, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions, :users
def setup
User.current = nil
@@ -207,4 +207,82 @@ class WikiPageTest < ActiveSupport::TestCase
assert_equal 3, diff.content_to.version
assert_equal 1, diff.content_from.version
end
+
+ def test_copy
+ page = WikiPage.create!(:wiki => @wiki, :title => 'CopyFrom')
+ page.content = WikiContent.new(:text => '#h1 Copy From', :comments => 'test')
+
+ copy_page_1 = page.copy
+ assert_equal page.wiki, copy_page_1.wiki
+ assert_equal '#h1 Copy From', copy_page_1.content.text
+ assert_equal 'CopyFrom_1', copy_page_1.title
+ copy_page_1.save
+
+ copy_page_2 = page.copy
+ assert_equal page.wiki, copy_page_2.wiki
+ assert_equal '#h1 Copy From', copy_page_2.content.text
+ assert_equal 'CopyFrom_2', copy_page_2.title
+ end
+
+ def test_copy_spacify_parent
+ parent_page = WikiPage.create!(:wiki => @wiki, :title => 'CopyFrom Parent')
+ parent_page.content = WikiContent.new(:text => '#h1 Copy From Parent', :comments => 'test')
+ page = WikiPage.create!(:wiki => @wiki, :title => 'CopyFrom', :parent => parent_page)
+ page.content = WikiContent.new(:text => '#h1 Copy From', :comments => 'test')
+ assert_equal parent_page, page.parent
+ assert_equal 1, parent_page.children.count
+
+ copy_page_1 = page.copy(:parent => parent_page)
+ assert_equal page.wiki, copy_page_1.wiki
+ assert_equal '#h1 Copy From', copy_page_1.content.text
+ assert_equal 'CopyFrom_1', copy_page_1.title
+ assert_equal parent_page, copy_page_1.parent
+ copy_page_1.save
+ assert_equal 2, parent_page.children.count
+
+ copy_page_2 = page.copy(:parent => nil)
+ assert_equal page.wiki, copy_page_2.wiki
+ assert_equal '#h1 Copy From', copy_page_2.content.text
+ assert_equal 'CopyFrom_2', copy_page_2.title
+ assert_nil copy_page_2.parent
+ copy_page_2.save
+ assert_equal 2, parent_page.children.count
+ end
+
+ def test_has_attachments
+ page = WikiPage.create!(:wiki => @wiki, :title => 'Parent')
+ child1 = WikiPage.create!(:wiki => @wiki, :title => 'Child1', :parent => page)
+ child11 = WikiPage.create!(:wiki => @wiki, :title => 'Child11', :parent => child1)
+ Attachment.generate!(:container => child11, :filename => 'test.txt')
+ page.reload
+
+ assert_equal true, page.has_attachments?(:include_childrens => true)
+ assert_equal false, page.has_attachments?(:include_childrens => false)
+ assert_equal false, page.has_attachments?
+ assert_equal false, child1.has_attachments?(:include_childrens => true)
+ assert_equal false, child1.has_attachments?(:include_childrens => false)
+ assert_equal false, child1.has_attachments?
+ assert_equal true, child11.has_attachments?(:include_childrens => true)
+ assert_equal true, child11.has_attachments?(:include_childrens => false)
+ assert_equal true, child11.has_attachments?
+ end
+
+ def test_save_with_content_with_copy_options
+ page = WikiPage.create!(:wiki => @wiki, :title => 'Parent')
+ page.content = WikiContent.new(:text => '#h1 Copy From Content', :comments => 'test')
+ page.save
+ child1 = WikiPage.create!(:wiki => @wiki, :title => 'Child1', :parent => page)
+ child1.content = WikiContent.new(:text => '#h1 Copy From Child Content', :comments => 'test')
+ Attachment.generate!(:container => child1, :filename => 'test.txt')
+ page.reload
+
+ new_page = WikiPage.new(:wiki => @wiki, :title => 'Parent_1')
+ content = page.content.dup
+ content.text = '#h1 Copy to Content'
+ assert new_page.save_with_content(content, :copy_from => page, :copy_attachments => true, :copy_child_pages => true)
+ assert_equal '#h1 Copy to Content', new_page.content.text
+ assert_equal 1, new_page.children.count
+ assert_equal 'Child1_1', new_page.children.first.title
+ assert_equal 1, new_page.children.first.attachments.count
+ end
end