notify.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. <?php if (!defined('PmWiki')) exit();
  2. /* Copyright 2006 Patrick R. Michaud (pmichaud@pobox.com)
  3. This file is part of PmWiki; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published
  5. by the Free Software Foundation; either version 2 of the License, or
  6. (at your option) any later version. See pmwiki.php for full details.
  7. This script enables email notifications to be sent when posts
  8. are made. It is included by default from the stdconfig.php
  9. script if $EnableNotify is set to non-zero.
  10. Once enabled, the addresses to receive messages are configured
  11. via the Site.NotifyList page. A simple line in that page
  12. such as
  13. notify=somebody@example.com
  14. will cause messages to be periodically sent to "somebody@example.com"
  15. listing the pages that have changed on the site since the previous
  16. message was sent. Multiple notify lines can be placed in the page,
  17. and there are options to restrict the types of notifications
  18. desired. For more details, see the PmWiki.Notify page in
  19. the documentation.
  20. Several variables set defaults for this script:
  21. $NotifyFrom - return email address to use in message.
  22. $NotifyDelay - number of seconds to wait before sending mail
  23. after the first post.
  24. $NotifySquelch - minimum number of seconds between sending email
  25. messages to each address. Individual "notify=" lines in
  26. Site.NotifyList can override this value via a custom "squelch="
  27. parameter.
  28. $NotifyFile - scratchpad file used to keep track of pending emails.
  29. $NotifyListPageFmt - name of the NotifyList configuration page.
  30. $NotifySubjectFmt - subject line for sent messages.
  31. $NotifyBodyFmt - body of message to be sent. The string '$NotifyItems'
  32. is replaced with the list of posts in the email.
  33. $NotifyItemFmt - the format for each post to be included in a notification.
  34. $NotifyTimeFmt - the format for dates and times ($PostTime)
  35. in notification messages.
  36. $NotifyHeaders - any additional message headers to be sent.
  37. $NotifyParameters - any additional parameters to be passed to PHP's
  38. mail() function.
  39. */
  40. SDV($NotifyDelay, 0);
  41. SDV($NotifySquelch, 10800);
  42. SDV($NotifyFile, "$WorkDir/.notifylist");
  43. SDV($NotifyListPageFmt, '{$SiteGroup}.NotifyList');
  44. SDV($NotifySubjectFmt, '[$WikiTitle] recent notify posts');
  45. SDV($NotifyBodyFmt,
  46. "Recent \$WikiTitle posts:\n"
  47. . " \$ScriptUrl/$[{\$SiteGroup}/AllRecentChanges]\n\n\$NotifyItems\n");
  48. SDV($NotifyTimeFmt, $TimeFmt);
  49. SDV($NotifyItemFmt,
  50. ' * {$FullName} . . . $PostTime by {$LastModifiedBy}');
  51. SDV($NotifyHeaders, '');
  52. SDV($NotifyParameters, '');
  53. if (@$NotifyFrom)
  54. $NotifyHeaders = "From: $NotifyFrom\r\n$NotifyHeaders";
  55. $EditFunctions[] = 'PostNotify';
  56. ## check if we need to do notifications
  57. if ($action != 'edit') NotifyCheck($pagename);
  58. function NotifyCheck($pagename) {
  59. global $NotifyFile, $Now, $LastModTime;
  60. $nfp = @fopen($NotifyFile, 'r');
  61. if (!$nfp) return;
  62. $nextevent = fgets($nfp);
  63. fclose($nfp);
  64. if ($Now < $nextevent && $LastModTime < filemtime($NotifyFile)) return;
  65. register_shutdown_function('flush');
  66. register_shutdown_function('NotifyUpdate', $pagename, getcwd());
  67. }
  68. function PostNotify($pagename, &$page, &$new) {
  69. global $IsPagePosted;
  70. if ($IsPagePosted) {
  71. register_shutdown_function('flush');
  72. register_shutdown_function('NotifyUpdate', $pagename, getcwd());
  73. }
  74. }
  75. function NotifyUpdate($pagename, $dir='') {
  76. global $NotifyList, $NotifyListPageFmt, $NotifyFile, $IsPagePosted,
  77. $FmtV, $NotifyTimeFmt, $NotifyItemFmt, $SearchPatterns,
  78. $NotifySquelch, $NotifyDelay, $Now,
  79. $NotifySubjectFmt, $NotifyBodyFmt, $NotifyHeaders, $NotifyParameters;
  80. $abort = ignore_user_abort(true);
  81. if ($dir) chdir($dir);
  82. $GLOBALS['EnableRedirect'] = 0;
  83. ## Read in the current notify configuration
  84. $pn = FmtPageName($NotifyListPageFmt, $pagename);
  85. $npage = ReadPage($pn, READPAGE_CURRENT);
  86. preg_match_all('/^[\s*:#->]*(notify[:=].*)/m', $npage['text'], $nlist);
  87. $nlist = array_merge((array)@$NotifyList, (array)@$nlist[1]);
  88. if (!$nlist) return;
  89. ## make sure other processes are locked out
  90. Lock(2);
  91. ## let's load the current .notifylist table
  92. $nfile = FmtPageName($NotifyFile, $pagename);
  93. $nfp = @fopen($nfile, 'r');
  94. if ($nfp) {
  95. ## get our current squelch and delay timestamps
  96. clearstatcache();
  97. $sz = filesize($nfile);
  98. list($nextevent, $firstpost) = explode(' ', rtrim(fgets($nfp, $sz)));
  99. ## restore our notify array
  100. $notify = unserialize(fgets($nfp, $sz));
  101. fclose($nfp);
  102. }
  103. if (!is_array($notify)) $notify = array();
  104. ## if this is for a newly posted page, get its information
  105. if ($IsPagePosted) {
  106. $page = ReadPage($pagename, READPAGE_CURRENT);
  107. $FmtV['$PostTime'] = strftime($NotifyTimeFmt, $Now);
  108. $item = urlencode(FmtPageName($NotifyItemFmt, $pagename));
  109. if ($firstpost < 1) $firstpost = $Now;
  110. }
  111. foreach($nlist as $n) {
  112. $opt = ParseArgs($n);
  113. $mailto = preg_split('/[\s,]+/', $opt['notify']);
  114. if (!$mailto) continue;
  115. if ($opt['squelch'])
  116. foreach($mailto as $m) $squelch[$m] = $opt['squelch'];
  117. if (!$IsPagePosted) continue;
  118. if ($opt['link']) {
  119. $link = MakePageName($pagename, $opt['link']);
  120. if (!preg_match("/(^|,)$link(,|$)/i", $page['targets'])) continue;
  121. }
  122. $pats = @(array)$SearchPatterns[$opt['list']];
  123. if ($opt['group']) $pats[] = FixGlob($opt['group'], '$1$2.*');
  124. if ($opt['name']) $pats[] = FixGlob($opt['name'], '$1*.$2');
  125. if ($pats && !MatchPageNames($pagename, $pats)) continue;
  126. if ($opt['trail']) {
  127. $trail = ReadTrail($pagename, $opt['trail']);
  128. for ($i=0; $i<count($trail); $i++)
  129. if ($trail[$i]['pagename'] == $pagename) break;
  130. if ($i >= count($trail)) continue;
  131. }
  132. foreach($mailto as $m) { $notify[$m][] = $item; }
  133. }
  134. $nnow = time();
  135. if ($nnow < $firstpost + $NotifyDelay)
  136. $nextevent = $firstpost + $NotifyDelay;
  137. else {
  138. $firstpost = 0;
  139. $nextevent = $nnow + 86400;
  140. $mailto = array_keys($notify);
  141. $subject = FmtPageName($NotifySubjectFmt, $pagename);
  142. $body = FmtPageName($NotifyBodyFmt, $pagename);
  143. foreach ($mailto as $m) {
  144. $msquelch = @$notify[$m]['lastmail'] +
  145. ((@$squelch[$m]) ? $squelch[$m] : $NotifySquelch);
  146. if ($nnow < $msquelch) {
  147. if ($msquelch < $nextevent && count($notify[$m])>1)
  148. $nextevent = $msquelch;
  149. continue;
  150. }
  151. unset($notify[$m]['lastmail']);
  152. if (!$notify[$m]) { unset($notify[$m]); continue; }
  153. $mbody = str_replace('$NotifyItems',
  154. urldecode(implode("\n", $notify[$m])), $body);
  155. mail($m, $subject, $mbody, $NotifyHeaders, $NotifyParameters);
  156. $notify[$m] = array('lastmail' => $nnow);
  157. }
  158. }
  159. ## save the updated notify status
  160. $nfp = @fopen($nfile, "w");
  161. if ($nfp) {
  162. fputs($nfp, "$nextevent $firstpost\n");
  163. fputs($nfp, serialize($notify) . "\n");
  164. fclose($nfp);
  165. }
  166. Lock(0);
  167. return true;
  168. }