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