notifyemail: Fix bug that caused duplicate emails to be sent when site was rebuilt.
[ikiwiki.git] / IkiWiki / Plugin / notifyemail.pm
1 #!/usr/bin/perl
2 package IkiWiki::Plugin::notifyemail;
3
4 use warnings;
5 use strict;
6 use IkiWiki 3.00;
7
8 sub import {
9         hook(type => "formbuilder", id => "notifyemail", call => \&formbuilder);
10         hook(type => "getsetup", id => "notifyemail",  call => \&getsetup);
11         hook(type => "changes", id => "notifyemail", call => \&notify);
12 }
13
14 sub getsetup () {
15         return
16                 plugin => {
17                         safe => 1,
18                         rebuild => 0,
19                 },
20 }
21
22 sub formbuilder (@) {
23         my %params=@_;
24         my $form=$params{form};
25         return unless $form->title eq "preferences";
26         my $session=$params{session};
27         my $username=$session->param("name");
28         $form->field(name => "subscriptions", size => 50,
29                 fieldset => "preferences",
30                 comment => "(".htmllink("", "", "ikiwiki/PageSpec", noimageinline => 1).")");
31         if (! $form->submitted) {
32                 $form->field(name => "subscriptions", force => 1,
33                         value => getsubscriptions($username));
34         }
35         elsif ($form->submitted eq "Save Preferences" && $form->validate &&
36                defined $form->field("subscriptions")) {
37                 setsubscriptions($username, $form->field('subscriptions'));
38         }
39 }
40
41 sub getsubscriptions ($) {
42         my $user=shift;
43         eval q{use IkiWiki::UserInfo};
44         error $@ if $@;
45         IkiWiki::userinfo_get($user, "subscriptions");
46 }
47
48 sub setsubscriptions ($$) {
49         my $user=shift;
50         my $subscriptions=shift;
51         eval q{use IkiWiki::UserInfo};
52         error $@ if $@;
53         IkiWiki::userinfo_set($user, "subscriptions", $subscriptions);
54 }
55
56 # Called by other plugins to subscribe the user to a pagespec.
57 sub subscribe ($$) {
58         my $user=shift;
59         my $addpagespec=shift;
60         my $pagespec=getsubscriptions($user);
61         setsubscriptions($user,
62                 length $pagespec ? $pagespec." or ".$addpagespec : $addpagespec);
63 }
64
65 # Called by other plugins to subscribe an email to a pagespec.
66 sub anonsubscribe ($$) {
67         my $email=shift;
68         my $addpagespec=shift;
69         if (IkiWiki::Plugin::passwordauth->can("anonuser")) {
70                 my $user=IkiWiki::Plugin::passwordauth::anonuser($email);
71                 if (! defined $user) {
72                         error(gettext("Cannot subscribe your email address without logging in."));
73                 }
74                 subscribe($user, $addpagespec);
75         }
76 }
77
78 sub notify (@) {
79         my @files=@_;
80         return unless @files;
81         return if $config{rebuild};
82
83         eval q{use Mail::Sendmail};
84         error $@ if $@;
85         eval q{use IkiWiki::UserInfo};
86         error $@ if $@;
87         eval q{use URI};
88         error($@) if $@;
89
90         # Daemonize, in case the mail sending takes a while.
91         defined(my $pid = fork) or error("Can't fork: $!");
92         return if $pid; # parent
93         chdir '/';
94         open STDIN, '/dev/null';
95         open STDOUT, '>/dev/null';
96         POSIX::setsid() or error("Can't start a new session: $!");
97         open STDERR, '>&STDOUT' or error("Can't dup stdout: $!");
98
99         # Don't need to keep a lock on the wiki as a daemon.
100         IkiWiki::unlockwiki();
101
102         my $userinfo=IkiWiki::userinfo_retrieve();
103         exit 0 unless defined $userinfo;
104
105         foreach my $user (keys %$userinfo) {
106                 my $pagespec=$userinfo->{$user}->{"subscriptions"};
107                 next unless defined $pagespec && length $pagespec;
108                 my $email=$userinfo->{$user}->{email};
109                 next unless defined $email && length $email;
110
111                 foreach my $file (@files) {
112                         my $page=pagename($file);
113                         next unless pagespec_match($page, $pagespec);
114                         my $content="";
115                         my $showcontent=defined pagetype($file);
116                         if ($showcontent) {
117                                 $content=eval { readfile(srcfile($file)) };
118                                 $showcontent=0 if $@;
119                         }
120                         my $url;
121                         if (! IkiWiki::isinternal($page)) {
122                                 $url=urlto($page, undef, 1);
123                         }
124                         elsif (defined $pagestate{$page}{meta}{permalink}) {
125                                 # need to use permalink for an internal page
126                                 $url=URI->new_abs($pagestate{$page}{meta}{permalink}, $config{url});
127                         }
128                         else {
129                                 $url=$config{url}; # crummy fallback url
130                         }
131                         my $pagedesc=$page;
132                         if (defined $pagestate{$page}{meta}{title} &&
133                             length $pagestate{$page}{meta}{title}) {
134                                 $pagedesc=qq{"$pagestate{$page}{meta}{title}"};
135                         }
136                         my $subject=gettext("change notification:")." ".$pagedesc;
137                         if (pagetype($file) eq '_comment') {
138                                 $subject=gettext("comment notification:")." ".$pagedesc;
139                         }
140                         my $prefsurl=IkiWiki::cgiurl_abs(do => 'prefs');
141                         if (IkiWiki::Plugin::passwordauth->can("anonusertoken")) {
142                                 my $token=IkiWiki::Plugin::passwordauth::anonusertoken($userinfo->{$user});
143                                 $prefsurl=IkiWiki::cgiurl_abs(
144                                         do => 'tokenauth',
145                                         name => $user,
146                                         token => $token,
147                                 ) if defined $token;
148                         }
149                         my $template=template("notifyemail.tmpl");
150                         $template->param(
151                                 wikiname => $config{wikiname},
152                                 url => $url,
153                                 prefsurl => $prefsurl,
154                                 showcontent => $showcontent,
155                                 content => $content,
156                         );
157                         sendmail(
158                                 To => $email,
159                                 From => "$config{wikiname} <$config{adminemail}>",
160                                 Subject => $subject,
161                                 Message => $template->output,
162                         );
163                 }
164         }
165
166         exit 0; # daemon child
167 }
168
169 1