2 package IkiWiki
::Plugin
::rename;
9 hook
(type
=> "getsetup", id
=> "rename", call
=> \
&getsetup
);
10 hook
(type
=> "formbuilder_setup", id
=> "rename", call
=> \
&formbuilder_setup
);
11 hook
(type
=> "formbuilder", id
=> "rename", call
=> \
&formbuilder
);
12 hook
(type
=> "sessioncgi", id
=> "rename", call
=> \
&sessioncgi
);
13 hook
(type
=> "rename", id
=> "rename", call
=> \
&rename_subpages
);
25 sub check_canrename
($$$$$$) {
33 my $attachment=! defined pagetype
($pagesources{$src});
35 # Must be a known source file.
36 if (! exists $pagesources{$src}) {
37 error
(sprintf(gettext
("%s does not exist"),
38 htmllink
("", "", $src, noimageinline
=> 1)));
41 # Must exist on disk, and be a regular file.
42 if (! -e
"$config{srcdir}/$srcfile") {
43 error
(sprintf(gettext
("%s is not in the srcdir, so it cannot be renamed"), $srcfile));
45 elsif (-l
"$config{srcdir}/$srcfile" && ! -f _
) {
46 error
(sprintf(gettext
("%s is not a file"), $srcfile));
50 IkiWiki
::check_canedit
($src, $q, $session);
52 if (IkiWiki
::Plugin
::attachment
->can("check_canattach")) {
53 IkiWiki
::Plugin
::attachment
::check_canattach
($session, $src, "$config{srcdir}/$srcfile");
56 error
("renaming of attachments is not allowed");
60 # Dest checks can be omitted by passing undef.
62 if ($srcfile eq $destfile) {
63 error
(gettext
("no change to the file name was specified"));
66 # Must be a legal filename.
67 if (IkiWiki
::file_pruned
($destfile)) {
68 error
(sprintf(gettext
("illegal name")));
71 # Must not be a known source file.
72 if ($src ne $dest && exists $pagesources{$dest}) {
73 error
(sprintf(gettext
("%s already exists"),
74 htmllink
("", "", $dest, noimageinline
=> 1)));
77 # Must not exist on disk already.
78 if (-l
"$config{srcdir}/$destfile" || -e _
) {
79 error
(sprintf(gettext
("%s already exists on disk"), $destfile));
83 IkiWiki
::check_canedit
($dest, $q, $session);
85 # Note that $srcfile is used here, not $destfile,
86 # because it wants the current file, to check it.
87 IkiWiki
::Plugin
::attachment
::check_canattach
($session, $dest, "$config{srcdir}/$srcfile");
92 IkiWiki
::run_hooks
(canrename
=> sub {
93 return if defined $canrename;
94 my $ret=shift->(cgi
=> $q, session
=> $session,
95 src
=> $src, srcfile
=> $srcfile,
96 dest
=> $dest, destfile
=> $destfile);
101 elsif (ref $ret eq 'CODE') {
105 elsif (defined $ret) {
111 return defined $canrename ?
$canrename : 1;
114 sub rename_form
($$$) {
119 eval q{use CGI::FormBuilder};
121 my $f = CGI
::FormBuilder
->new(
123 title
=> sprintf(gettext
("rename %s"), pagetitle
($page)),
129 action
=> IkiWiki
::cgiurl
(),
131 fields
=> [qw{do page new_name attachment
}],
134 $f->field(name
=> "do", type
=> "hidden", value
=> "rename", force
=> 1);
135 $f->field(name
=> "sid", type
=> "hidden", value
=> $session->id,
137 $f->field(name
=> "page", type
=> "hidden", value
=> $page, force
=> 1);
138 $f->field(name
=> "new_name", value
=> pagetitle
($page, 1), size
=> 60);
139 if (!$q->param("attachment")) {
140 # insert the standard extensions
142 if (exists $IkiWiki::hooks
{htmlize
}) {
143 foreach my $key (grep { !/^_/ } keys %{$IkiWiki::hooks
{htmlize
}}) {
144 push @page_types, [$key, $IkiWiki::hooks
{htmlize
}{$key}{longname
} || $key];
147 @page_types=sort @page_types;
149 # make sure the current extension is in the list
150 my ($ext) = $pagesources{$page}=~/\.([^.]+)$/;
151 if (! $IkiWiki::hooks
{htmlize
}{$ext}) {
152 unshift(@page_types, [$ext, $ext]);
155 $f->field(name
=> "type", type
=> 'select',
156 options
=> \
@page_types,
157 value
=> $ext, force
=> 1);
159 foreach my $p (keys %pagesources) {
160 if ($pagesources{$p}=~m/^\Q$page\E\//) {
161 $f->field(name
=> "subpages",
164 options
=> [ [ 1 => gettext
("Also rename SubPages and attachments") ] ],
171 $f->field(name
=> "attachment", type
=> "hidden");
173 return $f, ["Rename", "Cancel"];
176 sub rename_start
($$$$) {
179 my $attachment=shift;
182 check_canrename
($page, $pagesources{$page}, undef, undef,
185 # Save current form state to allow returning to it later
186 # without losing any edits.
187 # (But don't save what button was submitted, to avoid
188 # looping back to here.)
189 # Note: "_submit" is CGI::FormBuilder internals.
190 $q->param(-name
=> "_submit", -value
=> "");
191 $session->param(postrename
=> scalar $q->Vars);
192 IkiWiki
::cgi_savesession
($session);
194 if (defined $attachment) {
195 $q->param(-name
=> "attachment", -value
=> $attachment);
197 my ($f, $buttons)=rename_form
($q, $session, $page);
198 IkiWiki
::showform
($f, $buttons, $session, $q);
202 sub postrename
($;$$$) {
206 my $attachment=shift;
208 # Load saved form state and return to edit page.
209 my $postrename=CGI
->new($session->param("postrename"));
210 $session->clear("postrename");
211 IkiWiki
::cgi_savesession
($session);
215 # They renamed the page they were editing. This requires
216 # fixups to the edit form state.
217 # Tweak the edit form to be editing the new page.
218 $postrename->param("page", $dest);
221 # Update edit form content to fix any links present
223 $postrename->param("editcontent",
224 renamepage_hook
($dest, $src, $dest,
225 $postrename->param("editcontent")));
227 # Get a new edit token; old was likely invalidated.
228 $postrename->param("rcsinfo",
229 IkiWiki
::rcs_prepedit
($pagesources{$dest}));
232 IkiWiki
::cgi_editpage
($postrename, $session);
235 sub formbuilder
(@
) {
237 my $form=$params{form
};
239 if (defined $form->field("do") && ($form->field("do") eq "edit" ||
240 $form->field("do") eq "create")) {
241 IkiWiki
::decode_form_utf8
($form);
243 my $session=$params{session
};
245 if ($form->submitted eq "Rename" && $form->field("do") eq "edit") {
246 rename_start
($q, $session, 0, $form->field("page"));
248 elsif ($form->submitted eq "Rename Attachment") {
249 my @selected=map { Encode
::decode_utf8
($_) } $q->param("attachment_select");
251 error
(gettext
("Only one attachment can be renamed at a time."));
253 elsif (! @selected) {
254 error
(gettext
("Please select the attachment to rename."))
256 rename_start
($q, $session, 1, $selected[0]);
263 sub formbuilder_setup
(@
) {
265 my $form=$params{form
};
268 if (defined $form->field("do") && ($form->field("do") eq "edit" ||
269 $form->field("do") eq "create")) {
270 # Rename button for the page, and also for attachments.
271 push @
{$params{buttons
}}, "Rename" if $form->field("do") eq "edit";
272 $form->tmpl_param("field-rename" => '<input name="_submit" type="submit" value="Rename Attachment" />');
274 if (defined $renamesummary) {
275 $form->tmpl_param(message
=> $renamesummary);
280 sub sessioncgi
($$) {
283 if ($q->param("do") eq 'rename') {
285 my ($form, $buttons)=rename_form
($q, $session, Encode
::decode_utf8
($q->param("page")));
286 IkiWiki
::decode_form_utf8
($form);
288 if ($form->submitted eq 'Cancel') {
289 postrename
($session);
291 elsif ($form->submitted eq 'Rename' && $form->validate) {
292 IkiWiki
::checksessionexpiry
($q, $session, $q->param('sid'));
294 # Queue of rename actions to perfom.
297 # These untaints are safe because of the checks
298 # performed in check_canrename later.
299 my $src=$form->field("page");
300 my $srcfile=IkiWiki
::possibly_foolish_untaint
($pagesources{$src});
301 my $dest=IkiWiki
::possibly_foolish_untaint
(titlepage
($form->field("new_name")));
303 if (! $q->param("attachment")) {
304 my $type=$q->param('type');
305 if (defined $type && length $type && $IkiWiki::hooks
{htmlize
}{$type}) {
306 $type=IkiWiki
::possibly_foolish_untaint
($type);
309 my ($ext)=$srcfile=~/\.([^.]+)$/;
313 $destfile=newpagefile
($dest, $type);
319 destfile
=> $destfile,
323 @torename=rename_hook
(
324 torename
=> \
@torename,
330 require IkiWiki
::Render
;
331 IkiWiki
::disable_commit_hook
() if $config{rcs
};
332 my %origpagesources=%pagesources;
334 # First file renaming.
335 foreach my $rename (@torename) {
336 if ($rename->{required
}) {
337 do_rename
($rename, $q, $session);
340 eval {do_rename
($rename, $q, $session)};
347 # Temporarily tweak pagesources to point to
348 # the renamed file, in case fixlinks needs
350 $pagesources{$rename->{src
}}=$rename->{destfile
};
352 IkiWiki
::rcs_commit_staged
(
353 message
=> sprintf(gettext
("rename %s to %s"), $srcfile, $destfile),
358 foreach my $rename (@torename) {
359 next if $rename->{src
} eq $rename->{dest
};
360 next if $rename->{error
};
361 foreach my $p (fixlinks
($rename, $session)) {
362 # map old page names to new
363 foreach my $r (@torename) {
364 next if $rename->{error
};
365 if ($r->{src
} eq $p) {
370 push @
{$rename->{fixedlinks
}}, $p;
375 %pagesources=%origpagesources;
377 IkiWiki
::enable_commit_hook
();
378 IkiWiki
::rcs_update
();
381 IkiWiki
::saveindex
();
383 # Find pages with remaining, broken links.
384 foreach my $rename (@torename) {
385 next if $rename->{src
} eq $rename->{dest
};
387 foreach my $page (keys %links) {
389 foreach my $link (@
{$links{$page}}) {
390 my $bestlink=bestlink
($page, $link);
391 if ($bestlink eq $rename->{src
}) {
392 push @
{$rename->{brokenlinks
}}, $page;
399 # Generate a summary, that will be shown at the top
400 # of the edit template.
402 foreach my $rename (@torename) {
403 my $template=template
("renamesummary.tmpl");
404 $template->param(src
=> $rename->{srcfile
});
405 $template->param(dest
=> $rename->{destfile
});
406 $template->param(error
=> $rename->{error
});
407 if ($rename->{src
} ne $rename->{dest
}) {
408 $template->param(brokenlinks_checked
=> 1);
409 $template->param(brokenlinks
=> linklist
($rename->{dest
}, $rename->{brokenlinks
}));
410 $template->param(fixedlinks
=> linklist
($rename->{dest
}, $rename->{fixedlinks
}));
412 $renamesummary.=$template->output;
415 postrename
($session, $src, $dest, $q->param("attachment"));
418 IkiWiki
::showform
($form, $buttons, $session, $q);
425 # Add subpages to the list of pages to be renamed, if needed.
426 sub rename_subpages
(@
) {
429 my %torename = %{$params{torename
}};
430 my $q = $params{cgi
};
431 my $src = $torename{src
};
432 my $srcfile = $torename{src
};
433 my $dest = $torename{dest
};
434 my $destfile = $torename{dest
};
436 return () unless ($q->param("subpages") && $src ne $dest);
439 foreach my $p (keys %pagesources) {
440 next unless $pagesources{$p}=~m/^\Q$src\E\//;
441 # If indexpages is enabled, the srcfile should not be confused
443 next if $pagesources{$p} eq $srcfile;
445 my $d=$pagesources{$p};
446 $d=~s/^\Q$src\E\//$dest\
//;
449 srcfile
=> $pagesources{$p},
450 dest
=> pagename
($d),
459 # generates a list of links in a form suitable for FormBuilder
462 # converts a list of pages into a list of links
463 # in a form suitable for FormBuilder.
467 page
=> htmllink
($dest, $dest, $_,
469 linktext
=> pagetitle
($_),
475 sub renamepage_hook
($$$$) {
476 my ($page, $src, $dest, $content)=@_;
478 IkiWiki
::run_hooks
(renamepage
=> sub {
493 my @torename=@
{$params{torename
}};
494 my %done=%{$params{done
}};
496 my $session=$params{session
};
498 return () unless @torename;
501 foreach my $torename (@torename) {
502 unless (exists $done{$torename->{src
}} && $done{$torename->{src
}}) {
503 IkiWiki
::run_hooks
(rename => sub {
504 push @nextset, shift->(
505 torename
=> $torename,
510 $done{$torename->{src
}}=1;
514 push @torename, rename_hook
(
515 torename
=> \
@nextset,
523 return grep { ! $seen{$_->{src
}}++ } @torename;
526 sub do_rename
($$$) {
531 # First, check if this rename is allowed.
532 check_canrename
($rename->{src
},
538 # Ensure that the dest directory exists and is ok.
539 IkiWiki
::prep_writefile
($rename->{destfile
}, $config{srcdir
});
542 IkiWiki
::rcs_rename
($rename->{srcfile
}, $rename->{destfile
});
545 if (! rename($config{srcdir
}."/".$rename->{srcfile
},
546 $config{srcdir
}."/".$rename->{destfile
})) {
559 foreach my $page (keys %links) {
561 foreach my $link (@
{$links{$page}}) {
562 my $bestlink=bestlink
($page, $link);
563 if ($bestlink eq $rename->{src
}) {
569 my $file=$pagesources{$page};
570 next unless -e
$config{srcdir
}."/".$file;
571 my $oldcontent=readfile
($config{srcdir
}."/".$file);
572 my $content=renamepage_hook
($page, $rename->{src
}, $rename->{dest
}, $oldcontent);
573 if ($oldcontent ne $content) {
574 my $token=IkiWiki
::rcs_prepedit
($file);
575 eval { writefile
($file, $config{srcdir
}, $content) };
577 my $conflict=IkiWiki
::rcs_commit
(
579 message
=> sprintf(gettext
("update for rename of %s to %s"), $rename->{srcfile
}, $rename->{destfile
}),
583 push @fixedlinks, $page if ! defined $conflict;