Revert removal of sandbox content.
[ikiwiki] / ikiwiki-transition
CommitLineData
04f064e7 1#!/usr/bin/perl
d5eaf736
JH
2use warnings;
3use strict;
3479809f
JH
4use IkiWiki;
5use HTML::Entities;
d5eaf736 6
1b03a06c
JT
7my $regex = qr{
8 (\\?) # 1: escape?
9 \[\[(!?) # directive open; 2: optional prefix
10 ([-\w]+) # 3: command
11 ( # 4: the parameters (including initial whitespace)
12 \s+
13 (?:
14 (?:[-\w]+=)? # named parameter key?
15 (?:
16 """.*?""" # triple-quoted value
17 |
18 "[^"]+" # single-quoted value
19 |
20 [^\s\]]+ # unquoted value
21 )
22 \s* # whitespace or end
23 # of directive
24 )
25 *) # 0 or more parameters
26 \]\] # directive closed
27}sx;
28
d5eaf736 29sub handle_directive {
1b03a06c
JT
30 my $escape = shift;
31 my $prefix = shift;
32 my $directive = shift;
33 my $args = shift;
34
35 if (length $escape) {
36 return "${escape}[[${prefix}${directive}${args}]]"
37 }
38 if ($directive =~ m/^(if|more|table|template|toggleable)$/) {
39 $args =~ s{$regex}{handle_directive($1, $2, $3, $4)}eg;
40 }
41 return "[[!${directive}${args}]]"
42}
43
61ffa4a8 44sub prefix_directives {
f7ded117 45 loadsetup(shift);
04f064e7 46
04f064e7
JH
47 IkiWiki::loadplugins();
48 IkiWiki::checkconfig();
49 IkiWiki::loadindex();
50
51 if (! %pagesources) {
52 error "ikiwiki has not built this wiki yet, cannot transition";
53 }
54
55 foreach my $page (values %pagesources) {
56 next unless defined pagetype($page) &&
57 -f $config{srcdir}."/".$page;
58 my $content=readfile($config{srcdir}."/".$page);
59 my $oldcontent=$content;
60 $content=~s{$regex}{handle_directive($1, $2, $3, $4)}eg;
61 if ($oldcontent ne $content) {
62 writefile($page, $config{srcdir}, $content);
63 }
61ffa4a8
JH
64 }
65}
66
3479809f 67sub indexdb {
9f5f5543 68 setstatedir(shift);
3479809f 69
e943812d
JH
70 # Note: No lockwiki here because ikiwiki already locks it
71 # before calling this.
3479809f
JH
72 if (! IkiWiki::oldloadindex()) {
73 die "failed to load index\n";
74 }
75 if (! IkiWiki::saveindex()) {
76 die "failed to save indexdb\n"
77 }
78 if (! IkiWiki::loadindex()) {
79 die "transition failed, cannot load new indexdb\n";
80 }
81 if (! unlink("$config{wikistatedir}/index")) {
82 die "unlink failed: $!\n";
83 }
84}
85
e943812d 86sub hashpassword {
9f5f5543 87 setstatedir(shift);
ea6dc383 88
e943812d
JH
89 eval q{use IkiWiki::UserInfo};
90 eval q{use Authen::Passphrase::BlowfishCrypt};
91 if ($@) {
92 error("ikiwiki-transition hashpassword: failed to load Authen::Passphrase, passwords not hashed");
93 }
94
95 IkiWiki::lockwiki();
96 IkiWiki::loadplugin("passwordauth");
97 my $userinfo = IkiWiki::userinfo_retrieve();
98 foreach my $user (keys %{$userinfo}) {
99 if (ref $userinfo->{$user} &&
100 exists $userinfo->{$user}->{password} &&
101 length $userinfo->{$user}->{password} &&
102 ! exists $userinfo->{$user}->{cryptpassword}) {
103 IkiWiki::Plugin::passwordauth::setpassword($user, $userinfo->{$user}->{password});
104 }
105 }
106}
107
50a5ab3c 108sub aggregateinternal {
f7ded117 109 loadsetup(shift);
50a5ab3c 110 require IkiWiki::Plugin::aggregate;
b29d11b3 111 IkiWiki::checkconfig();
50a5ab3c 112 IkiWiki::Plugin::aggregate::migrate_to_internal();
50a5ab3c
SM
113}
114
ea6dc383
JH
115sub setupformat {
116 my $setup=shift;
ea6dc383 117
f7ded117 118 loadsetup($setup);
ea6dc383
JH
119 IkiWiki::checkconfig();
120
121 # unpack old-format wrappers setting into new fields
11a4ad8a
JH
122 my $cgi_seen=0;
123 my $rcs_seen=0;
ea6dc383
JH
124 foreach my $wrapper (@{$config{wrappers}}) {
125 if ($wrapper->{cgi}) {
11a4ad8a
JH
126 if ($cgi_seen) {
127 die "don't know what to do with second cgi wrapper ".$wrapper->{wrapper}."\n";
128 }
129 $cgi_seen++;
ea6dc383
JH
130 print "setting cgi_wrapper to ".$wrapper->{wrapper}."\n";
131 $config{cgi_wrapper}=$wrapper->{wrapper};
132 $config{cgi_wrappermode}=$wrapper->{wrappermode}
133 if exists $wrapper->{wrappermode};
134 }
135 elsif ($config{rcs}) {
11a4ad8a
JH
136 if ($rcs_seen) {
137 die "don't know what to do with second rcs wrapper ".$wrapper->{wrapper}."\n";
138 }
139 $rcs_seen++;
ea6dc383
JH
140 print "setting $config{rcs}_wrapper to ".$wrapper->{wrapper}."\n";
141 $config{$config{rcs}."_wrapper"}=$wrapper->{wrapper};
142 $config{$config{rcs}."_wrappermode"}=$wrapper->{wrappermode}
143 if exists $wrapper->{wrappermode};
144 }
145 else {
146 die "don't know what to do with wrapper ".$wrapper->{wrapper}."\n";
147 }
148 }
149
150 IkiWiki::Setup::dump($setup);
151}
152
7ba65e7f
JH
153sub moveprefs {
154 my $setup=shift;
7ba65e7f 155
f7ded117 156 loadsetup($setup);
7ba65e7f
JH
157 IkiWiki::checkconfig();
158
159 eval q{use IkiWiki::UserInfo};
160 error $@ if $@;
161
162 foreach my $field (qw{allowed_attachments locked_pages}) {
163 my $orig=$config{$field};
164 foreach my $admin (@{$config{adminuser}}) {
165 my $a=IkiWiki::userinfo_get($admin, $field);
166 if (defined $a && length $a &&
794dbd24
JH
167 # might already have been moved
168 (! defined $orig || $a ne $orig)) {
169 if (defined $config{$field} &&
170 length $config{$field}) {
171 $config{$field}=IkiWiki::pagespec_merge($config{$field}, $a);
172 }
173 else {
174 $config{$field}=$a;
175 }
7ba65e7f
JH
176 }
177 }
178 }
179
180 my %banned=map { $_ => 1 } @{$config{banned_users}}, IkiWiki::get_banned_users();
181 $config{banned_users}=[sort keys %banned];
182
183 IkiWiki::Setup::dump($setup);
184}
185
2a7721fe 186sub deduplinks {
c2e2da6e
JH
187 loadsetup(shift);
188 IkiWiki::loadplugins();
189 IkiWiki::checkconfig();
2a7721fe
JH
190 IkiWiki::loadindex();
191 foreach my $page (keys %links) {
192 my %l;
193 $l{$_}=1 foreach @{$links{$page}};
194 $links{$page}=[keys %l]
195 }
196 IkiWiki::saveindex();
197}
198
9f5f5543 199sub setstatedir {
f7ded117 200 my $dirorsetup=shift;
9f5f5543 201
f7ded117 202 if (! defined $dirorsetup) {
9f5f5543
JH
203 usage();
204 }
205
f7ded117
JH
206 if (-d $dirorsetup) {
207 $config{wikistatedir}=$dirorsetup."/.ikiwiki";
208 }
209 elsif (-f $dirorsetup) {
210 loadsetup($dirorsetup);
211 }
212 else {
213 error("ikiwiki-transition: $dirorsetup does not exist");
9f5f5543 214 }
9f5f5543
JH
215
216 if (! -d $config{wikistatedir}) {
217 error("ikiwiki-transition: $config{wikistatedir} does not exist");
218 }
219}
f7ded117
JH
220
221sub loadsetup {
222 my $setup=shift;
223 if (! defined $setup) {
224 usage();
225 }
226
227 require IkiWiki::Setup;
228
229 %config = IkiWiki::defaultconfig();
230 IkiWiki::Setup::load($setup);
231}
9f5f5543 232
61ffa4a8 233sub usage {
3479809f
JH
234 print STDERR "Usage: ikiwiki-transition type ...\n";
235 print STDERR "Currently supported transition subcommands:\n";
04f064e7 236 print STDERR "\tprefix_directives setupfile ...\n";
ea6dc383
JH
237 print STDERR "\taggregateinternal setupfile\n";
238 print STDERR "\tsetupformat setupfile\n";
7ba65e7f 239 print STDERR "\tmoveprefs setupfile\n";
f7ded117
JH
240 print STDERR "\thashpassword setupfile|srcdir\n";
241 print STDERR "\tindexdb setupfile|srcdir\n";
c2e2da6e 242 print STDERR "\tdeduplinks setupfile\n";
61ffa4a8
JH
243 exit 1;
244}
245
246usage() unless @ARGV;
247
248my $mode=shift;
249if ($mode eq 'prefix_directives') {
250 prefix_directives(@ARGV);
251}
1aab048e 252elsif ($mode eq 'hashpassword') {
e943812d
JH
253 hashpassword(@ARGV);
254}
3479809f
JH
255elsif ($mode eq 'indexdb') {
256 indexdb(@ARGV);
257}
50a5ab3c
SM
258elsif ($mode eq 'aggregateinternal') {
259 aggregateinternal(@ARGV);
260}
ea6dc383
JH
261elsif ($mode eq 'setupformat') {
262 setupformat(@ARGV);
263}
7ba65e7f
JH
264elsif ($mode eq 'moveprefs') {
265 moveprefs(@ARGV);
266}
2a7721fe
JH
267elsif ($mode eq 'deduplinks') {
268 deduplinks(@ARGV);
269}
61ffa4a8
JH
270else {
271 usage();
1b03a06c 272}
3479809f
JH
273
274package IkiWiki;
275
82ecf0aa 276# A slightly modified version of the old loadindex function.
3479809f
JH
277sub oldloadindex {
278 %oldrenderedfiles=%pagectime=();
279 if (! $config{rebuild}) {
280 %pagesources=%pagemtime=%oldlinks=%links=%depends=
281 %destsources=%renderedfiles=%pagecase=%pagestate=();
282 }
283 open (my $in, "<", "$config{wikistatedir}/index") || return;
284 while (<$in>) {
285 chomp;
286 my %items;
287 $items{link}=[];
288 $items{dest}=[];
289 foreach my $i (split(/ /, $_)) {
290 my ($item, $val)=split(/=/, $i, 2);
291 push @{$items{$item}}, decode_entities($val);
292 }
293
294 next unless exists $items{src}; # skip bad lines for now
295
296 my $page=pagename($items{src}[0]);
297 if (! $config{rebuild}) {
298 $pagesources{$page}=$items{src}[0];
299 $pagemtime{$page}=$items{mtime}[0];
300 $oldlinks{$page}=[@{$items{link}}];
301 $links{$page}=[@{$items{link}}];
be032a7b 302 $depends{$page}={ $items{depends}[0] => $IkiWiki::DEPEND_CONTENT } if exists $items{depends};
3479809f
JH
303 $destsources{$_}=$page foreach @{$items{dest}};
304 $renderedfiles{$page}=[@{$items{dest}}];
305 $pagecase{lc $page}=$page;
306 foreach my $k (grep /_/, keys %items) {
307 my ($id, $key)=split(/_/, $k, 2);
308 $pagestate{$page}{decode_entities($id)}{decode_entities($key)}=$items{$k}[0];
309 }
310 }
311 $oldrenderedfiles{$page}=[@{$items{dest}}];
312 $pagectime{$page}=$items{ctime}[0];
313 }
82ecf0aa
JH
314
315 # saveindex relies on %hooks being populated, else it won't save
316 # the page state owned by a given hook. But no plugins are loaded
317 # by this program, so populate %hooks with all hook ids that
318 # currently have page state.
319 foreach my $page (keys %pagemtime) {
320 foreach my $id (keys %{$pagestate{$page}}) {
321 $hooks{_dummy}{$id}=1;
322 }
323 }
324
3479809f
JH
325 return close($in);
326}
7ba65e7f
JH
327
328# Used to be in IkiWiki/UserInfo, but only used here now.
329sub get_banned_users () {
330 my @ret;
331 my $userinfo=userinfo_retrieve();
332 foreach my $user (keys %{$userinfo}) {
333 push @ret, $user if $userinfo->{$user}->{banned};
334 }
335 return @ret;
336}
337
1ea8580a
SM
338# Used to be in IkiWiki, but only used here (to migrate admin prefs into the
339# setup file) now.
340sub pagespec_merge ($$) {
341 my $a=shift;
342 my $b=shift;
343
344 return $a if $a eq $b;
345 return "($a) or ($b)";
346}
347
7ba65e7f 3481