2 rcs_id('$Id: MailNotify.php,v 1.8 2007-01-25 07:41:54 rurban Exp $');
5 * Handle the pagelist pref[notifyPages] logic for users
6 * and notify => hash ( page => (userid => userhash) ) for pages.
7 * Generate notification emails.
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
20 * ? handle emailed confirmation links (EmailSignup, ModeratedPage)
26 if (!defined("MAILER_LOG"))
28 define("MAILER_LOG", 'c:/wikimail.log');
30 define("MAILER_LOG", '/var/log/wikimail.log');
34 function MailNotify($pagename) {
35 $this->pagename = $pagename; /* which page */
36 $this->emails = array(); /* to which addresses */
37 $this->userids = array(); /* corresponding array of displayed names,
38 don't display the email addresses */
39 /* From: from whom the mail appears to be */
40 $this->from = $this->fromId();
45 return $request->_user->getId() . '@' . $request->get('REMOTE_HOST');
48 function userEmail($userid, $doverify = true) {
50 $u = $request->getUser();
51 if ($u->UserName() == $userid) { // lucky: current user
52 $prefs = $u->getPreferences();
53 $email = $prefs->get('email');
54 // do a dynamic emailVerified check update
55 if ($doverify and !$request->_prefs->get('emailVerified'))
57 } else { // not current user
58 if (ENABLE_USER_NEW) {
59 $u = WikiUser($userid);
63 $u = new WikiUser($request, $userid);
64 $prefs = $u->getPreferences();
66 $email = $prefs->get('email');
67 if ($doverify and !$prefs->get('emailVerified')) {
75 * getPageChangeEmails($notify)
76 * @param $notify: hash ( page => (userid => userhash) )
78 * unique array of ($emails, $userids)
80 function getPageChangeEmails($notify) {
82 $emails = array(); $userids = array();
83 foreach ($notify as $page => $users) {
84 if (glob_match($page, $this->pagename)) {
85 foreach ($users as $userid => $user) {
86 if (!$user) { // handle the case for ModeratePage:
87 // no prefs, just userid's.
88 $emails[] = $this->userEmail($userid, false);
91 if (!empty($user['verified']) and !empty($user['email'])) {
92 $emails[] = $user['email'];
94 } elseif (!empty($user['email'])) {
95 // do a dynamic emailVerified check update
96 $email = $this->userEmail($userid, true);
98 $notify[$page][$userid]['verified'] = 1;
99 $request->_dbi->set('notify', $notify);
101 $userids[] = $userid;
104 // ignore verification
107 if (!in_array($user['email'], $emails))
108 $emails[] = $user['email'];
115 $this->emails = array_unique($emails);
116 $this->userids = array_unique($userids);
117 return array($this->emails, $this->userids);
120 function sendMail($subject, $content,
125 $emails = $this->emails;
127 if (!$notice) $notice = _("PageChange Notification of %s");
128 $ok = mail(($to = array_shift($emails)),
129 "[".WIKI_NAME."] ".$subject,
130 $subject."\n".$content,
131 "From: $from\r\nBcc: ".join(',', $emails)
133 if (MAILER_LOG and is_writable(MAILER_LOG)) {
134 $f = fopen(MAILER_LOG, "a");
135 fwrite($f, "\n\nX-MailSentOK: " . $ok ? 'OK' : 'FAILED');
137 global $ErrorManager;
138 // get last error message
139 $last_err = $ErrorManager->_postponed_errors[count($ErrorHandler->_postponed_errors)-1];
140 fwrite($f, "\nX-MailFailure: " . $last_err);
142 fwrite($f, "\nDate: " . CTime());
143 fwrite($f, "\nSubject: $subject");
144 fwrite($f, "\nFrom: $from");
145 fwrite($f, "\nTo: $to");
146 fwrite($f, "\nBcc: ".join(',', $emails));
147 fwrite($f, "\n\n". $content);
152 trigger_error(sprintf($notice, $this->pagename)
154 . sprintf(_("sent to %s"), join(',',$this->userids)),
158 trigger_error(sprintf($notice, $this->pagename)
160 . sprintf(_("Error: Couldn't send %s to %s"),
161 $subject."\n".$content, join(',',$this->userids)),
168 * Send udiff for a changed page to multiple users.
169 * See rename and remove methods also
171 function sendPageChangeNotification(&$wikitext, $version, &$meta) {
175 if (@is_array($request->_deferredPageChangeNotification)) {
176 // collapse multiple changes (loaddir) into one email
177 $request->_deferredPageChangeNotification[] =
178 array($this->pagename, $this->emails, $this->userids);
181 $backend = &$request->_dbi->_backend;
182 $subject = _("Page change").' '.urlencode($this->pagename);
183 $previous = $backend->get_previous_version($this->pagename, $version);
184 if (!isset($meta['mtime'])) $meta['mtime'] = time();
186 $difflink = WikiURL($this->pagename, array('action'=>'diff'), true);
187 $dbh = &$request->getDbh();
188 $cache = &$dbh->_wikidb->_cache;
189 //$cache = &$request->_dbi->_cache;
190 $content = explode("\n", $wikitext);
191 $prevdata = $cache->get_versiondata($this->pagename, $previous, true);
192 if (empty($prevdata['%content']))
193 $prevdata = $backend->get_versiondata($this->pagename, $previous, true);
194 $other_content = explode("\n", $prevdata['%content']);
196 include_once("lib/difflib.php");
197 $diff2 = new Diff($other_content, $this_content);
198 //$context_lines = max(4, count($other_content) + 1,
199 // count($this_content) + 1);
200 $fmt = new UnifiedDiffFormatter(/*$context_lines*/);
201 $content = $this->pagename . " " . $previous . " " .
202 Iso8601DateTime($prevdata['mtime']) . "\n";
203 $content .= $this->pagename . " " . $version . " " .
204 Iso8601DateTime($meta['mtime']) . "\n";
205 $content .= $fmt->format($diff2);
208 $difflink = WikiURL($this->pagename,array(),true);
209 $content = $this->pagename . " " . $version . " " .
210 Iso8601DateTime($meta['mtime']) . "\n";
211 $content .= _("New page");
213 $editedby = sprintf(_("Edited by: %s"), $this->from);
214 //$editedby = sprintf(_("Edited by: %s"), $meta['author']);
215 $this->sendMail($subject,
216 $editedby."\n".$difflink."\n\n".$content);
220 * support mass rename / remove (not yet tested)
222 function sendPageRenameNotification ($to, &$meta) {
225 if (@is_array($request->_deferredPageRenameNotification)) {
226 $request->_deferredPageRenameNotification[] =
227 array($this->pagename, $to, $meta, $this->emails, $this->userids);
229 $pagename = $this->pagename;
230 //$editedby = sprintf(_("Edited by: %s"), $meta['author']) . ' ' . $meta['author_id'];
231 $editedby = sprintf(_("Edited by: %s"), $this->from);
232 $subject = sprintf(_("Page rename %s to %s"), urlencode($pagename), urlencode($to));
233 $link = WikiURL($to, true);
234 $this->sendMail($subject,
235 $editedby."\n".$link."\n\n"."Renamed $pagename to $to");
242 function onChangePage (&$wikidb, &$wikitext, $version, &$meta) {
244 if (!isa($GLOBALS['request'],'MockRequest')) {
245 $notify = $wikidb->get('notify');
246 /* Generate notification emails? */
247 if (!empty($notify) and is_array($notify)) {
248 if (empty($this->pagename))
249 $this->pagename = $meta['pagename'];
250 //TODO: defer it (quite a massive load if you MassRevert some pages).
251 //TODO: notification class which catches all changes,
252 // and decides at the end of the request what to mail.
253 // (type, page, who, what, users, emails)
254 // Could be used for ModeratePage and RSS2 Cloud xml-rpc also.
255 $this->getPageChangeEmails($notify);
256 if (!empty($this->emails)) {
257 $result = $this->sendPageChangeNotification($wikitext, $version, $meta);
264 function onDeletePage (&$wikidb, $pagename) {
266 /* Generate notification emails? */
267 if (! $wikidb->isWikiPage($pagename) and !isa($GLOBALS['request'],'MockRequest')) {
268 $notify = $wikidb->get('notify');
269 if (!empty($notify) and is_array($notify)) {
270 //TODO: deferr it (quite a massive load if you remove some pages).
271 $this->getPageChangeEmails($notify);
272 if (!empty($this->emails)) {
273 $editedby = sprintf(_("Removed by: %s"), $this->from); // Todo: host_id
274 //$emails = join(',', $this->emails);
275 $subject = sprintf(_("Page removed %s"), urlencode($pagename));
276 $page = $wikidb->getPage($pagename);
277 $rev = $page->getCurrentRevision(true);
278 $content = $rev->getPackedContent();
279 $result = $this->sendMail($subject,
280 $editedby."\n"."Deleted $pagename"."\n\n".$content);
284 //How to create a RecentChanges entry with explaining summary? Dynamically
286 $page = $this->getPage($pagename);
287 $current = $page->getCurrentRevision();
288 $meta = $current->_data;
289 $version = $current->getVersion();
290 $meta['summary'] = _("removed");
291 $page->save($current->getPackedContent(), $version + 1, $meta);
296 function onRenamePage (&$wikidb, $oldpage, $new_pagename) {
298 if (!isa($GLOBALS['request'], 'MockRequest')) {
299 $notify = $wikidb->get('notify');
300 if (!empty($notify) and is_array($notify)) {
301 $this->getPageChangeEmails($notify);
302 if (!empty($this->emails)) {
303 $newpage = $wikidb->getPage($new_pagename);
304 $current = $newpage->getCurrentRevision();
305 $meta = $current->_data;
306 $this->pagename = $oldpage;
307 $result = $this->sendPageRenameNotification($new_pagename, $meta);
314 * Send mail to user and store the cookie in the db
315 * wikiurl?action=ConfirmEmail&id=bla
317 function sendEmailConfirmation ($email, $userid) {
318 $id = rand_ascii_readable(16);
319 $wikidb = $GLOBALS['request']->getDbh();
320 $data = $wikidb->get('ConfirmEmail');
321 while(!empty($data[$id])) { // id collision
322 $id = rand_ascii_readable(16);
324 $subject = WIKI_NAME . " " . _("e-mail address confirmation");
325 $ip = $request->get('REMOTE_HOST');
326 $expire_date = time() + 7*86400;
327 $content = fmt("Someone, probably you from IP address %s, has registered an
328 account \"%s\" with this e-mail address on %s.
330 To confirm that this account really does belong to you and activate
331 e-mail features on %s, open this link in your browser:
335 If this is *not* you, don't follow the link. This confirmation code
337 $ip, $userid, WIKI_NAME, WIKI_NAME,
338 WikiURL(HOME_PAGE, array('action' => 'ConfirmEmail',
341 CTime($expire_date));
342 $this->sendMail($subject, $content, "", true);
343 $data[$id] = array('email' => $email,
345 'expire' => $expire_date);
346 $wikidb->set('ConfirmEmail', $data);
350 function checkEmailConfirmation () {
352 $wikidb = $request->getDbh();
353 $data = $wikidb->get('ConfirmEmail');
354 $id = $request->getArg('id');
355 if (empty($data[$id])) { // id not found
356 return HTML(HTML::h1("Confirm E-mail address"),
357 HTML::h1("Sorry! Wrong URL"));
360 $userid = $data['userid'];
361 $email = $data['email'];
362 $u = $request->getUser();
363 if ($u->UserName() == $userid) { // lucky: current user (session)
364 $prefs = $u->getPreferences();
365 $request->_user->_level = WIKIAUTH_USER;
366 $request->_prefs->set('emailVerified', true);
367 } else { // not current user
368 if (ENABLE_USER_NEW) {
369 $u = WikiUser($userid);
370 $u->getPreferences();
371 $prefs = &$u->_prefs;
373 $u = new WikiUser($request, $userid);
374 $prefs = $u->getPreferences();
376 $u->_level = WIKIAUTH_USER;
377 $request->setUser($u);
378 $request->_prefs->set('emailVerified', true);
381 $wikidb->set('ConfirmEmail', $data);
382 return HTML(HTML::h1("Confirm E-mail address"),
383 HTML::p("Your e-mail address has now been confirmed."));
388 // $Log: not supported by cvs2svn $
389 // Revision 1.7 2007/01/20 11:25:38 rurban
390 // Fix onDeletePage warning and content
392 // Revision 1.6 2007/01/09 12:34:55 rurban
393 // Fix typo (syntax error)
395 // Revision 1.5 2007/01/07 18:42:58 rurban
396 // Add MAILER_LOG logfile
398 // Revision 1.4 2007/01/04 16:47:49 rurban
401 // Revision 1.3 2006/12/24 13:35:43 rurban
402 // added experimental EMailConfirm auth. (not yet tested)
403 // requires actionpage ConfirmEmail
404 // TBD: purge expired cookies
406 // Revision 1.2 2006/12/23 11:50:45 rurban
407 // added missing result init
409 // Revision 1.1 2006/12/22 17:59:55 rurban
410 // Move mailer functions into seperate MailNotify.php
417 // c-hanging-comment-ender-p: nil
418 // indent-tabs-mode: nil