Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 2
- Log:
Initial import of Instiki 0.11.0 sources from a downloaded Tarball.
Instiki is a Ruby On Rails based Wiki clone.
- Author:
- adh
- Date:
- Sat Jul 22 14:54:51 +0100 2006
- Size:
- 6520 Bytes
- Properties:
- Property svn:executable is set
1 | #!/usr/bin/env ruby |
2 | |
3 | require 'optparse' |
4 | |
5 | OPTIONS = { |
6 | :instiki_root => nil, |
7 | :storage => nil, |
8 | :database => 'mysql' |
9 | } |
10 | |
11 | ARGV.options do |opts| |
12 | script_name = File.basename($0) |
13 | opts.banner = "Usage: ruby #{script_name} [options]" |
14 | |
15 | opts.separator "" |
16 | |
17 | opts.on("-t", "--storage /full/path/to/storage", String, |
18 | "Full path to your storage, ", |
19 | "such as /home/joe/instiki/storage/2500", |
20 | "It should be the directory that ", |
21 | "contains .snapshot files.") do |storage| |
22 | OPTIONS[:storage] = storage |
23 | end |
24 | |
25 | opts.separator "" |
26 | |
27 | opts.on("-i", "--instiki /full/path/to/instiki", String, |
28 | "Full path to your Instiki 0.10 installation, ", |
29 | "such as /home/joe/instiki-0.10.2") do |instiki| |
30 | OPTIONS[:instiki] = instiki |
31 | end |
32 | |
33 | opts.separator "" |
34 | |
35 | opts.on("-o", "--outfile /full/path/to/output_file", String, |
36 | "Full path (including filename!) to where ", |
37 | "you want the SQL output placed, such as ", |
38 | "/home/joe/instiki.sql") do |outfile| |
39 | OPTIONS[:outfile] = outfile |
40 | end |
41 | |
42 | opts.on("-d", "--database {mysql|sqlite|postgres}", String, |
43 | "Target database (they have slightly different syntax)", |
44 | "default: mysql") do |database| |
45 | OPTIONS[:database] = database |
46 | end |
47 | |
48 | opts.separator "" |
49 | |
50 | opts.on_tail("-h", "--help", |
51 | "Show this help message.") { puts opts; exit } |
52 | |
53 | opts.parse! |
54 | end |
55 | |
56 | if OPTIONS[:instiki].nil? or OPTIONS[:storage].nil? or OPTIONS[:outfile].nil? |
57 | $stderr.puts "Please specify full paths to Instiki 0.10 installation and storage," |
58 | $stderr.puts "as well as the path to the output file" |
59 | $stderr.puts |
60 | puts ARGV.options |
61 | exit -1 |
62 | end |
63 | |
64 | if FileTest.exists? OPTIONS[:outfile] |
65 | $stderr.puts "Output file #{OPTIONS[:outfile]} already exists!" |
66 | $stderr.puts "Please specify a new file" |
67 | $stderr.puts |
68 | puts ARGV.options |
69 | exit -1 |
70 | end |
71 | |
72 | raise "Directory #{OPTIONS[:instiki]} not found" unless File.directory?(OPTIONS[:instiki]) |
73 | raise "Directory #{OPTIONS[:storage]} not found" unless File.directory?(OPTIONS[:storage]) |
74 | |
75 | expected_page_rb_path = File.join(OPTIONS[:instiki], 'app/models/page.rb') |
76 | raise "Instiki installation not found in #{OPTIONS[:instiki]}" unless File.file?(expected_page_rb_path) |
77 | |
78 | expected_snapshot_pattern = File.join(OPTIONS[:storage], '*.snapshot') |
79 | raise "No snapshots found in #{expected_snapshot_pattern}" if Dir[expected_snapshot_pattern].empty? |
80 | |
81 | INSTIKI_ROOT = File.expand_path(OPTIONS[:instiki]) |
82 | |
83 | ADDITIONAL_LOAD_PATHS = %w( |
84 | app/models |
85 | lib |
86 | vendor/madeleine-0.7.1/lib |
87 | vendor/RedCloth-3.0.3/lib |
88 | vendor/RedCloth-3.0.4/lib |
89 | vendor/rubyzip-0.5.8/lib |
90 | ).map { |dir| "#{File.expand_path(File.join(INSTIKI_ROOT, dir))}" |
91 | }.delete_if { |dir| not File.exist?(dir) } |
92 | |
93 | # Prepend to $LOAD_PATH |
94 | ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } |
95 | |
96 | require 'webrick' |
97 | require 'wiki_service' |
98 | |
99 | # substitute an extremely expensive method with something cheap. |
100 | class Revision |
101 | alias :__display_content :display_content |
102 | def display_content |
103 | return self |
104 | end |
105 | end |
106 | |
107 | class Time |
108 | def ansi |
109 | strftime('%Y-%m-%d %H:%M:%S') |
110 | end |
111 | end |
112 | |
113 | def sql_insert(table, hash) |
114 | columns = hash.keys |
115 | |
116 | values = columns.map { |column| hash[column] } |
117 | values = values.map do |value| |
118 | if value.nil? |
119 | 'NULL' |
120 | else |
121 | if (value == false or value == true) and OPTIONS[:database] == 'mysql' |
122 | value = value ? '1' : '0' |
123 | end |
124 | |
125 | case OPTIONS[:database] |
126 | when 'mysql', 'postgres' |
127 | value = value.to_s.gsub("'", "\\\\'") |
128 | when 'sqlite' |
129 | value = value.to_s.gsub("'", "''") |
130 | else |
131 | raise "Unsupported database option #{OPTIONS[:database]}" |
132 | end |
133 | "'#{value.gsub("\r\n", "\n")}'" |
134 | end |
135 | end |
136 | |
137 | output = "INSERT INTO #{table} (" |
138 | output << columns.join(", ") |
139 | |
140 | output << ") VALUES (" |
141 | output << values.join(", ") |
142 | output << ");" |
143 | output |
144 | end |
145 | |
146 | def delete_all(outfile) |
147 | %w(wiki_references revisions pages system webs).each { |table| outfile.puts "DELETE FROM #{table};" } |
148 | end |
149 | |
150 | def next_id(key) |
151 | $ids ||= {} |
152 | if $ids[key].nil? |
153 | $ids[key] = 1 |
154 | else |
155 | $ids[key] = $ids[key] + 1 |
156 | end |
157 | $ids[key] |
158 | end |
159 | |
160 | def current_id(key) |
161 | $ids[key] or raise "No curent ID for #{key.inspect}" |
162 | end |
163 | |
164 | WikiService.storage_path = OPTIONS[:storage] |
165 | wiki = WikiService.instance |
166 | |
167 | File.open(OPTIONS[:outfile], 'w') { |outfile| |
168 | |
169 | outfile.puts "BEGIN;" |
170 | delete_all(outfile) |
171 | outfile.puts "COMMIT;" |
172 | |
173 | wiki.webs.each_pair do |web_name, web| |
174 | outfile.puts "BEGIN;" |
175 | outfile.puts sql_insert(:webs, { |
176 | :id => next_id(:web), |
177 | :name => web.name, |
178 | :address => web.address, |
179 | :password => web.password, |
180 | :additional_style => web.additional_style, |
181 | :allow_uploads => web.allow_uploads, |
182 | :published => web.published, |
183 | :count_pages => web.count_pages, |
184 | :markup => web.markup, |
185 | :color => web.color, |
186 | :max_upload_size => web.max_upload_size, |
187 | :safe_mode => web.safe_mode, |
188 | :brackets_only => web.brackets_only, |
189 | :created_at => web.pages.values.map { |p| p.revisions.first.created_at }.min.ansi, |
190 | :updated_at => web.pages.values.map { |p| p.revisions.last.created_at }.max.ansi |
191 | }) |
192 | outfile.puts "COMMIT;" |
193 | |
194 | puts "Web #{web_name} has #{web.pages.keys.size} pages" |
195 | web.pages.each_pair do |page_name, page| |
196 | |
197 | outfile.puts "BEGIN;" |
198 | |
199 | outfile.puts sql_insert(:pages, { |
200 | :id => next_id(:page), |
201 | :web_id => current_id(:web), |
202 | :locked_by => page.locked_by, |
203 | :name => page.name, |
204 | :created_at => page.revisions.first.created_at.ansi, |
205 | :updated_at => page.revisions.last.created_at.ansi |
206 | }) |
207 | |
208 | puts " Page #{page_name} has #{page.revisions.size} revisions" |
209 | page.revisions.each_with_index do |rev, i| |
210 | |
211 | outfile.puts sql_insert(:revisions, { |
212 | :id => next_id(:revision), |
213 | :page_id => current_id(:page), |
214 | :content => rev.content, |
215 | :author => rev.author.to_s, |
216 | :ip => (rev.author.is_a?(Author) ? rev.author.ip : 'N/A'), |
217 | :created_at => rev.created_at.ansi, |
218 | :updated_at => rev.created_at.ansi, |
219 | :revised_at => rev.created_at.ansi |
220 | }) |
221 | puts " Revision #{i} created at #{rev.created_at.ansi}" |
222 | end |
223 | |
224 | outfile.puts "COMMIT;" |
225 | |
226 | end |
227 | end |
228 | } |