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