allow users to subscribe to comments w/o registering

Technically, when the user does this, a passwordless account is created
for them. The notify mails include a login url, and once logged in that
way, the user can enter a password to get a regular account (although
one with an annoying username).

This all requires the passwordauth plugin is enabled. A future enhancement
could be to split the passwordless user concept out into a separate plugin.
master
Joey Hess 2012-04-02 13:39:29 -04:00
parent c16b1e638e
commit c885ec66e0
4 changed files with 119 additions and 23 deletions

View File

@ -302,7 +302,7 @@ sub editcomment ($$) {
my @buttons = (POST_COMMENT, PREVIEW, CANCEL);
my $form = CGI::FormBuilder->new(
fields => [qw{do sid page subject editcontent type author
url subscribe}],
email url subscribe anonsubscribe}],
charset => 'utf-8',
method => 'POST',
required => [qw{editcontent}],
@ -349,24 +349,33 @@ sub editcomment ($$) {
my $username=$session->param('name');
$form->tmpl_param(username => $username);
if (defined $username && IkiWiki::Plugin::notifyemail->can("subscribe")) {
$form->field(name => "subscribe",
options => [gettext("email replies to me")]);
}
else {
$form->field(name => "subscribe", type => 'hidden');
$form->field(name => "subscribe", type => 'hidden');
$form->field(name => "anonsubscribe", type => 'hidden');
if (IkiWiki::Plugin::notifyemail->can("subscribe")) {
if (defined $username) {
$form->field(name => "subscribe", type => "checkbox",
options => [gettext("email replies to me")]);
}
elsif (IkiWiki::Plugin::passwordauth->can("anonuser")) {
$form->field(name => "anonsubscribe", type => "checkbox",
options => [gettext("email replies to me")]);
}
}
if ($config{comments_allowauthor} and
! defined $session->param('name')) {
$form->tmpl_param(allowauthor => 1);
$form->field(name => 'author', type => 'text', size => '40');
$form->field(name => 'email', type => 'text', size => '40');
$form->field(name => 'url', type => 'text', size => '40');
}
else {
$form->tmpl_param(allowauthor => 0);
$form->field(name => 'author', type => 'hidden', value => '',
force => 1);
$form->field(name => 'email', type => 'hidden', value => '',
force => 1);
$form->field(name => 'url', type => 'hidden', value => '',
force => 1);
}
@ -500,10 +509,18 @@ sub editcomment ($$) {
if ($form->submitted eq POST_COMMENT && $form->validate) {
IkiWiki::checksessionexpiry($cgi, $session);
if (defined $username && length $form->field("subscribe") &&
IkiWiki::Plugin::notifyemail->can("subscribe")) {
IkiWiki::Plugin::notifyemail::subscribe($username,
"comment($page)");
if (IkiWiki::Plugin::notifyemail->can("subscribe")) {
my $subspec="comment($page)";
if (defined $username &&
length $form->field("subscribe")) {
IkiWiki::Plugin::notifyemail::subscribe(
$username, $subspec);
}
elsif (length $form->field("email") &&
length $form->field("anonsubscribe")) {
IkiWiki::Plugin::notifyemail::anonsubscribe(
$form->field("email"), $subspec);
}
}
$postcomment=1;
@ -590,7 +607,8 @@ sub editcomment ($$) {
sub getavatar ($) {
my $user=shift;
return undef unless defined $user;
my $avatar;
eval q{use Libravatar::URL};
if (! $@) {

View File

@ -62,6 +62,19 @@ sub subscribe ($$) {
length $pagespec ? $pagespec." or ".$addpagespec : $addpagespec);
}
# Called by other plugins to subscribe an email to a pagespec.
sub anonsubscribe ($$) {
my $email=shift;
my $addpagespec=shift;
if (IkiWiki::Plugin::passwordauth->can("anonuser")) {
my $user=IkiWiki::Plugin::passwordauth::anonuser($email);
if (! defined $user) {
error(gettext("Cannot subscribe your email address without logging in."));
}
subscribe($user, $addpagespec);
}
}
sub notify (@) {
my @files=@_;
return unless @files;
@ -123,11 +136,20 @@ sub notify (@) {
if (pagetype($file) eq '_comment') {
$subject=gettext("comment notification:")." ".$pagedesc;
}
my $prefsurl=IkiWiki::cgiurl_abs(do => 'prefs');
if (IkiWiki::Plugin::passwordauth->can("anonusertoken")) {
my $token=IkiWiki::Plugin::passwordauth::anonusertoken($userinfo->{$user});
$prefsurl=IkiWiki::cgiurl_abs(
do => 'tokenauth',
name => $user,
token => $token,
) if defined $token;
}
my $template=template("notifyemail.tmpl");
$template->param(
wikiname => $config{wikiname},
url => $url,
prefsurl => IkiWiki::cgiurl_abs(do => 'prefs'),
prefsurl => $prefsurl,
showcontent => $showcontent,
content => $content,
);

View File

@ -99,11 +99,71 @@ sub setpassword ($$;$) {
# Setting the password clears any passwordless login token.
if ($field ne 'passwordless') {
IkiWiki::userinfo_set($user, "cryptpasswordless", "");
IkiWiki::userinfo_set($user, "passwordless", "");
}
}
# Generates a token that can be used to log the user in.
# This needs to be hard to guess. Generating a cgi session id will
# make it as hard to guess as any cgi session.
sub gentoken ($$;$) {
my $user=shift;
my $tokenfield=shift;
my $reversable=shift;
eval q{use CGI::Session};
error($@) if $@;
my $token = CGI::Session->new->id;
if (! $reversable) {
setpassword($user, $token, $tokenfield);
}
else {
IkiWiki::userinfo_set($user, $tokenfield, $token);
}
return $token;
}
# An anonymous user has no normal password, only a passwordless login
# token. Given an email address, this sets up such a user for that email,
# unless one already exists, and returns the username.
sub anonuser ($) {
my $email=shift;
# Want a username for this email that won't overlap with any other.
my $user=$email;
$user=~s/@/_/g;
my $userinfo=IkiWiki::userinfo_retrieve();
if (! exists $userinfo->{$user} || ! ref $userinfo->{$user}) {
if (IkiWiki::userinfo_setall($user, {
'email' => $email,
'regdate' => time})) {
gentoken($user, "passwordless", 1);
return $user;
}
else {
error(gettext("Error creating account."));
}
}
elsif (defined anonusertoken($userinfo->{$user})) {
return $user;
}
else {
return undef;
}
}
sub anonusertoken ($) {
my $userhash=shift;
if (exists $userhash->{passwordless} &&
length $userhash->{passwordless}) {
return $userhash->{passwordless};
}
else {
return undef;
}
}
sub formbuilder_setup (@) {
my %params=@_;
@ -283,15 +343,8 @@ sub formbuilder (@) {
if (! length $email) {
error(gettext("No email address, so cannot email password reset instructions."));
}
# Store a token that can be used once
# to log the user in. This needs to be hard
# to guess. Generating a cgi session id will
# make it as hard to guess as any cgi session.
eval q{use CGI::Session};
error($@) if $@;
my $token = CGI::Session->new->id;
setpassword($user_name, $token, "resettoken");
my $token=gentoken($user_name, "resettoken");
my $template=template("passwordmail.tmpl");
$template->param(

View File

@ -13,6 +13,9 @@
<label for="url" class="block">Website:</label>
<TMPL_VAR NAME=FIELD-URL> (optional)
<br />
<label for="url" class="block">Email:</label>
<TMPL_VAR NAME=FIELD-EMAIL> <TMPL_VAR FIELD-ANONSUBSCRIBE>
<br />
<TMPL_ELSE>
(You might want to <a href="<TMPL_VAR SIGNINURL>">Signin</a> first?)
<br />