]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/MailNotify.php
Fix onDeletePage warning and content
[SourceForge/phpwiki.git] / lib / MailNotify.php
1 <?php //-*-php-*-
2 rcs_id('$Id: MailNotify.php,v 1.7 2007-01-20 11:25:38 rurban Exp $');
3
4 /**
5  * Handle the pagelist pref[notifyPages] logic for users
6  * and notify => hash ( page => (userid => userhash) ) for pages.
7  * Generate notification emails.
8  *
9  * We add WikiDB handlers and register ourself there:
10  *   onChangePage, onDeletePage, onRenamePage
11  * Administrative actions:
12  *   [Watch] WatchPage - add a page, or delete watch handlers into the users 
13  *                       pref[notifyPages] slot.
14  *   My WatchList      - view or edit list/regex of pref[notifyPages].
15  *   EMailConfirm methods: send and verify
16  *
17  * Helper functions:
18  *   getPageChangeEmails
19  *   MailAdmin
20  *   ? handle emailed confirmation links (EmailSignup, ModeratedPage)
21  *
22  * @package MailNotify
23  * @author  Reini Urban
24  */
25
26 if (!defined("MAILER_LOG"))
27     define("MAILER_LOG", 'c:/wikimail.log');
28
29 class MailNotify {
30
31     function MailNotify($pagename) {
32         $this->pagename = $pagename; /* which page */
33         $this->emails  = array();    /* to which addresses */
34         $this->userids = array();    /* corresponding array of displayed names, 
35                                         don't display the email addresses */
36         /* From: from whom the mail appears to be */
37         $this->from = $this->fromId();
38     }
39
40     function fromId() {
41         global $request;
42         return $request->_user->getId() . '@' .  $request->get('REMOTE_HOST');
43     }
44
45     function userEmail($userid, $doverify = true) {
46         global $request;
47         $u = $request->getUser();
48         if ($u->UserName() == $userid) { // lucky: current user
49             $prefs = $u->getPreferences();
50             $email = $prefs->get('email');
51             // do a dynamic emailVerified check update
52             if ($doverify and !$request->_prefs->get('emailVerified'))
53                 $email = '';
54         } else {  // not current user
55             if (ENABLE_USER_NEW) {
56                 $u = WikiUser($userid);
57                 $u->getPreferences();
58                 $prefs = &$u->_prefs;
59             } else {
60                 $u = new WikiUser($request, $userid);
61                 $prefs = $u->getPreferences();
62             }
63             $email = $prefs->get('email');
64             if ($doverify and !$prefs->get('emailVerified')) {
65                 $email = '';
66             }
67         }
68         return $email;
69     }
70
71     /**
72      * getPageChangeEmails($notify)
73      * @param  $notify: hash ( page => (userid => userhash) )
74      * @return array
75      *         unique array of ($emails, $userids)
76      */
77     function getPageChangeEmails($notify) {
78         global $request;
79         $emails = array(); $userids = array();
80         foreach ($notify as $page => $users) {
81             if (glob_match($page, $this->pagename)) {
82                 foreach ($users as $userid => $user) {
83                     if (!$user) { // handle the case for ModeratePage: 
84                                   // no prefs, just userid's.
85                         $emails[] = $this->userEmail($userid, false);
86                         $userids[] = $userid;
87                     } else {
88                         if (!empty($user['verified']) and !empty($user['email'])) {
89                             $emails[]  = $user['email'];
90                             $userids[] = $userid;
91                         } elseif (!empty($user['email'])) {
92                             // do a dynamic emailVerified check update
93                             $email = $this->userEmail($userid, true);
94                             if ($email) {
95                                 $notify[$page][$userid]['verified'] = 1;
96                                 $request->_dbi->set('notify', $notify);
97                                 $emails[] = $email;
98                                 $userids[] = $userid;
99                             }
100                         }
101                         // ignore verification
102                         /*
103                         if (DEBUG) {
104                             if (!in_array($user['email'], $emails))
105                                 $emails[] = $user['email'];
106                         }
107                         */
108                     }
109                 }
110             }
111         }
112         $this->emails = array_unique($emails);
113         $this->userids = array_unique($userids);
114         return array($this->emails, $this->userids);
115     }
116     
117     function sendMail($subject, $content, 
118                       $notice = false,
119                       $silent = false)
120     {
121         global $request;
122         $emails = $this->emails;
123         $from = $this->from;
124         if (!$notice) $notice = _("PageChange Notification of %s");
125         $ok = mail(($to = array_shift($emails)),
126                  "[".WIKI_NAME."] ".$subject, 
127                    $subject."\n".$content,
128                    "From: $from\r\nBcc: ".join(',', $emails)
129                    );
130         if (MAILER_LOG and is_writable(MAILER_LOG)) {
131             $f = fopen(MAILER_LOG, "a");
132             fwrite($f, "\n\nX-MailSentOK: " . $ok ? 'OK' : 'FAILED');
133             if (!$ok) {
134                 global $ErrorManager;
135                 // get last error message
136                 $last_err = $ErrorManager->_postponed_errors[count($ErrorHandler->_postponed_errors)-1];
137                 fwrite($f, "\nX-MailFailure: " . $last_err);
138             }
139             fwrite($f, "\nDate: " . CTime());
140             fwrite($f, "\nSubject: $subject");
141             fwrite($f, "\nFrom: $from");
142             fwrite($f, "\nTo: $to");
143             fwrite($f, "\nBcc: ".join(',', $emails));
144             fwrite($f, "\n\n". $content);
145             fclose($f);
146         }
147         if ($ok) {
148             if (!$silent)
149                 trigger_error(sprintf($notice, $this->pagename)
150                               . " "
151                               . sprintf(_("sent to %s"), join(',',$this->userids)),
152                               E_USER_NOTICE);
153             return true;
154         } else {
155             trigger_error(sprintf($notice, $this->pagename)
156                           . " "
157                           . sprintf(_("Error: Couldn't send %s to %s"), 
158                                    $subject."\n".$content, join(',',$this->userids)), 
159                           E_USER_WARNING);
160             return false;
161         }
162     }
163     
164     /**
165      * Send udiff for a changed page to multiple users.
166      * See rename and remove methods also
167      */
168     function sendPageChangeNotification(&$wikitext, $version, &$meta) {
169
170         global $request;
171
172         if (@is_array($request->_deferredPageChangeNotification)) {
173             // collapse multiple changes (loaddir) into one email
174             $request->_deferredPageChangeNotification[] = 
175                 array($this->pagename, $this->emails, $this->userids);
176             return;
177         }
178         $backend = &$request->_dbi->_backend;
179         $subject = _("Page change").' '.urlencode($this->pagename);
180         $previous = $backend->get_previous_version($this->pagename, $version);
181         if (!isset($meta['mtime'])) $meta['mtime'] = time();
182         if ($previous) {
183             $difflink = WikiURL($this->pagename, array('action'=>'diff'), true);
184             $dbh = &$request->getDbh();
185             $cache = &$dbh->_wikidb->_cache;
186             //$cache = &$request->_dbi->_cache;
187             $content = explode("\n", $wikitext);
188             $prevdata = $cache->get_versiondata($this->pagename, $previous, true);
189             if (empty($prevdata['%content']))
190                 $prevdata = $backend->get_versiondata($this->pagename, $previous, true);
191             $other_content = explode("\n", $prevdata['%content']);
192             
193             include_once("lib/difflib.php");
194             $diff2 = new Diff($other_content, $this_content);
195             //$context_lines = max(4, count($other_content) + 1,
196             //                     count($this_content) + 1);
197             $fmt = new UnifiedDiffFormatter(/*$context_lines*/);
198             $content  = $this->pagename . " " . $previous . " " . 
199                 Iso8601DateTime($prevdata['mtime']) . "\n";
200             $content .= $this->pagename . " " . $version . " " .  
201                 Iso8601DateTime($meta['mtime']) . "\n";
202             $content .= $fmt->format($diff2);
203             
204         } else {
205             $difflink = WikiURL($this->pagename,array(),true);
206             $content = $this->pagename . " " . $version . " " .  
207                 Iso8601DateTime($meta['mtime']) . "\n";
208             $content .= _("New page");
209         }
210         $editedby = sprintf(_("Edited by: %s"), $this->from);
211         //$editedby = sprintf(_("Edited by: %s"), $meta['author']);
212         $this->sendMail($subject, 
213                         $editedby."\n".$difflink."\n\n".$content);
214     }
215
216     /** 
217      * support mass rename / remove (not yet tested)
218      */
219     function sendPageRenameNotification ($to, &$meta) {
220         global $request;
221
222         if (@is_array($request->_deferredPageRenameNotification)) {
223             $request->_deferredPageRenameNotification[] = 
224                 array($this->pagename, $to, $meta, $this->emails, $this->userids);
225         } else {
226             $pagename = $this->pagename;
227             //$editedby = sprintf(_("Edited by: %s"), $meta['author']) . ' ' . $meta['author_id'];
228             $editedby = sprintf(_("Edited by: %s"), $this->from);
229             $subject = sprintf(_("Page rename %s to %s"), urlencode($pagename), urlencode($to));
230             $link = WikiURL($to, true);
231             $this->sendMail($subject, 
232                             $editedby."\n".$link."\n\n"."Renamed $pagename to $to");
233         }
234     }
235
236     /**
237      * The handlers:
238      */
239     function onChangePage (&$wikidb, &$wikitext, $version, &$meta) {
240         $result = true;
241         if (!isa($GLOBALS['request'],'MockRequest')) {
242             $notify = $wikidb->get('notify');
243             /* Generate notification emails? */
244             if (!empty($notify) and is_array($notify)) {
245                 if (empty($this->pagename))
246                     $this->pagename = $meta['pagename'];
247                 //TODO: defer it (quite a massive load if you MassRevert some pages).
248                 //TODO: notification class which catches all changes,
249                 //  and decides at the end of the request what to mail. 
250                 //  (type, page, who, what, users, emails)
251                 // Could be used for ModeratePage and RSS2 Cloud xml-rpc also.
252                 $this->getPageChangeEmails($notify);
253                 if (!empty($this->emails)) {
254                     $result = $this->sendPageChangeNotification($wikitext, $version, $meta);
255                 }
256             }
257         }
258         return $result;
259     }
260
261     function onDeletePage (&$wikidb, $pagename) {
262         $result = true;
263         /* Generate notification emails? */
264         if (! $wikidb->isWikiPage($pagename) and !isa($GLOBALS['request'],'MockRequest')) {
265             $notify = $wikidb->get('notify');
266             if (!empty($notify) and is_array($notify)) {
267                 //TODO: deferr it (quite a massive load if you remove some pages).
268                 $this->getPageChangeEmails($notify);
269                 if (!empty($this->emails)) {
270                     $editedby = sprintf(_("Removed by: %s"), $this->from); // Todo: host_id
271                     //$emails = join(',', $this->emails);
272                     $subject = sprintf(_("Page removed %s"), urlencode($pagename));
273                     $page = $wikidb->getPage($pagename);
274                     $rev = $page->getCurrentRevision(true);
275                     $content = $rev->getPackedContent();
276                     $result = $this->sendMail($subject, 
277                                               $editedby."\n"."Deleted $pagename"."\n\n".$content);
278                 }
279             }
280         }
281         //How to create a RecentChanges entry with explaining summary? Dynamically
282         /*
283           $page = $this->getPage($pagename);
284           $current = $page->getCurrentRevision();
285           $meta = $current->_data;
286           $version = $current->getVersion();
287           $meta['summary'] = _("removed");
288           $page->save($current->getPackedContent(), $version + 1, $meta);
289         */
290         return $result;
291     }
292
293     function onRenamePage (&$wikidb, $oldpage, $new_pagename) {
294         $result = true;
295         if (!isa($GLOBALS['request'], 'MockRequest')) {
296             $notify = $wikidb->get('notify');
297             if (!empty($notify) and is_array($notify)) {
298                 $this->getPageChangeEmails($notify);
299                 if (!empty($this->emails)) {
300                     $newpage = $wikidb->getPage($new_pagename);
301                     $current = $newpage->getCurrentRevision();
302                     $meta = $current->_data;
303                     $this->pagename = $oldpage;
304                     $result = $this->sendPageRenameNotification($new_pagename, $meta);
305                 }
306             }
307         }
308     }
309
310     /**
311      * Send mail to user and store the cookie in the db
312      * wikiurl?action=ConfirmEmail&id=bla
313      */
314     function sendEmailConfirmation ($email, $userid) {
315         $id = rand_ascii_readable(16);
316         $wikidb = $GLOBALS['request']->getDbh();
317         $data = $wikidb->get('ConfirmEmail');
318         while(!empty($data[$id])) { // id collision
319             $id = rand_ascii_readable(16);
320         }
321         $subject = WIKI_NAME . " " . _("e-mail address confirmation");
322         $ip = $request->get('REMOTE_HOST');
323         $expire_date = time() + 7*86400;
324         $content = fmt("Someone, probably you from IP address %s, has registered an
325 account \"%s\" with this e-mail address on %s.
326
327 To confirm that this account really does belong to you and activate
328 e-mail features on %s, open this link in your browser:
329
330 %s
331
332 If this is *not* you, don't follow the link. This confirmation code
333 will expire at %s.", 
334                        $ip, $userid, WIKI_NAME, WIKI_NAME, 
335                        WikiURL(HOME_PAGE, array('action' => 'ConfirmEmail',
336                                                 'id' => $id), 
337                                true),
338                        CTime($expire_date));
339         $this->sendMail($subject, $content, "", true);
340         $data[$id] = array('email' => $email,
341                            'userid' => $userid,
342                            'expire' => $expire_date);
343         $wikidb->set('ConfirmEmail', $data);
344         return '';
345     }
346
347     function checkEmailConfirmation () {
348         global $request;
349         $wikidb = $request->getDbh();
350         $data = $wikidb->get('ConfirmEmail');
351         $id = $request->getArg('id');
352         if (empty($data[$id])) { // id not found
353             return HTML(HTML::h1("Confirm E-mail address"),
354                         HTML::h1("Sorry! Wrong URL"));
355         }
356         // upgrade the user
357         $userid = $data['userid'];
358         $email = $data['email'];
359         $u = $request->getUser();
360         if ($u->UserName() == $userid) { // lucky: current user (session)
361             $prefs = $u->getPreferences();
362             $request->_user->_level = WIKIAUTH_USER;
363             $request->_prefs->set('emailVerified', true);
364         } else {  // not current user
365             if (ENABLE_USER_NEW) {
366                 $u = WikiUser($userid);
367                 $u->getPreferences();
368                 $prefs = &$u->_prefs;
369             } else {
370                 $u = new WikiUser($request, $userid);
371                 $prefs = $u->getPreferences();
372             }
373             $u->_level = WIKIAUTH_USER;
374             $request->setUser($u);
375             $request->_prefs->set('emailVerified', true);
376         }
377         unset($data[$id]);
378         $wikidb->set('ConfirmEmail', $data);
379         return HTML(HTML::h1("Confirm E-mail address"),
380                     HTML::p("Your e-mail address has now been confirmed."));
381     }
382 }
383
384
385 // $Log: not supported by cvs2svn $
386 // Revision 1.6  2007/01/09 12:34:55  rurban
387 // Fix typo (syntax error)
388 //
389 // Revision 1.5  2007/01/07 18:42:58  rurban
390 // Add MAILER_LOG logfile
391 //
392 // Revision 1.4  2007/01/04 16:47:49  rurban
393 // improve text
394 //
395 // Revision 1.3  2006/12/24 13:35:43  rurban
396 // added experimental EMailConfirm auth. (not yet tested)
397 // requires actionpage ConfirmEmail
398 // TBD: purge expired cookies
399 //
400 // Revision 1.2  2006/12/23 11:50:45  rurban
401 // added missing result init
402 //
403 // Revision 1.1  2006/12/22 17:59:55  rurban
404 // Move mailer functions into seperate MailNotify.php
405 //
406
407 // Local Variables:
408 // mode: php
409 // tab-width: 8
410 // c-basic-offset: 4
411 // c-hanging-comment-ender-p: nil
412 // indent-tabs-mode: nil
413 // End:   
414 ?>