use IkiWiki;
use open qw{:utf8 :std};
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "editpage", call => \&getsetup);
hook(type => "refresh", id => "editpage", call => \&refresh);
hook(type => "sessioncgi", id => "editpage", call => \&IkiWiki::cgi_editpage);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 1,
+ section => "core",
},
-} #}}}
+}
sub refresh () {
if (exists $wikistate{editpage} && exists $wikistate{editpage}{previews}) {
}
if ($delete) {
debug(sprintf(gettext("removing old preview %s"), $file));
- IkiWiki::prune("$config{destdir}/$file");
+ IkiWiki::prune("$config{destdir}/$file", $config{destdir});
}
}
elsif (defined $mtime) {
# Back to ikiwiki namespace for the rest, this code is very much
# internal to ikiwiki even though it's separated into a plugin,
-# and other plugins use the functions below.
+# and other plugins use the function below.
package IkiWiki;
-sub check_canedit ($$$;$) { #{{{
- my $page=shift;
- my $q=shift;
- my $session=shift;
- my $nonfatal=shift;
-
- my $canedit;
- run_hooks(canedit => sub {
- return if defined $canedit;
- my $ret=shift->($page, $q, $session);
- if (defined $ret) {
- if ($ret eq "") {
- $canedit=1;
- }
- elsif (ref $ret eq 'CODE') {
- $ret->() unless $nonfatal;
- $canedit=0;
- }
- elsif (defined $ret) {
- error($ret) unless $nonfatal;
- $canedit=0;
- }
- }
- });
- return $canedit;
-} #}}}
-
-sub cgi_editpage ($$) { #{{{
+sub cgi_editpage ($$) {
my $q=shift;
my $session=shift;
decode_cgi_utf8($q);
- my @fields=qw(do rcsinfo subpage from page type editcontent comments);
+ my @fields=qw(do rcsinfo subpage from page type editcontent
+ editmessage subscribe);
my @buttons=("Save Page", "Preview", "Cancel");
eval q{use CGI::FormBuilder};
error($@) if $@;
required => [qw{editcontent}],
javascript => 0,
params => $q,
- action => $config{cgiurl},
+ action => IkiWiki::cgiurl(),
header => 0,
table => 0,
- template => scalar template_params("editpage.tmpl"),
- wikiname => $config{wikiname},
+ template => { template("editpage.tmpl") },
);
decode_form_utf8($form);
# This untaint is safe because we check file_pruned and
# wiki_file_regexp.
my ($page)=$form->field('page')=~/$config{wiki_file_regexp}/;
+ if (! defined $page) {
+ error(gettext("bad page name"));
+ }
$page=possibly_foolish_untaint($page);
- my $absolute=($page =~ s#^/+##);
+ my $absolute=($page =~ s#^/+##); # absolute name used to force location
if (! defined $page || ! length $page ||
- file_pruned($page, $config{srcdir})) {
- error("bad page name");
+ file_pruned($page)) {
+ error(gettext("bad page name"));
}
- my $baseurl = urlto($page, undef, 1);
+ my $baseurl = urlto($page);
my $from;
if (defined $form->field('from')) {
# favor the type of linking page
$type=pagetype($pagesources{$from});
}
- $type=$config{default_pageext} unless defined $type;
+ $type=$config{default_pageext}
+ if ! defined $type || $type=~/^_/; # not internal type
$file=newpagefile($page, $type);
if (! $form->submitted) {
$form->field(name => "rcsinfo", value => "", force => 1);
$form->field(name => "subpage", type => 'hidden');
$form->field(name => "page", value => $page, force => 1);
$form->field(name => "type", value => $type, force => 1);
- $form->field(name => "comments", type => "text", size => 80);
+ $form->field(name => "editmessage", type => "text", size => 80);
$form->field(name => "editcontent", type => "textarea", rows => 20,
cols => 80);
$form->tmpl_param("can_commit", $config{rcs});
- $form->tmpl_param("indexlink", indexlink());
$form->tmpl_param("helponformattinglink",
htmllink($page, $page, "ikiwiki/formatting",
noimageinline => 1,
linktext => "FormattingHelp"));
+ my $cansubscribe=IkiWiki::Plugin::notifyemail->can("subscribe")
+ && IkiWiki::Plugin::comments->can("import")
+ && defined $session->param('name');
+ if ($cansubscribe) {
+ $form->field(name => "subscribe", type => "checkbox",
+ options => [gettext("email comments to me")]);
+ }
+ else {
+ $form->field(name => "subscribe", type => 'hidden');
+ }
+
+ my $previewing=0;
if ($form->submitted eq "Cancel") {
if ($form->field("do") eq "create" && defined $from) {
- redirect($q, urlto($from, undef, 1));
+ redirect($q, urlto($from));
}
elsif ($form->field("do") eq "create") {
- redirect($q, $config{url});
+ redirect($q, baseurl(undef));
}
else {
- redirect($q, urlto($page, undef, 1));
+ redirect($q, $baseurl);
}
exit;
}
elsif ($form->submitted eq "Preview") {
+ $previewing=1;
+
my $new=not exists $pagesources{$page};
- if ($new) {
- # temporarily record its type
- $pagesources{$page}=$page.".".$type;
- }
+ # temporarily record its type
+ $pagesources{$page}=$page.".".$type if $new;
my %wasrendered=map { $_ => 1 } @{$renderedfiles{$page}};
my $content=$form->field('editcontent');
});
$form->tmpl_param("page_preview", $preview);
- if ($new) {
- delete $pagesources{$page};
- }
-
# Previewing may have created files on disk.
# Keep a list of these to be deleted later.
my %previews = map { $_ => 1 } @{$wikistate{editpage}{previews}};
foreach my $f (@{$renderedfiles{$page}}) {
$previews{$f}=1 unless $wasrendered{$f};
}
+
+ # Throw out any other state changes made during previewing,
+ # and save the previews list.
+ loadindex();
@{$wikistate{editpage}{previews}} = keys %previews;
- $renderedfiles{$page}=[keys %wasrendered];
saveindex();
}
elsif ($form->submitted eq "Save Page") {
my $best_loc;
if (! defined $from || ! length $from ||
$from ne $form->field('from') ||
- file_pruned($from, $config{srcdir}) ||
- $from=~/^\// ||
+ file_pruned($from) ||
$absolute ||
$form->submitted) {
@page_locs=$best_loc=$page;
+ unshift @page_locs, lc($page)
+ if ! $form->submitted && lc($page) ne $page;
+ }
+ elsif (lc $page eq lc $config{discussionpage}) {
+ @page_locs=$best_loc="$from/".lc($page);
}
else {
my $dir=$from."/";
$dir=~s![^/]+/+$!!;
- if ((defined $form->field('subpage') && length $form->field('subpage')) ||
- $page eq gettext('discussion')) {
+ if ((defined $form->field('subpage') &&
+ length $form->field('subpage'))) {
$best_loc="$from/$page";
}
else {
$best_loc=$dir.$page;
}
+ my $mixedcase=lc($page) ne $page;
+
+ push @page_locs, $dir.lc($page) if $mixedcase;
push @page_locs, $dir.$page;
- push @page_locs, "$from/$page";
+ push @page_locs, $from."/".lc($page) if $mixedcase;
+ push @page_locs, $from."/".$page;
while (length $dir) {
$dir=~s![^/]+/+$!!;
+ push @page_locs, $dir.lc($page) if $mixedcase;
push @page_locs, $dir.$page;
}
-
- push @page_locs, "$config{userdir}/$page"
- if length $config{userdir};
+
+ my $userpage=IkiWiki::userpage($page);
+ push @page_locs, $userpage
+ if ! grep { $_ eq $userpage } @page_locs;
}
@page_locs = grep {
if (! @page_locs) {
# hmm, someone else made the page in the
# meantime?
- if ($form->submitted eq "Preview") {
+ if ($previewing) {
# let them go ahead with the edit
# and resolve the conflict at save
# time
@page_locs=$page;
}
else {
- redirect($q, urlto($page, undef, 1));
+ redirect($q, $baseurl);
exit;
}
}
check_canedit($_, $q, $session, 1)
} @page_locs;
if (! @editable_locs) {
- # let it throw an error this time
- map { check_canedit($_, $q, $session) } @page_locs;
+ # now let it throw an error, or prompt for
+ # login
+ map { check_canedit($_, $q, $session) }
+ ($best_loc, @page_locs);
}
my @page_types;
if (exists $hooks{htmlize}) {
- @page_types=grep { !/^_/ }
- keys %{$hooks{htmlize}};
+ foreach my $key (grep { !/^_/ } keys %{$hooks{htmlize}}) {
+ push @page_types, [$key, $hooks{htmlize}{$key}{longname} || $key];
+ }
}
+ @page_types=sort @page_types;
$form->tmpl_param("page_select", 1);
$form->field(name => "page", type => 'select',
value => $best_loc);
$form->field(name => "type", type => 'select',
options => \@page_types);
- $form->title(sprintf(gettext("creating %s"), pagetitle($page)));
+ $form->title(sprintf(gettext("creating %s"), pagetitle(basename($page))));
}
elsif ($form->field("do") eq "edit") {
$form->tmpl_param("page_select", 0);
$form->field(name => "page", type => 'hidden');
$form->field(name => "type", type => 'hidden');
- $form->title(sprintf(gettext("editing %s"), pagetitle($page)));
+ $form->title(sprintf(gettext("editing %s"), pagetitle(basename($page))));
}
- showform($form, \@buttons, $session, $q, forcebaseurl => $baseurl);
+ showform($form, \@buttons, $session, $q, page => $page);
}
else {
# save page
check_canedit($page, $q, $session);
-
- # The session id is stored on the form and checked to
- # guard against CSRF. But only if the user is logged in,
- # as anonok can allow anonymous edits.
- if (defined $session->param("name")) {
- my $sid=$q->param('sid');
- if (! defined $sid || $sid ne $session->id) {
- error(gettext("Your login session has expired."));
- }
- }
+ checksessionexpiry($q, $session);
my $exists=-e "$config{srcdir}/$file";
$form->field(name => "page", type => 'hidden');
$form->field(name => "type", type => 'hidden');
$form->title(sprintf(gettext("editing %s"), $page));
- showform($form, \@buttons, $session, $q, forcebaseurl => $baseurl);
+ showform($form, \@buttons, $session, $q,
+ page => $page);
exit;
}
elsif ($form->field("do") eq "create" && $exists) {
value => readfile("$config{srcdir}/$file").
"\n\n\n".$form->field("editcontent"),
force => 1);
- showform($form, \@buttons, $session, $q, forcebaseurl => $baseurl);
+ showform($form, \@buttons, $session, $q,
+ page => $page);
exit;
}
+
+ my $message="";
+ if (defined $form->field('editmessage') &&
+ length $form->field('editmessage')) {
+ $message=$form->field('editmessage');
+ }
my $content=$form->field('editcontent');
+ check_content(content => $content, page => $page,
+ cgi => $q, session => $session,
+ subject => $message);
run_hooks(editcontent => sub {
$content=shift->(
content => $content,
eval { writefile($file, $config{srcdir}, $content) };
$config{cgi}=1;
if ($@) {
+ # save $@ in case a called function clobbers it
+ my $error = $@;
$form->field(name => "rcsinfo", value => rcs_prepedit($file),
force => 1);
my $mtemplate=template("editfailedsave.tmpl");
- $mtemplate->param(error_message => $@);
+ $mtemplate->param(error_message => $error);
$form->tmpl_param("message", $mtemplate->output);
$form->field("editcontent", value => $content, force => 1);
$form->tmpl_param("page_select", 0);
$form->field(name => "type", type => 'hidden');
$form->title(sprintf(gettext("editing %s"), $page));
showform($form, \@buttons, $session, $q,
- forcebaseurl => $baseurl);
+ page => $page);
exit;
}
my $conflict;
if ($config{rcs}) {
- my $message="";
- if (defined $form->field('comments') &&
- length $form->field('comments')) {
- $message=$form->field('comments');
- }
-
if (! $exists) {
rcs_add($file);
}
# signaling to it that it should not try to
# do anything.
disable_commit_hook();
- $conflict=rcs_commit($file, $message,
- $form->field("rcsinfo"),
- $session->param("name"), $ENV{REMOTE_ADDR});
+ $conflict=rcs_commit(
+ file => $file,
+ message => $message,
+ token => $form->field("rcsinfo"),
+ session => $session,
+ );
enable_commit_hook();
rcs_update();
}
$form->field(name => "type", type => 'hidden');
$form->title(sprintf(gettext("editing %s"), $page));
showform($form, \@buttons, $session, $q,
- forcebaseurl => $baseurl);
+ page => $page);
}
else {
# The trailing question mark tries to avoid broken
# caches and get the most recent version of the page.
- redirect($q, urlto($page, undef, 1)."?updated");
+ redirect($q, $baseurl."?updated");
+ }
+
+ if ($cansubscribe && length $form->field("subscribe")) {
+ my $subspec="comment($page)";
+ IkiWiki::Plugin::notifyemail::subscribe(
+ $session->param('name'), $subspec);
}
}
exit;
-} #}}}
+}
1