trac_migration.rb

Updated Conversion - Karl Heinz Marbaise, 2008-11-07 16:12

Download (12.2 KB)

 
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