Project

General

Profile

Feature #1330 » 1330-copy-wiki-page-with-childs.patch

Takenori TAKAKI, 2023-09-14 04:01

View differences:

app/controllers/wiki_controller.rb
36 36
  before_action :find_wiki, :authorize
37 37
  before_action :find_existing_or_new_page, :only => [:show, :edit]
38 38
  before_action :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy, :destroy_version]
39
  before_action :find_copy_from, :only => [:show, :update]
39 40
  before_action :find_attachments, :only => [:preview]
40 41
  accept_api_auth :index, :show, :update, :destroy
41 42

  
......
63 64
  end
64 65

  
65 66
  def new
66
    @page = WikiPage.new(:wiki => @wiki, :title => params[:title])
67
    @page = if params[:copy_to_project_id]
68
             WikiPage.new(:wiki => Project.find(params[:copy_to_project_id]).wiki, :title => (params[:title] || params[:copy_from]))
69
            else
70
              WikiPage.new(:wiki => @wiki, :title => (params[:title] || params[:copy_from]))
71
            end
67 72
    unless User.current.allowed_to?(:edit_wiki_pages, @project)
68 73
      render_403
69 74
      return
......
72 77
      @page.title = '' unless editable?
73 78
      @page.validate
74 79
      if @page.errors[:title].blank?
75
        path = project_wiki_page_path(@project, @page.title, :parent => params[:parent])
80
        if params[:copy_from]
81
          copy_to_project = Project.find(params[:copy_to_project_id])
82
          path = project_wiki_page_path(copy_to_project, @page.title, :parent => params[:parent], :copy_from => params[:copy_from], :copy_from_project_id => @project.id)
83
        else
84
          path = project_wiki_page_path(@project, @page.title, :parent => params[:parent])
85
        end
76 86
        respond_to do |format|
77 87
          format.html {redirect_to path}
78 88
          format.js   {render :js => "window.location = #{path.to_json}"}
79 89
        end
80 90
      end
91
    else
92
      if params[:copy_from]
93
        @page = @page.copy
94
      end
81 95
    end
82 96
  end
83 97

  
......
136 150

  
137 151
    @content = @page.content_for_version(params[:version])
138 152
    @content ||= WikiContent.new(:page => @page)
153
    if @copy_from
154
      @content.text = @copy_from.content.try(:text)
155
    end
139 156
    @content.text = initial_page_content(@page) if @content.text.blank?
140 157
    # don't keep previous comment
141 158
    @content.comments = nil
......
178 195
    end
179 196
    @content.author = User.current
180 197

  
181
    if @page.save_with_content(@content)
198
    if @page.save_with_content(@content, :copy_from => @copy_from, :copy_attachments => params[:copy_attachments], :copy_child_pages => params[:copy_child_pages])
182 199
      attachments = Attachment.attach_files(@page, params[:attachments] || (params[:wiki_page] && params[:wiki_page][:uploads]))
183 200
      render_attachment_warning_if_needed(@page)
184 201
      call_hook(:controller_wiki_edit_after_save, {:params => params, :page => @page})
......
373 390
    end
374 391
  end
375 392

  
393
  def find_copy_from
394
    @copy_from = if params[:copy_from_project_id] && params[:copy_from]
395
                   Project.find(params[:copy_from_project_id]).wiki.try(:find_page, params[:copy_from]) rescue nil
396
                 else
397
                   nil
398
                 end
399
  end
400

  
376 401
  def redirect_to_page(page)
377 402
    if page.project && page.project.visible?
378 403
      redirect_to :action => action_name, :project_id => page.project, :id => page.title
app/helpers/wiki_helper.rb
46 46
    end
47 47
  end
48 48

  
49
  def wiki_page_project_options_for_select(page)
50
    projects = Project.allowed_to(:add_wiki_pages).joins(:wiki).preload(:wiki).to_a
51
    projects.select! {|p| p.enabled_module_names.include?('wiki') }
52
    projects << page.project unless projects.include?(page.project)
53

  
54
    project_tree_options_for_select(projects, :selected => page.project)
55
  end
56

  
49 57
  def wiki_page_breadcrumb(page)
50 58
    breadcrumb(
51 59
      page.ancestors.reverse.collect do |parent|
app/models/wiki_page.rb
243 243

  
244 244
  # Saves the page and its content if text was changed
245 245
  # Return true if the page was saved
246
  def save_with_content(content)
246
  def save_with_content(content, options={ :copy_from => nil, :copy_attachments => false, :copy_child_pages => false})
247 247
    ret = nil
248 248
    transaction do
249 249
      ret = save
250 250
      if content.text_changed?
251 251
        begin
252 252
          self.content = content
253
          copy_from, copy_attachments, copy_child_pages = options.values_at(:copy_from, :copy_attachments, :copy_child_pages)
254
          if copy_from && copy_attachments
255
            self.attachments = copy_from.attachments.collect do |attachment|
256
              attachment.copy(:container => self)
257
            end
258
          end
259
          if copy_from && copy_child_pages
260
            self.children = copy_from.children.collect do |child_page|
261
              page = child_page.copy(:parent => self)
262
              unless page.save_with_content(child_page.content.try(:dup),
263
                                            :copy_from => child_page,
264
                                            :copy_attachments => copy_attachments,
265
                                            :copy_child_pages => copy_child_pages)
266
                page.errors.full_messages.each do |message|
267
                  self.errors.add :base, message
268
                end
269
                raise ActiveRecord::RecordNotSaved
270
              end
271
              page
272
            end
273
          end
253 274
        rescue ActiveRecord::RecordNotSaved
254 275
          ret = false
255 276
        end
......
259 280
    ret
260 281
  end
261 282

  
283
  def copy(options={ :parent => nil })
284
    wiki_page = self.class.new
285
    wiki_page.parent = options[:parent]
286
    wiki_page.wiki = options[:parent].try(:wiki) || self.wiki
287
    wiki_page.title = self.title
288
    wiki_page.content = self.content.try(:dup)
289
    suffix = 1
290
    while wiki_page.validate == false && wiki_page.errors[:title].present?
291
      wiki_page.title = self.title + "_#{suffix}"
292
      suffix += 1
293
    end
294
    wiki_page
295
  end
296

  
297
  def has_attachments?(options={ :include_childrens => false })
298
    if self.children && options[:include_childrens]
299
      self.attachments.any? || self.children.any? { |c| c.has_attachments?(options) }
300
    else
301
      self.attachments.any?
302
    end
303
  end
304

  
262 305
  def deleted_attachment_ids
263 306
    Array(@deleted_attachment_ids).map(&:to_i)
264 307
  end
app/views/wiki/_copy_modal.html.erb
1
<h3 class="title"><%=l(:label_wiki_page_copy)%></h3>
2

  
3
<%= labelled_form_for :page, @page,
4
            :url => project_copy_wiki_page_path(@project),
5
            :method => 'post',
6
            :remote => true do |f| %>
7

  
8
  <%= render_error_messages @page.errors.full_messages_for(:title) %>
9
  <%= hidden_field_tag 'copy_from_project_id', @project.id %>
10
  <%= hidden_field_tag 'copy_from', params[:copy_from] %>
11

  
12
  <div class="box tabular">
13
    <p>
14
      <label for="copy_to_project_id"><%=l(:label_project)%><span class="required"> *</span></label>
15
      <%= select_tag :copy_to_project_id, wiki_page_project_options_for_select(@page), :label => :label_project %></p>
16
    <p>
17
      <%= f.text_field :title, :name => 'title', :size => 60, :required => true %>
18
      <em class="info"><%= l(:text_unallowed_characters) %>: , . / ? ; : |</em>
19
    </p>
20
    <% if params[:parent].present? %>
21
    <p id="copy_parent_page">
22
      <label class="inline">
23
        <%= check_box_tag 'parent', params[:parent], true %>
24
        <%= l(:field_parent_title) %>: <%= params[:parent] %>
25
      </label>
26
    </p>
27
    <% end %>
28
  </div>
29
  <p class="buttons">
30
    <%= submit_tag l(:label_next), :name => nil %>
31
    <%= submit_tag l(:button_cancel), :name => nil, :onclick => "hideModal(this);", :type => 'button' %>
32
  </p>
33
<% end %>
34
<%= javascript_tag do %>
35
$('#copy_to_project_id').change(function() {
36
  if ($('#copy_from_project_id').val()==$(this).val()) {
37
    $('#copy_parent_page').show();
38
  }else{
39
    $('#copy_parent_page').hide();
40
  }
41
});
42
<% end %>
app/views/wiki/edit.html.erb
1 1
<%= wiki_page_breadcrumb(@page) %>
2 2

  
3
<%= error_messages_for 'page' %>
4

  
3 5
<h2><%= @page.pretty_title %></h2>
4 6

  
5 7
<%= form_for @content, :as => :content,
......
11 13
<%= hidden_field_tag 'section_hash', @section_hash %>
12 14
<% end %>
13 15
<%= error_messages_for 'content' %>
16
<%= hidden_field_tag 'copy_from_project_id', params[:copy_from_project_id] if params[:copy_from_project_id] %>
17
<%= hidden_field_tag 'copy_from', params[:copy_from] if params[:copy_from] %>
14 18

  
15 19
<div class="box tabular">
16 20
<%= text_area_tag 'content[text]', @text, :cols => 100, :rows => 25, :accesskey => accesskey(:edit),
......
34 38
<% end %>
35 39

  
36 40
<p><label><%= l(:field_comments) %></label><%= f.text_field :comments, :size => 120, :maxlength => 1024 %></p>
41
<% if @copy_from && @copy_from.has_attachments?(:include_childrens => true) %>
42
<p>
43
  <label for="copy_attachments"><%= l(:label_copy_attachments) %></label>
44
  <%= check_box_tag 'copy_attachments', '1', true %>
45
</p>
46
<% end %>
47
<% if @copy_from && @copy_from.children.any? %>
48
<p>
49
  <label for="copy_child_pages"><%= l(:label_copy_child_pages) %></label>
50
  <%= check_box_tag 'copy_child_pages', '1', true %>
51
</p>
52
<% end %>
37 53
<fieldset>
38 54
<legend><%=l(:label_attachment_plural)%></legend>
39 55
<% if @page.attachments.any? && @page.safe_attribute?('deleted_attachment_ids') %>
app/views/wiki/new.js.erb
1
$('#ajax-modal').html('<%= escape_javascript(render :partial => 'wiki/new_modal') %>');
1
$('#ajax-modal').html('<%= escape_javascript(render :partial => params['copy_from'] ? 'wiki/copy_modal' : 'wiki/new_modal') %>');
2 2
showModal('ajax-modal', '600px');
app/views/wiki/show.html.erb
23 23

  
24 24
    <% if User.current.allowed_to?(:edit_wiki_pages, @project) %>
25 25
      <%= link_to l(:label_wiki_page_new), new_project_wiki_page_path(@project, :parent => @page.title), :remote => true, :class => 'icon icon-add' %>
26
      <%= link_to l(:button_copy), project_copy_wiki_page_path(@project, :copy_from => @page.title, :parent => @page.parent.try(:title)), :remote => true, :class => 'icon icon-copy' %>
26 27
    <% end %>
27 28
  <% end %>
28 29
</div>
config/locales/en.yml
857 857
  label_wiki_page: Wiki page
858 858
  label_wiki_page_plural: Wiki pages
859 859
  label_wiki_page_new: New wiki page
860
  label_wiki_page_copy: Copy wiki page
860 861
  label_index_by_title: Index by title
861 862
  label_index_by_date: Index by date
862 863
  label_current_version: Current version
......
1022 1023
  label_export_options: "%{export_format} export options"
1023 1024
  label_copy_attachments: Copy attachments
1024 1025
  label_copy_subtasks: Copy subtasks
1026
  label_copy_child_pages: Copy child pages
1025 1027
  label_item_position: "%{position} of %{count}"
1026 1028
  label_completed_versions: Completed versions
1027 1029
  label_search_for_watchers: Search for watchers to add
config/locales/ja.yml
1063 1063
  label_attribute_of_assigned_to: 担当者の %{name}
1064 1064
  label_attribute_of_fixed_version: 対象バージョンの %{name}
1065 1065
  label_copy_subtasks: 子チケットをコピー
1066
  label_copy_child_pages: 子ページをコピー
1066 1067
  label_copied_to: コピー先
1067 1068
  label_copied_from: コピー元
1068 1069
  label_any_issues_in_project: 次のプロジェクト内のチケット
......
1199 1200
  mail_body_settings_updated: ! '下記の設定が変更されました:'
1200 1201
  field_remote_ip: IPアドレス
1201 1202
  label_wiki_page_new: 新しいWikiページ
1203
  label_wiki_page_copy: Wikiページのコピー
1202 1204
  label_relations: 関係
1203 1205
  button_filter: フィルタ
1204 1206
  mail_body_password_updated: パスワードが変更されました。
config/routes.rb
190 190
    end
191 191

  
192 192
    match 'wiki/index', :controller => 'wiki', :action => 'index', :via => :get
193
    match 'wiki/:copy_from/copy', :to => 'wiki#new', :as => 'copy_wiki_page', via: [:get, :post]
193 194
    resources :wiki, :except => [:index, :create], :as => 'wiki_page' do
194 195
      member do
195 196
        get 'rename'
test/functional/wiki_controller_test.rb
1279 1279
    assert_response :success
1280 1280
    assert_select 'head>meta[name="robots"]', false
1281 1281
  end
1282

  
1283
  def test_get_new_with_copy_from
1284
    @request.session[:user_id] = 2
1285

  
1286
    get :new, :params => {:project_id => 'ecookbook', :copy_from => 'CookBook_documentation'}
1287
    assert_response :success
1288
    assert_select 'input[name=?][value=?]', 'title', 'CookBook_documentation_1'
1289
  end
1290

  
1291
  def test_post_new_with_copy_from_should_redirect_to_edit_wity_copy_params
1292
    @request.session[:user_id] = 2
1293

  
1294
    post :new, :params => {:project_id => 'ecookbook', :title => 'CookBook documentation 1', :copy_from => 'CookBook_documentation', :copy_to_project_id => 'ecookbook'}
1295
    assert_redirected_to '/projects/ecookbook/wiki/CookBook_documentation_1?copy_from=CookBook_documentation&copy_from_project_id=1'
1296
  end
1297

  
1298
  def test_show_with_copy_from_should_copy_content
1299
    @request.session[:user_id] = 2
1300

  
1301
    get :show, :params => {:project_id => 1, :id => 'CookBook_documentation_1', :copy_from => 'CookBook_documentation', :copy_from_project_id => 1}
1302
    assert_response :success
1303
    assert_select 'textarea[name=?]', 'content[text]', :text => /h1. CookBook documentation/
1304
    assert_select 'textarea[name=?]', 'content[text]', :text => /{{child_pages}}/
1305
    assert_select 'textarea[name=?]', 'content[text]', :text => /Some updated \[\[documentation\]\] here with gzipped history/
1306
  end
1307

  
1308
  def test_update_with_copy_from_and_copy_attachments_should_copy_attachments
1309
    @request.session[:user_id] = 2
1310

  
1311
    assert_difference 'WikiPage.count', 1 do
1312
      assert_difference 'WikiContent.count', 1 do
1313
        assert_difference 'Attachment.count', 2 do
1314
          post :update, :params => {
1315
            :project_id => 1,
1316
            :id => 'CookBook_documentation_1',
1317
            :content => {
1318
              :comments => 'Copy Wiki Page',
1319
              :text => 'h1. CookBook documentation',
1320
              :varsion => 0
1321
            },
1322
            :copy_from => 'CookBook_documentation',
1323
            :copy_from_project_id => 1,
1324
            :copy_attachments => '1'
1325
          }
1326
        end
1327
      end
1328
    end
1329
  end
1330

  
1331
  def test_update_with_copy_from_and_copy_child_pages
1332
    @request.session[:user_id] = 2
1333

  
1334
    assert_difference 'WikiPage.count', 4 do
1335
      assert_difference 'WikiContent.count', 4 do
1336
        post :update, :params => {
1337
          :project_id => 1,
1338
          :id => 'Another_page_1',
1339
          :content => {
1340
            :comments => 'Copy Wiki Page',
1341
            :text => 'h1. Another page',
1342
            :varsion => 0
1343
          },
1344
          :copy_from => 'Another_page',
1345
          :copy_from_project_id => 1,
1346
          :copy_child_pages => '1'
1347
        }
1348
      end
1349
    end
1350
  end
1282 1351
end
test/unit/wiki_page_test.rb
20 20
require_relative '../test_helper'
21 21

  
22 22
class WikiPageTest < ActiveSupport::TestCase
23
  fixtures :projects, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
23
  fixtures :projects, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions, :users
24 24

  
25 25
  def setup
26 26
    User.current = nil
......
207 207
    assert_equal 3, diff.content_to.version
208 208
    assert_equal 1, diff.content_from.version
209 209
  end
210

  
211
  def test_copy
212
    page = WikiPage.create!(:wiki => @wiki, :title => 'CopyFrom')
213
    page.content = WikiContent.new(:text => '#h1 Copy From', :comments => 'test')
214

  
215
    copy_page_1 = page.copy
216
    assert_equal page.wiki, copy_page_1.wiki
217
    assert_equal '#h1 Copy From', copy_page_1.content.text
218
    assert_equal 'CopyFrom_1', copy_page_1.title
219
    copy_page_1.save
220

  
221
    copy_page_2 = page.copy
222
    assert_equal page.wiki, copy_page_2.wiki
223
    assert_equal '#h1 Copy From', copy_page_2.content.text
224
    assert_equal 'CopyFrom_2', copy_page_2.title
225
  end
226

  
227
  def test_copy_spacify_parent
228
    parent_page = WikiPage.create!(:wiki => @wiki, :title => 'CopyFrom Parent')
229
    parent_page.content = WikiContent.new(:text => '#h1 Copy From Parent', :comments => 'test')
230
    page = WikiPage.create!(:wiki => @wiki, :title => 'CopyFrom', :parent => parent_page)
231
    page.content = WikiContent.new(:text => '#h1 Copy From', :comments => 'test')
232
    assert_equal parent_page, page.parent
233
    assert_equal 1, parent_page.children.count
234

  
235
    copy_page_1 = page.copy(:parent => parent_page)
236
    assert_equal page.wiki, copy_page_1.wiki
237
    assert_equal '#h1 Copy From', copy_page_1.content.text
238
    assert_equal 'CopyFrom_1', copy_page_1.title
239
    assert_equal parent_page, copy_page_1.parent
240
    copy_page_1.save
241
    assert_equal 2, parent_page.children.count
242

  
243
    copy_page_2 = page.copy(:parent => nil)
244
    assert_equal page.wiki, copy_page_2.wiki
245
    assert_equal '#h1 Copy From', copy_page_2.content.text
246
    assert_equal 'CopyFrom_2', copy_page_2.title
247
    assert_nil copy_page_2.parent
248
    copy_page_2.save
249
    assert_equal 2, parent_page.children.count
250
  end
251

  
252
  def test_has_attachments
253
    page = WikiPage.create!(:wiki => @wiki, :title => 'Parent')
254
    child1 = WikiPage.create!(:wiki => @wiki, :title => 'Child1', :parent => page)
255
    child11 = WikiPage.create!(:wiki => @wiki, :title => 'Child11', :parent => child1)
256
    Attachment.generate!(:container => child11, :filename => 'test.txt')
257
    page.reload
258

  
259
    assert_equal true, page.has_attachments?(:include_childrens => true)
260
    assert_equal false, page.has_attachments?(:include_childrens => false)
261
    assert_equal false, page.has_attachments?
262
    assert_equal false, child1.has_attachments?(:include_childrens => true)
263
    assert_equal false, child1.has_attachments?(:include_childrens => false)
264
    assert_equal false, child1.has_attachments?
265
    assert_equal true, child11.has_attachments?(:include_childrens => true)
266
    assert_equal true, child11.has_attachments?(:include_childrens => false)
267
    assert_equal true, child11.has_attachments?
268
  end
269

  
270
  def test_save_with_content_with_copy_options
271
    page = WikiPage.create!(:wiki => @wiki, :title => 'Parent')
272
    page.content = WikiContent.new(:text => '#h1 Copy From Content', :comments => 'test')
273
    page.save
274
    child1 = WikiPage.create!(:wiki => @wiki, :title => 'Child1', :parent => page)
275
    child1.content = WikiContent.new(:text => '#h1 Copy From Child Content', :comments => 'test')
276
    Attachment.generate!(:container => child1, :filename => 'test.txt')
277
    page.reload
278

  
279
    new_page = WikiPage.new(:wiki => @wiki, :title => 'Parent_1')
280
    content = page.content.dup
281
    content.text = '#h1 Copy to Content'
282
    assert new_page.save_with_content(content, :copy_from => page, :copy_attachments => true, :copy_child_pages => true)
283
    assert_equal '#h1 Copy to Content', new_page.content.text
284
    assert_equal 1, new_page.children.count
285
    assert_equal 'Child1_1', new_page.children.first.title
286
    assert_equal 1, new_page.children.first.attachments.count
287
  end
210 288
end
(2-2/3)