From c885ec66e0ffeefb41462b87707f5e80c0ffa993 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Mon, 2 Apr 2012 13:39:29 -0400 Subject: [PATCH] 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. --- IkiWiki/Plugin/comments.pm | 42 +++++++++++++------ IkiWiki/Plugin/notifyemail.pm | 24 ++++++++++- IkiWiki/Plugin/passwordauth.pm | 73 +++++++++++++++++++++++++++++----- templates/editcomment.tmpl | 3 ++ 4 files changed, 119 insertions(+), 23 deletions(-) diff --git a/IkiWiki/Plugin/comments.pm b/IkiWiki/Plugin/comments.pm index c2b2d919d..0d5e3c078 100644 --- a/IkiWiki/Plugin/comments.pm +++ b/IkiWiki/Plugin/comments.pm @@ -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 (! $@) { diff --git a/IkiWiki/Plugin/notifyemail.pm b/IkiWiki/Plugin/notifyemail.pm index 9f5a255ae..2c1775f2e 100644 --- a/IkiWiki/Plugin/notifyemail.pm +++ b/IkiWiki/Plugin/notifyemail.pm @@ -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, ); diff --git a/IkiWiki/Plugin/passwordauth.pm b/IkiWiki/Plugin/passwordauth.pm index ea6ed158a..0cf2a26ea 100644 --- a/IkiWiki/Plugin/passwordauth.pm +++ b/IkiWiki/Plugin/passwordauth.pm @@ -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( diff --git a/templates/editcomment.tmpl b/templates/editcomment.tmpl index 17c65624e..e177db959 100644 --- a/templates/editcomment.tmpl +++ b/templates/editcomment.tmpl @@ -13,6 +13,9 @@ (optional)
+ + +
(You might want to Signin first?)
-- 2.26.2