1
|
# redMine - project management software
|
2
|
# Copyright (C) 2008 Karl Heinz Marbaise
|
3
|
#
|
4
|
# This program is free software; you can redistribute it and/or
|
5
|
# modify it under the terms of the GNU General Public License
|
6
|
# as published by the Free Software Foundation; either version 2
|
7
|
# of the License, or (at your option) any later version.
|
8
|
#
|
9
|
# This program is distributed in the hope that it will be useful,
|
10
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
# GNU General Public License for more details.
|
13
|
#
|
14
|
# You should have received a copy of the GNU General Public License
|
15
|
# along with this program; if not, write to the Free Software
|
16
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
|
18
|
# This class is intended to do the conversion of trac wiki content
|
19
|
# into Redmine wiki content. This is needed cause
|
20
|
# trac wiki and Redmine wiki have different markup languages.
|
21
|
|
22
|
module TracMigrate
|
23
|
class TracWiki
|
24
|
|
25
|
public
|
26
|
|
27
|
# This will hold the renumbered ticket numbers
|
28
|
# of trac.
|
29
|
@@ticket_map = []
|
30
|
|
31
|
def TracWiki.ticketmap(ticket, issue)
|
32
|
@@ticket_map[ticket] = issue
|
33
|
end
|
34
|
|
35
|
def TracWiki.ticketmap
|
36
|
return @@ticket_map
|
37
|
end
|
38
|
|
39
|
# This method will do the whole conversion between
|
40
|
# trac wiki and Redmine wiki.
|
41
|
#
|
42
|
def TracWiki.wiki(text)
|
43
|
text = text.gsub(/(\n|\r)(\n|\r)?/, "\n")
|
44
|
# Titles
|
45
|
text = text.gsub(/^(\=+)\s(.+)\s(\=+)/) {|s| "\nh#{$1.length}. #{$2}\n"}
|
46
|
|
47
|
# External Links
|
48
|
text = text.gsub(/\[(http[^\s]+)\s+([^\]]+)\]/) {|s| "\"#{$2}\":#{$1}"}
|
49
|
|
50
|
text = linebreak(text)
|
51
|
|
52
|
# Do ticket extractions and renumbering.
|
53
|
text = ticket_links(text)
|
54
|
|
55
|
# Do mailto extraction and conversion
|
56
|
text = mailto_links(text)
|
57
|
|
58
|
# Usual wiki links
|
59
|
text = wiki_links(text)
|
60
|
|
61
|
# Do the changeset link conversion
|
62
|
text = changeset_links(text)
|
63
|
|
64
|
# First see if we have to convert something as monospace.
|
65
|
text = code_monospace(text)
|
66
|
|
67
|
#Do the code highlighting stuff.
|
68
|
text = code_highlighting(text)
|
69
|
|
70
|
# emphasize etc.
|
71
|
text = highlight(text)
|
72
|
|
73
|
# different kind of lists.
|
74
|
text = lists(text)
|
75
|
|
76
|
# Table stuff
|
77
|
text = tables(text)
|
78
|
|
79
|
# Links to source area (repository browser in trac)
|
80
|
text = source_links(text)
|
81
|
|
82
|
# Links to versions in Redmine (trac milestones)
|
83
|
text = milestone_links(text)
|
84
|
|
85
|
# links to attachments
|
86
|
text = attachment_links(text)
|
87
|
|
88
|
# Links to pages UsingJustWikiCaps
|
89
|
text = text.gsub(/([^!]|^)(^| )([A-Z][a-z]+[A-Z][a-zA-Z]+)/, '\\1\\2[[\3]]')
|
90
|
# Normalize things that were supposed to not be links
|
91
|
# like !NotALink
|
92
|
text = text.gsub(/(^| )!([A-Z][A-Za-z]+)/, '\1\2')
|
93
|
|
94
|
#TODO: Blockquotes ?
|
95
|
#TODO: http://trac.edgewall/wiki/WikiRestructuredText#BiggerReSTExample
|
96
|
end
|
97
|
|
98
|
private
|
99
|
|
100
|
# This will convert the trac monospace marker
|
101
|
# into Redmine's one (currently inline code).
|
102
|
#TODO: Check to see if a better choice exists?
|
103
|
#
|
104
|
def TracWiki.code_monospace(text)
|
105
|
text = text.gsub(/\{\{\{(.*?)\}\}\}/, '@\1@')
|
106
|
end
|
107
|
|
108
|
# This method will convert code highlighting
|
109
|
# of trac where {{{ and }}} is used to separate
|
110
|
# it from the usual wiki content.
|
111
|
# A shebang line can be used to indicate a particular
|
112
|
# code high lighter in Trac (#!java etc.)
|
113
|
#
|
114
|
# TODO: Add warnings if we have situations which can't be converted
|
115
|
# unknown or not supported code tags!
|
116
|
def TracWiki.code_highlighting(text)
|
117
|
# We would like to convert the Code highlighting too
|
118
|
# This will go into the next line.
|
119
|
|
120
|
shebang_line = false
|
121
|
if text =~/^\{\{\{(\n|\r)(\n|\r)?\#\![a-z]+(\n|\r)(\n|\r)?/m
|
122
|
text = text.gsub(/^\{\{\{(\n|\r)(\n|\r)?\#\!([a-z]+)(\n|\r)(\n|\r)?/m, '<pre><code class="\3">')
|
123
|
shebang_line = true
|
124
|
end
|
125
|
|
126
|
text = text.gsub(/^\{\{\{/, '<pre>')
|
127
|
|
128
|
if shebang_line
|
129
|
text = text.gsub(/^\}\}\}/, '</code></pre>')
|
130
|
else
|
131
|
text = text.gsub(/^\}\}\}/, '</pre>')
|
132
|
end
|
133
|
end
|
134
|
|
135
|
# Things like _Text_ '''''AAA''''' etc.
|
136
|
#
|
137
|
def TracWiki.highlight(text)
|
138
|
# Highlighting
|
139
|
# bold italic
|
140
|
text = text.gsub(/'''''([^\s])/, '_*\1')
|
141
|
# bold italic
|
142
|
text = text.gsub(/([^\s])'''''/, '\1*_')
|
143
|
# bold
|
144
|
text = text.gsub(/'''/, '*')
|
145
|
# italic
|
146
|
text = text.gsub(/''/, '_')
|
147
|
# underline.
|
148
|
text = text.gsub(/__/, '+')
|
149
|
# stike-through
|
150
|
text = text.gsub(/~~/, '-')
|
151
|
# monospace
|
152
|
text = text.gsub(/`/, '@')
|
153
|
# subscript
|
154
|
text = text.gsub(/,,/, '~')
|
155
|
# TODO: superscript => CHECK HOW THIS IS DONE IN REDMINE
|
156
|
#text = text.gsub(//, '~')
|
157
|
end
|
158
|
|
159
|
# The lists will be converted.
|
160
|
#
|
161
|
def TracWiki.lists(text)
|
162
|
# Lists
|
163
|
text = text.gsub(/^([ ]+)\* /) {|s| '*' * $1.length + " "}
|
164
|
# TODO: definition list => trac manual
|
165
|
# TODO: unordered lists (1. 2. etc.)
|
166
|
end
|
167
|
|
168
|
# This method will do the conversion of
|
169
|
# all ticket entries.
|
170
|
# Here we do a renumbering of the ticket
|
171
|
# numbers into appropiate issue numbers of redmine.
|
172
|
#
|
173
|
#TODO: Check the ticket re-writing in the first three cases; should be done too!
|
174
|
def TracWiki.ticket_links(text)
|
175
|
# Situations like the following:
|
176
|
# - [ticket:234 Text],[ticket:234 This is a test]
|
177
|
# Hints: This is a little hack, cause in Redmine it seemed not to be
|
178
|
# possible to have a link to an issue with different text like
|
179
|
# in Trac. So this uses the URL syntax to make a link to the
|
180
|
# particular issue
|
181
|
text = text.gsub(/\[ticket\:([^\ ]+)\ (.+?)\]/, '"\2":/issues/show/\1')
|
182
|
|
183
|
# Situations like the following:
|
184
|
# - [ticket:234]
|
185
|
text = text.gsub(/\[ticket\:([^\ ]+)\]/, '#\1')
|
186
|
|
187
|
# Situations like:
|
188
|
# - ticket:1234
|
189
|
# - #1 is working cause Redmine uses the same syntax.
|
190
|
text = text.gsub(/ticket\:([^\ ]+)/, '#\1')
|
191
|
|
192
|
# Ticket number re-writing
|
193
|
text = text.gsub(/#(\d+)/) do |s|
|
194
|
if $1.length < 10
|
195
|
@@ticket_map[$1.to_i] ||= $1
|
196
|
"\##{@@ticket_map[$1.to_i] || $1}"
|
197
|
else
|
198
|
s
|
199
|
end
|
200
|
end
|
201
|
end
|
202
|
|
203
|
# This will convert the links to revisions
|
204
|
# or in Subversion terms Changesets.
|
205
|
#
|
206
|
def TracWiki.changeset_links(text)
|
207
|
# changeset links [123]
|
208
|
text = text.gsub(/\[(\d+)\]/, 'r\1')
|
209
|
# changeset:122
|
210
|
text = text.gsub(/changeset\:(\d+)/, 'r\1')
|
211
|
# r123 => r123 (No conversion needed.
|
212
|
#text = text.gsub(/r(\d+)/, 'r\1')
|
213
|
end
|
214
|
|
215
|
# This method will do the conversion of mailto entries
|
216
|
# in trac which will be converted to Redmine syntax.
|
217
|
#
|
218
|
def TracWiki.mailto_links(text)
|
219
|
# Situations like:
|
220
|
# - [mailto:user@test.de]
|
221
|
text = text.gsub(/\[mailto\:([^\ ]+)\]/, '\1')
|
222
|
# - [mailto:user@test.de This is the text]
|
223
|
# - Here we use a hack to have a different text in relationship
|
224
|
# with an email address.
|
225
|
text = text.gsub(/\[mailto\:([^\ ]+)\ (.+?)\]/, '"\2":mailto:\1')
|
226
|
end
|
227
|
|
228
|
# This will convert the milestone references from trac to redmine.
|
229
|
#
|
230
|
def TracWiki.milestone_links(text)
|
231
|
# First Situation:
|
232
|
# - [milestone:"0.1.0 Mercury" Milestone 0.1.0 (Mercury)]
|
233
|
# Hint: The text "Milestone 0.1.0 (Mercury)" is not converted,
|
234
|
# cause Redmine's wiki does not support this.
|
235
|
#TODO: May be we can use a hack to convert into something visual identical
|
236
|
text = text.gsub(/\[milestone\:\"([^\"]+)\"\ (.+?)\]/, 'version:"\1"')
|
237
|
|
238
|
# Second Situation:
|
239
|
# [milestone:"0.1.0 Mercury"]
|
240
|
# milestone:"0.1.2 Mercury"
|
241
|
text = text.gsub(/\[milestone\:\"([^\"]+)\"\]/, 'version:"\1"')
|
242
|
text = text.gsub(/milestone\:\"([^\"]+)\"/, 'version:"\1"')
|
243
|
# Third Situation:
|
244
|
# [milestone:0.1.0]
|
245
|
text = text.gsub(/\[milestone\:([^\ ]+)\]/, 'version:\1')
|
246
|
# Forth situation:
|
247
|
# milestone:xyzabc
|
248
|
text = text.gsub(/milestone\:([^\ ]+)/, 'version:\1')
|
249
|
end
|
250
|
|
251
|
# This will convert source code links. In trac
|
252
|
# these are links to the repository browser.
|
253
|
#
|
254
|
def TracWiki.source_links(text)
|
255
|
# Links to repository browser trac: source:/some/path/file@123
|
256
|
# source:/some/path/file@123#L10
|
257
|
|
258
|
# Example 1:
|
259
|
# [source:/tags/JAGOSI-0.0.10/jagosiapi/src/main/java/com/soebes/jagosi/api/JaGoSI.java@latest#L626 Line 626]
|
260
|
text = text.gsub(/\[source\:([a-zA-Z0-9_\/\.\-]+)(\@latest)#L(\d+) (.*?)\]/, 'source:\1#L\3')
|
261
|
|
262
|
# Example 2:
|
263
|
# [source:/tags/JAGOSI-0.0.10/jagosiapi/src/main/java/com/soebes/jagosi/api/JaGoSI.java@699#L626 Line 626]
|
264
|
text = text.gsub(/\[source\:([a-zA-Z0-9_\/\.\-]+)\@(\d+)#L(\d+) (.*?)\]/, 'source:\1@\2#L\3')
|
265
|
|
266
|
# Example 3:
|
267
|
# [source:/trunk/jagosiclient This is Text]
|
268
|
# Hint: This is a hack to make it possible to link sources with different text.
|
269
|
# This seemed to be not possible in Redmine.
|
270
|
#TODO: Check this. There must be added the project identifier to get working links.
|
271
|
text = text.gsub(/\[source\:([a-zA-Z0-9_\/\.\-]+) (.*?)\]/, '"\2":/repositories/browse/\1')
|
272
|
end
|
273
|
|
274
|
# Attachments within wiki pages
|
275
|
#
|
276
|
# In trac the following parts are valid:
|
277
|
# attachment:the_file.txt creates a link to the attachment the_file.txt of the current object
|
278
|
# attachment:wiki:MyPage:the_file.txt creates a link to the attachment the_file.txt of the MyPage wiki page
|
279
|
# attachment:ticket:753:the_file.txt creates a link to the attachment the_file.txt of the ticket 753
|
280
|
def TracWiki.attachment_links(text)
|
281
|
# [attachment:JaGoSIAPI-0.0.10.jar] => attachment:JaGoSIAPI-0.0.10.jar
|
282
|
text = text.gsub(/\[attachment\:([a-zA-Z0-9_\/\.\-]+)\]/, 'attachment:\1')
|
283
|
# [attachment:JaGoSIAPI-0.0.10.jar This is text] => "This is texgt":attachment:JaGoSIAPI-0.0.10.jar
|
284
|
text = text.gsub(/\[attachment\:([a-zA-Z0-9_\/\.\-]+) (.*?)\]/, '"\2":attachment:\1')
|
285
|
|
286
|
# attachment:wiki:MyPage:the_file.txt
|
287
|
#TODO: Currently not supported.
|
288
|
# attachment:ticket:643:the_file.txt
|
289
|
#TODO: Currently not supported.
|
290
|
#
|
291
|
end
|
292
|
|
293
|
# The usual wiki links
|
294
|
#
|
295
|
def TracWiki.wiki_links(text)
|
296
|
# [wiki:"This is Text"]
|
297
|
# [wiki:"String WikiString"]
|
298
|
text = text.gsub(/\[wiki\:"([^\"]+)\"\]/, '[[\1]]')
|
299
|
# ["Text" xxxxx]
|
300
|
text = text.gsub(/\[\"(.+)\".*?\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"}
|
301
|
# [wiki:"text text" more text]
|
302
|
text = text.gsub(/\[wiki:\"(.+)\".*?\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"}
|
303
|
#
|
304
|
text = text.gsub(/\[wiki:\"(.+)\".*?\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"}
|
305
|
text = text.gsub(/\[wiki:([^\s\]]+)\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"}
|
306
|
text = text.gsub(/\[wiki:([^\s\]]+)\s(.*?)\]/) {|s| "[[#{$1.delete(',./?;|:')}|#{$2.delete(',./?;|:')}]]"}
|
307
|
end
|
308
|
|
309
|
# This will convert the particular line breaks in trac
|
310
|
# into simple line breaks.
|
311
|
#
|
312
|
def TracWiki.linebreak(text)
|
313
|
# line break trac support both ways.
|
314
|
text = text.gsub(/\[\[(BR|br)\]\]/, "\n") # This has to go before the rules below
|
315
|
end
|
316
|
|
317
|
# Currently we will only convert the simple
|
318
|
# situation ||X||YY||ZZ||
|
319
|
# Not converted will be: empty lines (trac will ignore them)
|
320
|
# Formatting left, right alignment etc.
|
321
|
#TODO: Support alignment etc.
|
322
|
def TracWiki.tables(text)
|
323
|
# Tables
|
324
|
text = text.gsub(/\|\|/, '|')
|
325
|
end
|
326
|
end
|
327
|
end
|