RedmineのAプロジェクトにあるWikiをBプロジェクトにコピーする

表題のママ。
開発用のWikiで集積してたデータを運用用のプロジェクト内に移動したいみたいな話。
redmineのDBを直接読み書きできる人向け。
**雑に作ってるので流す前にバックアップ必須。**


[Copy Wiki to another project \- Redmine](https://www.redmine.org/boards/1/topics/13986) でアップロードされているSQLでだいたい解決するものの、

- Wikiページに添付されたファイルが複製されない
- パラメータが`wiki_id`になっているのがちょっと微妙

というのがあって修正した。

## redmineのdatabase

![](http://www.plantuml.com/plantuml/png/RL9BRiCW4Drp2dQH8Wq7g1GfH-Ykcqe8mJIPri4MJ5jLLUvUIAO6nrxPVlC-Rmp7p2xnvsLGVd0vQyVi_Fc2aVElqfeYQmhouiV8PM1N0FzCgAxYCANn7NsLY_YgPF9D7zJW-X2khQoEPCKuRs3s2rEcCOhHOlGOPYTtmZvH-2jUma2fbbRglxw60TyOxgNrqxoMeN2uOQOhVoVj0RhO6Mm-NKxZQG1ORbtInBD1UqXqEZDi_ulEhZ7bACMn-p9LxYXYkftSOVTIrCz5l7kDsxIoSqimfTmHOoYNvWy0)

だいたいこんな構造になってる。

<details><summary>plantumlソース</summary>
<div>

```
@startuml
class attachments{
  int id
  int content_id
  int content_type
}
class projects{
int id
}

class wikis{
int id
int project_id
}
class wiki_contents
class wiki_content_versions{
int wiki_content_id
}

class wiki_pages{
int id
int wiki_id
}

class wiki_redirects

projects -left- wikis : projects.id=wikis.project_id
wikis *-- wiki_pages
wikis *-left- wiki_redirects
wiki_pages --- wiki_contents : id
wiki_contents *-right- wiki_content_versions
wiki_pages *-right- attachments : attachments.container_type='WikiPage'\nattachments.container_id=wiki_pages.id
@enduml
```
</div>
</details>

## まずはプロジェクトidを調べる

redmineのDBの`projects.id`を使ってコピー元、コピー先のidを調べておく。

## コピー用プロシージャを登録

以下のreplicawiki2.sqlをローカルに保存して

```bash
$ mysql -p <redmine_database> < replicawiki2.sql
```
で登録する。

**注意：コピー先にWikiページが存在する場合一度削除されます。**

```sql:replicawiki2.sql
delimiter //
drop procedure if exists replicawiki2;
create procedure replicawiki2(projectid_from int, projectid_to int)
	begin
		declare done int default 0;
		declare last_id int;
		declare st_page varchar(255); 
		declare curso int;
		declare id_or int;
		declare id_nu int;
        declare wikid_from int;
        declare wikid_to int
		declare pages cursor for select id from wiki_pages wp where wp.wiki_id = wikid_from;
		declare copied_pages cursor for select id_original, id_new from aux_map;
		declare continue handler for not found set done = 1;

        -- get wiki id
        select wikis.id into wikid_from from wikis where wikis.project_id = projectid_from;
        select wikis.id into wikid_to   from wikis where wikis.project_id = projectid_to;

		-- caution this erase all current wiki pages of destination (to) wiki
        delete from attachments where container_type='wikipages' and container_id in (select id from wiki_pages where wiki_pages.wiki_id = wikid_to);
        delete from wiki_contents where id in (select id from wiki_pages where wiki_pages.wiki_id = wikid_to);
		delete from wiki_redirects where wiki_id = wikid_to ;
		delete from wiki_pages where wiki_pages.wiki_id = wikid_to ;
		
		open pages;
		
		-- fix wikis start page
		select wikis.start_page into st_page from wikis where id = wikid_from;
		update wikis set start_page = st_page where id = wikid_to;
	
		-- aux table to replicate the hierachy 
		drop table if exists aux_map;
		create table aux_map(id_original int, id_new int);
		
		-- copy redirects
		insert into wiki_redirects (wiki_id, `title`, redirects_to, created_on,redirects_to_wiki_id)
			select wikid_to as wiki_id, `title`, redirects_to, created_on ,wiki_to as redirects_to_wiki_id from wiki_redirects where wiki_id = wikid_from;
	
		-- copy all the wiki pages to origin to dest. and fill the auxiliary hierachy table  
		repeat
			fetch pages into curso;
			insert into wiki_pages (wiki_id, title, created_on, protected, parent_id)
				select wikid_to as wiki_id, wp.title, wp.created_on, wp.protected, wp.parent_id from wiki_pages wp where wp.id = curso limit 1;
			set last_id = last_insert_id();
			insert into aux_map (id_original, id_new)
				values(curso, last_id);	
		until done end repeat;
		
		set done = 0;
		open copied_pages;
		
		-- update hierachy and copy all the wiki contents.
		repeat 
			fetch copied_pages into id_or, id_nu;
			update wiki_pages set parent_id = id_nu where wiki_id = wikid_to and parent_id = id_or;
			insert into wiki_contents (page_id, author_id, `text`, `comments`, `updated_on`, `version`)
				select id_nu as page_id, author_id, `text`, `comments`, `updated_on`, `version` from wiki_contents wc where wc.page_id = id_or;
		until done end repeat;

        -- clone attachments
        insert into attachments(container_id,container_type,filename,disk_filename,filesize,content_type,digest,downloads,author_id,created_on,description,disk_directory)
        select
            aux_map.id_new,
            container_type,
            filename,
            concat('c',disk_filename),
            filesize,
            content_type,
            digest,
            downloads,
            author_id,
            created_on,
            description,
            disk_directory
        from `attachments`
        inner join aux_map on attachments.container_id = id_original
        where container_type='wikipage' ;

        -- print cannonical attachment copy script
        select
            concat('cp -n ',disk_directory ,'/', disk_filename,' ',disk_directory ,'/c', disk_filename,' ;') as pathto
        from `attachments`
        inner join aux_map on attachments.container_id = id_original
        where container_type='wikipage' ;

		close pages;
		close copied_pages;
		
		-- deleting auxiliary table
		drop table if exists aux_map;
	end
	//
```

## 実行

```sql
call replicawiki2(1,2); -- project_id=1から2へWikiの内容をコピー
```

これを実行するとコンソールに物理ファイルコピー用のコマンドが出力される

```bash:出力結果
cp -n 2021/03/150222162940_fb8abc027e5f98ab48c8b90bd550507d.png 2021/03/c150222162940_fb8abc027e5f98ab48c8b90bd550507d.png ;
cp -n 2021/03/150312113518_f257bd912a377aea98b7550787460168.png 2021/03/c150312113518_f257bd912a377aea98b7550787460168.png ;
# : 以下略
```

`<redmine_home>/files` で↑のコマンドを実行すればOK