2 * Copyright (C) 2006-2007 Reini Urban
3 * Copyright (C) 2009 Marc-Etienne Vargenau, Alcatel-Lucent
5 * This file is part of PhpWiki.
7 * PhpWiki is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * PhpWiki is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with PhpWiki; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * Handle the pagelist pref[notifyPages] logic for users
24 * and notify => hash ( page => (userid => userhash) ) for pages.
25 * Generate notification emails.
27 * We add WikiDB handlers and register ourself there:
28 * onChangePage, onDeletePage, onRenamePage
29 * Administrative actions:
30 * [Watch] WatchPage - add a page, or delete watch handlers into the users
31 * pref[notifyPages] slot.
32 * My WatchList - view or edit list/regex of pref[notifyPages].
33 * EMailConfirm methods: send and verify
38 * ? handle emailed confirmation links (EmailSignup, ModeratedPage)
44 if (!defined("MAILER_LOG"))
46 define("MAILER_LOG", 'c:/wikimail.log');
48 define("MAILER_LOG", '/var/log/wikimail.log');
52 function MailNotify($pagename) {
53 $this->pagename = $pagename; /* which page */
54 $this->emails = array(); /* to which addresses */
55 $this->userids = array(); /* corresponding array of displayed names,
56 don't display the email addresses */
57 /* From: from whom the mail appears to be */
58 $this->from = $this->fromId();
63 return $request->_user->getId() . '@' . $request->get('REMOTE_HOST');
66 function userEmail($userid, $doverify = true) {
69 // Disable verification of emails for corporate env.
70 if (defined('GFORGE') and GFORGE) {
74 $u = $request->getUser();
75 if ($u->UserName() == $userid) { // lucky: current user
76 $prefs = $u->getPreferences();
77 $email = $prefs->get('email');
78 // do a dynamic emailVerified check update
79 if ($doverify and !$request->_prefs->get('emailVerified'))
81 } else { // not current user
82 if (ENABLE_USER_NEW) {
83 $u = WikiUser($userid);
87 $u = new WikiUser($request, $userid);
88 $prefs = $u->getPreferences();
90 $email = $prefs->get('email');
91 if ($doverify and !$prefs->get('emailVerified')) {
99 * getPageChangeEmails($notify)
100 * @param $notify: hash ( page => (userid => userhash) )
102 * unique array of ($emails, $userids)
104 function getPageChangeEmails($notify) {
106 $emails = array(); $userids = array();
107 foreach ($notify as $page => $users) {
108 if (glob_match($page, $this->pagename)) {
109 foreach ($users as $userid => $user) {
110 if (!$user) { // handle the case for ModeratePage:
111 // no prefs, just userid's.
112 $emails[] = $this->userEmail($userid, false);
113 $userids[] = $userid;
115 if (!empty($user['verified']) and !empty($user['email'])) {
116 $emails[] = $user['email'];
117 $userids[] = $userid;
118 } elseif (!empty($user['email'])) {
119 // do a dynamic emailVerified check update
120 $email = $this->userEmail($userid, true);
122 $notify[$page][$userid]['verified'] = 1;
123 $request->_dbi->set('notify', $notify);
125 $userids[] = $userid;
128 // ignore verification
131 if (!in_array($user['email'], $emails))
132 $emails[] = $user['email'];
139 $this->emails = array_unique($emails);
140 $this->userids = array_unique($userids);
141 return array($this->emails, $this->userids);
144 function sendMail($subject, $content,
149 if (!DEBUG and $silent === 0)
151 $emails = $this->emails;
153 if (!$notice) $notice = _("PageChange Notification of %s");
154 $ok = mail(($to = array_shift($emails)),
155 "[".WIKI_NAME."] ".$subject,
156 $subject."\n".$content,
157 "From: $from\r\nBcc: ".join(',', $emails)
159 if (MAILER_LOG and is_writable(MAILER_LOG)) {
160 $f = fopen(MAILER_LOG, "a");
161 fwrite($f, "\n\nX-MailSentOK: " . $ok ? 'OK' : 'FAILED');
163 global $ErrorManager;
164 // get last error message
165 $last_err = $ErrorManager->_postponed_errors[count($ErrorHandler->_postponed_errors)-1];
166 fwrite($f, "\nX-MailFailure: " . $last_err);
168 fwrite($f, "\nDate: " . CTime());
169 fwrite($f, "\nSubject: $subject");
170 fwrite($f, "\nFrom: $from");
171 fwrite($f, "\nTo: $to");
172 fwrite($f, "\nBcc: ".join(',', $emails));
173 fwrite($f, "\n\n". $content);
178 trigger_error(sprintf($notice, $this->pagename)
180 . sprintf(_("sent to %s"), join(',',$this->userids)),
184 trigger_error(sprintf($notice, $this->pagename)
186 . sprintf(_("Error: Couldn't send %s to %s"),
187 $subject."\n".$content, join(',',$this->userids)),
194 * Send udiff for a changed page to multiple users.
195 * See rename and remove methods also
197 function sendPageChangeNotification(&$wikitext, $version, &$meta) {
201 if (@is_array($request->_deferredPageChangeNotification)) {
202 // collapse multiple changes (loaddir) into one email
203 $request->_deferredPageChangeNotification[] =
204 array($this->pagename, $this->emails, $this->userids);
207 $backend = &$request->_dbi->_backend;
208 $subject = _("Page change").' '.urlencode($this->pagename);
209 $previous = $backend->get_previous_version($this->pagename, $version);
210 if (!isset($meta['mtime'])) $meta['mtime'] = time();
212 $difflink = WikiURL($this->pagename, array('action'=>'diff'), true);
213 $cache = &$request->_dbi->_cache;
214 $this_content = explode("\n", $wikitext);
215 $prevdata = $cache->get_versiondata($this->pagename, $previous, true);
216 if (empty($prevdata['%content']))
217 $prevdata = $backend->get_versiondata($this->pagename, $previous, true);
218 $other_content = explode("\n", $prevdata['%content']);
220 include_once("lib/difflib.php");
221 $diff2 = new Diff($other_content, $this_content);
222 //$context_lines = max(4, count($other_content) + 1,
223 // count($this_content) + 1);
224 $fmt = new UnifiedDiffFormatter(/*$context_lines*/);
225 $content = $this->pagename . " " . $previous . " " .
226 Iso8601DateTime($prevdata['mtime']) . "\n";
227 $content .= $this->pagename . " " . $version . " " .
228 Iso8601DateTime($meta['mtime']) . "\n";
229 $content .= $fmt->format($diff2);
232 $difflink = WikiURL($this->pagename,array(),true);
233 $content = $this->pagename . " " . $version . " " .
234 Iso8601DateTime($meta['mtime']) . "\n";
235 $content .= _("New page");
237 $editedby = sprintf(_("Edited by: %s"), $this->from);
238 //$editedby = sprintf(_("Edited by: %s"), $meta['author']);
239 $this->sendMail($subject,
240 $editedby."\n".$difflink."\n\n".$content);
244 * Support mass rename / remove (not yet tested)
246 function sendPageRenameNotification ($to, &$meta) {
249 if (@is_array($request->_deferredPageRenameNotification)) {
250 $request->_deferredPageRenameNotification[] =
251 array($this->pagename, $to, $meta, $this->emails, $this->userids);
253 $pagename = $this->pagename;
254 //$editedby = sprintf(_("Edited by: %s"), $meta['author']) . ' ' . $meta['author_id'];
255 $editedby = sprintf(_("Edited by: %s"), $this->from);
256 $subject = sprintf(_("Page rename %s to %s"), urlencode($pagename), urlencode($to));
257 $link = WikiURL($to, true);
258 $this->sendMail($subject,
259 $editedby."\n".$link."\n\n"."Renamed $pagename to $to");
266 function onChangePage (&$wikidb, &$wikitext, $version, &$meta) {
268 if (!isa($GLOBALS['request'],'MockRequest')) {
269 $notify = $wikidb->get('notify');
270 /* Generate notification emails? */
271 if (!empty($notify) and is_array($notify)) {
272 if (empty($this->pagename))
273 $this->pagename = $meta['pagename'];
274 // TODO: Should be used for ModeratePage and RSS2 Cloud xml-rpc also.
275 $this->getPageChangeEmails($notify);
276 if (!empty($this->emails)) {
277 $result = $this->sendPageChangeNotification($wikitext, $version, $meta);
284 function onDeletePage (&$wikidb, $pagename) {
286 /* Generate notification emails? */
287 if (! $wikidb->isWikiPage($pagename) and !isa($GLOBALS['request'],'MockRequest')) {
288 $notify = $wikidb->get('notify');
289 if (!empty($notify) and is_array($notify)) {
290 //TODO: deferr it (quite a massive load if you remove some pages).
291 $this->getPageChangeEmails($notify);
292 if (!empty($this->emails)) {
293 $editedby = sprintf(_("Removed by: %s"), $this->from); // Todo: host_id
294 //$emails = join(',', $this->emails);
295 $subject = sprintf(_("Page removed %s"), urlencode($pagename));
296 $page = $wikidb->getPage($pagename);
297 $rev = $page->getCurrentRevision(true);
298 $content = $rev->getPackedContent();
299 $result = $this->sendMail($subject,
300 $editedby."\n"."Deleted $pagename"."\n\n".$content);
304 //How to create a RecentChanges entry with explaining summary? Dynamically
306 $page = $this->getPage($pagename);
307 $current = $page->getCurrentRevision();
308 $meta = $current->_data;
309 $version = $current->getVersion();
310 $meta['summary'] = _("removed");
311 $page->save($current->getPackedContent(), $version + 1, $meta);
316 function onRenamePage (&$wikidb, $oldpage, $new_pagename) {
318 if (!isa($GLOBALS['request'], 'MockRequest')) {
319 $notify = $wikidb->get('notify');
320 if (!empty($notify) and is_array($notify)) {
321 $this->getPageChangeEmails($notify);
322 if (!empty($this->emails)) {
323 $newpage = $wikidb->getPage($new_pagename);
324 $current = $newpage->getCurrentRevision();
325 $meta = $current->_data;
326 $this->pagename = $oldpage;
327 $result = $this->sendPageRenameNotification($new_pagename, $meta);
334 * Send mail to user and store the cookie in the db
335 * wikiurl?action=ConfirmEmail&id=bla
337 function sendEmailConfirmation ($email, $userid) {
338 $id = rand_ascii_readable(16);
339 $wikidb = $GLOBALS['request']->getDbh();
340 $data = $wikidb->get('ConfirmEmail');
341 while(!empty($data[$id])) { // id collision
342 $id = rand_ascii_readable(16);
344 $subject = WIKI_NAME . " " . _("e-mail address confirmation");
345 $ip = $request->get('REMOTE_HOST');
346 $expire_date = time() + 7*86400;
347 $content = fmt("Someone, probably you from IP address %s, has registered an
348 account \"%s\" with this e-mail address on %s.
350 To confirm that this account really does belong to you and activate
351 e-mail features on %s, open this link in your browser:
355 If this is *not* you, don't follow the link. This confirmation code
357 $ip, $userid, WIKI_NAME, WIKI_NAME,
358 WikiURL(HOME_PAGE, array('action' => 'ConfirmEmail',
361 CTime($expire_date));
362 $this->sendMail($subject, $content, "", true);
363 $data[$id] = array('email' => $email,
365 'expire' => $expire_date);
366 $wikidb->set('ConfirmEmail', $data);
370 function checkEmailConfirmation () {
372 $wikidb = $request->getDbh();
373 $data = $wikidb->get('ConfirmEmail');
374 $id = $request->getArg('id');
375 if (empty($data[$id])) { // id not found
376 return HTML(HTML::h1("Confirm E-mail address"),
377 HTML::h1("Sorry! Wrong URL"));
380 $userid = $data['userid'];
381 $email = $data['email'];
382 $u = $request->getUser();
383 if ($u->UserName() == $userid) { // lucky: current user (session)
384 $prefs = $u->getPreferences();
385 $request->_user->_level = WIKIAUTH_USER;
386 $request->_prefs->set('emailVerified', true);
387 } else { // not current user
388 if (ENABLE_USER_NEW) {
389 $u = WikiUser($userid);
390 $u->getPreferences();
391 $prefs = &$u->_prefs;
393 $u = new WikiUser($request, $userid);
394 $prefs = $u->getPreferences();
396 $u->_level = WIKIAUTH_USER;
397 $request->setUser($u);
398 $request->_prefs->set('emailVerified', true);
401 $wikidb->set('ConfirmEmail', $data);
402 return HTML(HTML::h1("Confirm E-mail address"),
403 HTML::p("Your e-mail address has now been confirmed."));
411 // c-hanging-comment-ender-p: nil
412 // indent-tabs-mode: nil