aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoey <joey@0fa5a96a-9a0e-0410-b3b2-a0fd24251071>2006-11-20 20:37:27 +0000
committerjoey <joey@0fa5a96a-9a0e-0410-b3b2-a0fd24251071>2006-11-20 20:37:27 +0000
commit3e593eb9c0edd3f5cce7381ca145c0889441d719 (patch)
treeecca037124400a34803e8baefadb3179c296aac9
parent74cb86fbb79b011014958712325112d65bea5f12 (diff)
downloadikiwiki-3e593eb9c0edd3f5cce7381ca145c0889441d719.tar
ikiwiki-3e593eb9c0edd3f5cce7381ca145c0889441d719.tar.gz
* Add "last" parameter to hook function. Very basic ordering, and hopefully
nothing more spohisticated will be needed. * Add formbuilder_setup and formbuilder hooks. * Split out a passwordauth module, that holds all the traditional password based authentication etc code. It's enabled by default, but can be disabled if you want only openid or some other auth method.
-rw-r--r--IkiWiki.pm10
-rw-r--r--IkiWiki/CGI.pm218
-rw-r--r--IkiWiki/Plugin/openid.pm95
-rw-r--r--IkiWiki/Plugin/skeleton.pm14
-rw-r--r--debian/changelog10
-rw-r--r--doc/ikiwiki.setup2
-rw-r--r--doc/plugins.mdwn7
-rw-r--r--doc/plugins/passwordauth.mdwn9
-rw-r--r--doc/plugins/write.mdwn44
9 files changed, 182 insertions, 227 deletions
diff --git a/IkiWiki.pm b/IkiWiki.pm
index 15f7bcec0..703b596a8 100644
--- a/IkiWiki.pm
+++ b/IkiWiki.pm
@@ -62,7 +62,7 @@ sub defaultconfig () { #{{{
setup => undef,
adminuser => undef,
adminemail => undef,
- plugin => [qw{mdwn inline htmlscrubber}],
+ plugin => [qw{mdwn inline htmlscrubber passwordauth}],
timeformat => '%c',
locale => undef,
sslcookie => 0,
@@ -663,7 +663,15 @@ sub run_hooks ($$) { # {{{
my $sub=shift;
if (exists $hooks{$type}) {
+ my @deferred;
foreach my $id (keys %{$hooks{$type}}) {
+ if ($hooks{$type}{$id}{last}) {
+ push @deferred, $id;
+ next;
+ }
+ $sub->($hooks{$type}{$id}{call});
+ }
+ foreach my $id (@deferred) {
$sub->($hooks{$type}{$id}{call});
}
}
diff --git a/IkiWiki/CGI.pm b/IkiWiki/CGI.pm
index 14861e398..e1cb83b49 100644
--- a/IkiWiki/CGI.pm
+++ b/IkiWiki/CGI.pm
@@ -127,16 +127,9 @@ sub cgi_signin ($$) { #{{{
error($@) if $@;
my $form = CGI::FormBuilder->new(
title => "signin",
- fields => [qw(do name password openid_url)],
header => 1,
charset => "utf-8",
method => 'POST',
- validate => {
- confirm_password => {
- perl => q{eq $form->field("password")},
- },
- email => 'EMAIL',
- },
required => 'NONE',
javascript => 0,
params => $q,
@@ -146,170 +139,32 @@ sub cgi_signin ($$) { #{{{
{template_params("signin.tmpl")} : ""),
stylesheet => baseurl()."style.css",
);
-
- decode_form_utf8($form);
+ my $buttons=["Login"];
- $form->field(name => "name", required => 0, size => 30);
$form->field(name => "do", type => "hidden");
- $form->field(name => "password", type => "password", required => 0);
- if ($config{openid}) {
- $form->field(name => "openid_url", label => "OpenID", size => 30,
- comment => '('.
- htmllink("", "", "OpenID", 1, 0, "What's this?")
- .($config{openidsignup} ? " | <a href=\"$config{openidsignup}\">Get an OpenID</a>" : "")
- .')');
- }
- else {
- $form->field(name => "openid_url", type => "hidden");
- }
- if ($form->submitted eq "Register" || $form->submitted eq "Create Account") {
- $form->title("register");
- $form->text("");
- $form->fields(qw(do name password confirm_password email));
- $form->field(name => "confirm_password", type => "password");
- $form->field(name => "email", type => "text");
- $form->field(name => "openid_url", type => "hidden");
- }
+
if ($q->param("do") ne "signin" && !$form->submitted) {
$form->text("You need to log in first.");
}
- if ($form->submitted) {
- my $submittype=$form->submitted;
- # OpenID login uses the Login button, but validates
- # differently.
- if ($submittype eq "Login" && $config{openid} &&
- length $form->field("openid_url")) {
- $submittype="OpenID";
-
- $form->field(
- name => "openid_url",
- validate => sub {
- # FIXME: ugh
- IkiWiki::Plugin::openid::validate($q, $session, shift, $form);
- },
- );
- }
-
- # Set required fields based on how form was submitted.
- my %required=(
- "Login" => [qw(name password)],
- "Register" => [],
- "Create Account" => [qw(name password confirm_password email)],
- "Mail Password" => [qw(name)],
- "OpenID" => [qw(openid_url)],
- );
- foreach my $opt (@{$required{$submittype}}) {
- $form->field(name => $opt, required => 1);
- }
+ run_hooks(formbuilder_setup => sub {
+ shift->(form => $form, cgi => $q, session => $session);
+ });
- # Validate password differently depending on how
- # form was submitted.
- if ($submittype eq 'Login') {
- $form->field(
- name => "password",
- validate => sub {
- length $form->field("name") &&
- shift eq userinfo_get($form->field("name"), 'password');
- },
- );
- $form->field(name => "name", validate => '/^\w+$/');
- }
- elsif ($submittype ne 'OpenID') {
- $form->field(name => "password", validate => 'VALUE');
- }
- # And make sure the entered name exists when logging
- # in or sending email, and does not when registering.
- if ($submittype eq 'Create Account' ||
- $submittype eq 'Register') {
- $form->field(
- name => "name",
- validate => sub {
- my $name=shift;
- length $name &&
- $name=~/$config{wiki_file_regexp}/ &&
- ! userinfo_get($name, "regdate");
- },
- );
- }
- elsif ($submittype ne 'OpenID') {
- $form->field(
- name => "name",
- validate => sub {
- my $name=shift;
- length $name &&
- userinfo_get($name, "regdate");
- },
- );
- }
- }
- else {
- # First time settings.
- $form->field(name => "name", comment => "use FirstnameLastName");
- if ($session->param("name")) {
- $form->field(name => "name", value => $session->param("name"));
- }
- }
+ decode_form_utf8($form);
- if ($form->submitted && $form->validate) {
- if ($form->submitted eq 'Login') {
- $session->param("name", $form->field("name"));
- cgi_postsignin($q, $session);
- }
- elsif ($form->submitted eq 'Create Account') {
- my $user_name=$form->field('name');
- if (userinfo_setall($user_name, {
- 'email' => $form->field('email'),
- 'password' => $form->field('password'),
- 'regdate' => time
- })) {
- $form->field(name => "confirm_password", type => "hidden");
- $form->field(name => "email", type => "hidden");
- $form->text("Account creation successful. Now you can Login.");
- printheader($session);
- print misctemplate($form->title, $form->render(submit => ["Login"]));
- }
- else {
- error("Error creating account.");
- }
- }
- elsif ($form->submitted eq 'Mail Password') {
- my $user_name=$form->field("name");
- my $template=template("passwordmail.tmpl");
- $template->param(
- user_name => $user_name,
- user_password => userinfo_get($user_name, "password"),
- wikiurl => $config{url},
- wikiname => $config{wikiname},
- REMOTE_ADDR => $ENV{REMOTE_ADDR},
- );
-
- eval q{use Mail::Sendmail};
- error($@) if $@;
- sendmail(
- To => userinfo_get($user_name, "email"),
- From => "$config{wikiname} admin <$config{adminemail}>",
- Subject => "$config{wikiname} information",
- Message => $template->output,
- ) or error("Failed to send mail");
-
- $form->text("Your password has been emailed to you.");
- $form->field(name => "name", required => 0);
- printheader($session);
- print misctemplate($form->title, $form->render(submit => ["Login", "Mail Password"]));
- }
- elsif ($form->submitted eq "Register") {
- printheader($session);
- print misctemplate($form->title, $form->render(submit => ["Create Account"]));
- }
- }
- elsif ($form->submitted eq "Create Account") {
- printheader($session);
- print misctemplate($form->title, $form->render(submit => ["Create Account"]));
+ if (exists $hooks{formbuilder}) {
+ run_hooks(formbuilder => sub {
+ shift->(form => $form, cgi => $q, session => $session,
+ buttons => $buttons);
+ });
}
else {
+ if ($form->submitted) {
+ $form->validate;
+ }
printheader($session);
- print misctemplate($form->title, $form->render(submit => ["Login", "Register", "Mail Password"]));
+ print misctemplate($form->title, $form->render(submit => $buttons));
}
} #}}}
@@ -338,15 +193,10 @@ sub cgi_prefs ($$) { #{{{
error($@) if $@;
my $form = CGI::FormBuilder->new(
title => "preferences",
- fields => [qw(do name password confirm_password email
- subscriptions locked_pages)],
header => 0,
charset => "utf-8",
method => 'POST',
validate => {
- confirm_password => {
- perl => q{eq $form->field("password")},
- },
email => 'EMAIL',
},
required => 'NONE',
@@ -357,30 +207,26 @@ sub cgi_prefs ($$) { #{{{
{template_params("prefs.tmpl")} : ""),
stylesheet => baseurl()."style.css",
);
- my @buttons=("Save Preferences", "Logout", "Cancel");
+ my $buttons=["Save Preferences", "Logout", "Cancel"];
+
+ run_hooks(formbuilder_setup => sub {
+ shift->(form => $form, cgi => $q, session => $session);
+ });
- my $user_name=$session->param("name");
$form->field(name => "do", type => "hidden");
- $form->field(name => "name", disabled => 1,
- value => $user_name, force => 1, size => 30);
- $form->field(name => "password", type => "password");
- $form->field(name => "confirm_password", type => "password");
+ $form->field(name => "email", size => 50);
$form->field(name => "subscriptions", size => 50,
comment => "(".htmllink("", "", "PageSpec", 1).")");
$form->field(name => "locked_pages", size => 50,
comment => "(".htmllink("", "", "PageSpec", 1).")");
$form->field(name => "banned_users", size => 50);
+ my $user_name=$session->param("name");
if (! is_admin($user_name)) {
$form->field(name => "locked_pages", type => "hidden");
$form->field(name => "banned_users", type => "hidden");
}
- if ($config{httpauth}) {
- $form->field(name => "password", type => "hidden");
- $form->field(name => "confirm_password", type => "hidden");
- }
-
if (! $form->submitted) {
$form->field(name => "email", force => 1,
value => userinfo_get($user_name, "email"));
@@ -406,8 +252,8 @@ sub cgi_prefs ($$) { #{{{
return;
}
elsif ($form->submitted eq "Save Preferences" && $form->validate) {
- foreach my $field (qw(password email subscriptions locked_pages)) {
- if (length $form->field($field)) {
+ foreach my $field (qw(email subscriptions locked_pages)) {
+ if (defined $form->field($field) && length $form->field($field)) {
userinfo_set($user_name, $field, $form->field($field)) || error("failed to set $field");
}
}
@@ -418,8 +264,16 @@ sub cgi_prefs ($$) { #{{{
$form->text("Preferences saved.");
}
- printheader($session);
- print misctemplate($form->title, $form->render(submit => \@buttons));
+ if (exists $hooks{formbuilder}) {
+ run_hooks(formbuilder => sub {
+ shift->(form => $form, cgi => $q, session => $session,
+ buttons => $buttons);
+ });
+ }
+ else {
+ printheader($session);
+ print misctemplate($form->title, $form->render(submit => $buttons));
+ }
} #}}}
sub cgi_editpage ($$) { #{{{
@@ -454,6 +308,10 @@ sub cgi_editpage ($$) { #{{{
template => $renderer,
);
+ run_hooks(formbuilder_setup => sub {
+ shift->(form => $form, cgi => $q, session => $session);
+ });
+
decode_form_utf8($form);
# This untaint is safe because titlepage removes any problematic
diff --git a/IkiWiki/Plugin/openid.pm b/IkiWiki/Plugin/openid.pm
index 43ce8fd31..7ea67c5ca 100644
--- a/IkiWiki/Plugin/openid.pm
+++ b/IkiWiki/Plugin/openid.pm
@@ -8,8 +8,9 @@ use IkiWiki;
sub import { #{{{
hook(type => "getopt", id => "openid", call => \&getopt);
- hook(type => "checkconfig", id => "openid", call => \&checkconfig);
hook(type => "auth", id => "openid", call => \&auth);
+ hook(type => "formbuilder_setup", id => "openid",
+ call => \&formbuilder_setup, last => 1);
} # }}}
sub getopt () { #{{{
@@ -19,37 +20,43 @@ sub getopt () { #{{{
GetOptions("openidsignup=s" => \$config{openidsignup});
} #}}}
-sub checkconfig () { #{{{
- # Currently part of the OpenID code is in CGI.pm, and is enabled by
- # this setting.
- # TODO: modularise it all out into this plugin..
- $config{openid}=1;
-} #}}}
-
-sub auth ($$) { #{{{
- my $q=shift;
- my $session=shift;
-
- if (defined $q->param('openid.mode')) {
- my $csr=getobj($q, $session);
-
- if (my $setup_url = $csr->user_setup_url) {
- IkiWiki::redirect($q, $setup_url);
+sub formbuilder_setup (@) { #{{{
+ my %params=@_;
+
+ my $form=$params{form};
+ my $session=$params{session};
+ my $cgi=$params{cgi};
+
+ if ($form->title eq "signin") {
+ $form->field(
+ name => "openid_url",
+ label => "OpenID",
+ size => 30,
+ comment => '('.
+ htmllink("", "", "OpenID", 1, 0, "What's this?")
+ .($config{openidsignup} ? " | <a href=\"$config{openidsignup}\">Get an OpenID</a>" : "")
+ .')'
+ );
+
+ # Handle submission of an OpenID as validation.
+ if ($form->submitted && $form->submitted eq "Login" &&
+ defined $form->field("openid_url") &&
+ length $form->field("openid_url")) {
+ $form->field(
+ name => "openid_url",
+ validate => sub {
+ validate($cgi, $session, shift, $form);
+ },
+ );
+ # Skip all other required fields in this case.
+ foreach my $field ($form->field) {
+ next if $field eq "openid_url";
+ $form->field(name => $field, required => 0,
+ validate => '/.*/');
+ }
}
- elsif ($csr->user_cancel) {
- IkiWiki::redirect($q, $config{url});
- }
- elsif (my $vident = $csr->verified_identity) {
- $session->param(name => $vident->url);
- }
- else {
- error("OpenID failure: ".$csr->err);
- }
- }
- elsif (defined $q->param('openid_identifier')) {
- validate($q, $session, $q->param('openid_identifier'));
}
-} #}}}
+}
sub validate ($$$;$) { #{{{
my $q=shift;
@@ -77,11 +84,37 @@ sub validate ($$$;$) { #{{{
delayed_return => 1,
);
# Redirect the user to the OpenID server, which will
- # eventually bounce them back to auth() above.
+ # eventually bounce them back to auth()
IkiWiki::redirect($q, $check_url);
exit 0;
} #}}}
+sub auth ($$) { #{{{
+ my $q=shift;
+ my $session=shift;
+
+ if (defined $q->param('openid.mode')) {
+ my $csr=getobj($q, $session);
+
+ if (my $setup_url = $csr->user_setup_url) {
+ IkiWiki::redirect($q, $setup_url);
+ }
+ elsif ($csr->user_cancel) {
+ IkiWiki::redirect($q, $config{url});
+ }
+ elsif (my $vident = $csr->verified_identity) {
+ $session->param(name => $vident->url);
+ }
+ else {
+ error("OpenID failure: ".$csr->err);
+ }
+ }
+ elsif (defined $q->param('openid_identifier')) {
+ # myopenid.com affiliate support
+ validate($q, $session, $q->param('openid_identifier'));
+ }
+} #}}}
+
sub getobj ($$) { #{{{
my $q=shift;
my $session=shift;
diff --git a/IkiWiki/Plugin/skeleton.pm b/IkiWiki/Plugin/skeleton.pm
index f3244ae14..feb0f7419 100644
--- a/IkiWiki/Plugin/skeleton.pm
+++ b/IkiWiki/Plugin/skeleton.pm
@@ -21,6 +21,8 @@ sub import { #{{{
hook(type => "change", id => "skeleton", call => \&change);
hook(type => "cgi", id => "skeleton", call => \&cgi);
hook(type => "auth", id => "skeleton", call => \&auth);
+ hook(type => "formbuilder_setup", id => "skeleton", call => \&formbuilder_setup);
+ hook(type => "formbuilder", id => "skeleton", call => \&formbuilder);
hook(type => "savestate", id => "savestate", call => \&savestate);
} # }}}
@@ -103,6 +105,18 @@ sub auth ($$) { #{{{
debug("skeleton plugin running in auth");
} #}}}
+sub formbuilder_setup (@) { #{{{
+ my %params=@_;
+
+ debug("skeleton plugin running in formbuilder_setup");
+} # }}}
+
+sub formbuilder (@) { #{{{
+ my %params=@_;
+
+ debug("skeleton plugin running in formbuilder");
+} # }}}
+
sub savestate () { #{{{
debug("skeleton plugin running in savestate");
} #}}}
diff --git a/debian/changelog b/debian/changelog
index b622fa006..b6f397f27 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -14,8 +14,14 @@ ikiwiki (1.34) UNRELEASED; urgency=low
* Add optional "desc" parameter to shortcut definitions.
* Avoid locking the wiki at all when handling some basic cgi stuff
(searches, recentchanges).
-
- -- Joey Hess <joeyh@debian.org> Mon, 20 Nov 2006 06:54:12 -0500
+ * Add "last" parameter to hook function. Very basic ordering, and hopefully
+ nothing more spohisticated will be needed.
+ * Add formbuilder_setup and formbuilder hooks.
+ * Split out a passwordauth module, that holds all the traditional password
+ based authentication etc code. It's enabled by default, but can be disabled
+ if you want only openid or some other auth method.
+
+ -- Joey Hess <joeyh@debian.org> Mon, 20 Nov 2006 09:17:07 -0500
ikiwiki (1.33) unstable; urgency=low
diff --git a/doc/ikiwiki.setup b/doc/ikiwiki.setup
index 7d0eb71fe..c6aa08ddd 100644
--- a/doc/ikiwiki.setup
+++ b/doc/ikiwiki.setup
@@ -97,7 +97,7 @@ use IkiWiki::Setup::Standard {
# wikitext camelcase pagestats htmltidy fortune
# sidebar map rst toc linkmap openid}],
# If you want to disable any of the default plugins, list them here.
- #disable_plugins => [qw{inline htmlscrubber}],
+ #disable_plugins => [qw{inline htmlscrubber passwordauth}],
# For use with the tag plugin, make all tags be located under a
# base page.
diff --git a/doc/plugins.mdwn b/doc/plugins.mdwn
index 6de17adc7..cd6037c2b 100644
--- a/doc/plugins.mdwn
+++ b/doc/plugins.mdwn
@@ -7,9 +7,10 @@ wiki, or just have [[type/fun]].
There's documentation if you want to [[write]] your own plugins, or you can
install and use plugins contributed by others.
-The [[mdwn]], [[inline]], and [[htmlscrubber]] plugins are enabled by default.
-To enable other plugins, use the `--plugin` switch described in [[usage]],
-or the equivalent `add_plugins` line in [[ikiwiki.setup]].
+The [[mdwn]], [[inline]], [[htmlscrubber]], and [[passwordauth]] plugins
+are enabled by default. To enable other plugins, use the `--plugin` switch
+described in [[usage]], or the equivalent `add_plugins` line in
+[[ikiwiki.setup]].
# Plugin directory
diff --git a/doc/plugins/passwordauth.mdwn b/doc/plugins/passwordauth.mdwn
new file mode 100644
index 000000000..aded8829f
--- /dev/null
+++ b/doc/plugins/passwordauth.mdwn
@@ -0,0 +1,9 @@
+[[template id=plugin name=passwordauth core=1 included=1 author="[[Joey]]"]]
+[[tag type/auth]]
+
+This plugin lets ikiwiki prompt for a user name and password when logging
+into the wiki. It also handles registering users, mailing passwords, and
+changing passwords in the prefs page.
+
+It is enabled by default, but can be turned off if you want to only use
+some other form of authentication, such as [[openid]].
diff --git a/doc/plugins/write.mdwn b/doc/plugins/write.mdwn
index 651023701..f808d2e1e 100644
--- a/doc/plugins/write.mdwn
+++ b/doc/plugins/write.mdwn
@@ -36,6 +36,10 @@ before begining to render pages. This parameter should be set to true if
the hook modifies data in `%links`. Note that doing so will make the hook
be run twice per page build, so avoid doing it for expensive hooks.
+An optional "last" parameter, if set to a true value, makes the hook run
+after all other hooks of its type. Useful if the hook depends on some other
+hook being run first.
+
## Types of hooks
In roughly the order they are called.
@@ -119,13 +123,13 @@ return the htmlized content.
hook(type => "pagetemplate", id => "foo", call => \&pagetemplate);
-[[Templates]] are filled out for many different things in ikiwiki,
-like generating a page, or part of a blog page, or an rss feed, or a cgi.
-This hook allows modifying those templates. The function is passed named
+[[Templates]] are filled out for many different things in ikiwiki, like
+generating a page, or part of a blog page, or an rss feed, or a cgi. This
+hook allows modifying those templates. The function is passed named
parameters. The "page" and "destpage" parameters are the same as for a
-preprocess hook. The "template" parameter is a `HTML::Template` object that
-is the template that will be used to generate the page. The function can
-manipulate that template object.
+preprocess hook. The "template" parameter is a [[cpan HTML::Template]]
+object that is the template that will be used to generate the page. The
+function can manipulate that template object.
The most common thing to do is probably to call $template->param() to add
a new custom parameter to the template.
@@ -177,7 +181,7 @@ terminate the program.
### auth
- hook(type => "cgi", id => "foo", call => \&auth);
+ hook(type => "auth", id => "foo", call => \&auth);
This hook can be used to implement a different authentication method than
the standard web form. When a user needs to be authenticated, each registered
@@ -188,6 +192,28 @@ object's "name" parameter to the authenticated user's name. Note that
if the name is set to the name of a user who is not registered,
a basic registration of the user will be automatically performed.
+### formbuilder
+
+ hook(type => "formbuilder_setup", id => "foo", call => \&formbuilder_setup);
+ hook(type => "formbuilder", id => "foo", call => \&formbuilder);
+
+These hooks allow tapping into the parts of ikiwiki that use [[cpan
+CGI::FormBuilder]] to generate web forms. These hooks are passed named
+parameters: `cgi`, `session`, and `form`. These are, respectively, the
+`CGI` object, the user's `CGI::Session`, and a `CGI::FormBuilder`.
+
+Each time a form is set up, the formbuilder_setup hook is called.
+Typically the formbuilder_setup hook will check the form's title, and if
+it's a form that it needs to modify, will call various methods to
+add/remove/change fields, tweak the validation code for the fields, etc. It
+will not validate or display the form.
+
+Form validation and display can be overridden by the formbuilder hook.
+By default, ikiwiki will do a basic validation and display of the form,
+but if this hook is registered, it will stop that and let the hook take
+over. This hook is passed an additional named parameter: `buttons` is an
+array of the submit buttons for the form.
+
### savestate
hook(type => "savestate", id => "foo", call => \&savestate);
@@ -259,8 +285,8 @@ appear on the wiki page, rather than calling error().
#### `template($;@)`
-Creates and returns a HTML::Template object. The first parameter is the
-name of the file in the template directory. The optional remaining
+Creates and returns a [[cpan HTML::Template]] object. The first parameter
+is the name of the file in the template directory. The optional remaining
parameters are passed to HTML::Template->new.
#### `htmlpage($)`