aboutsummaryrefslogtreecommitdiff
path: root/scripts/notify.php
blob: 36600994d17368f9411340657272d785ee79da42 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
<?php if (!defined('PmWiki')) exit();
/*  Copyright 2006-2022 Patrick R. Michaud (pmichaud@pobox.com)
    This file is part of PmWiki; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published
    by the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.  See pmwiki.php for full details.

    This script enables email notifications to be sent when posts
    are made.  It is included by default from the stdconfig.php 
    script if $EnableNotify is set to non-zero.

    Once enabled, the addresses to receive messages are configured
    via the Site.NotifyList page.  A simple line in that page
    such as 

        notify=somebody@example.com

    will cause messages to be periodically sent to "somebody@example.com"
    listing the pages that have changed on the site since the previous
    message was sent.  Multiple notify lines can be placed in the page,
    and there are options to restrict the types of notifications
    desired.  For more details, see the PmWiki.Notify page in
    the documentation.

    Several variables set defaults for this script:

    $NotifyFrom - return email address to use in message.
    $NotifyDelay - number of seconds to wait before sending mail
        after the first post.
    $NotifySquelch - minimum number of seconds between sending email
        messages to each address.  Individual "notify=" lines in
        Site.NotifyList can override this value via a custom "squelch="
        parameter.
    $NotifyFile - scratchpad file used to keep track of pending emails.
    $NotifyListPageFmt - name of the NotifyList configuration page.
    $NotifySubjectFmt - subject line for sent messages.
    $NotifyBodyFmt - body of message to be sent.  The string '$NotifyItems'
        is replaced with the list of posts in the email.
    $NotifyItemFmt - the format for each post to be included in a notification.
    $NotifyTimeFmt - the format for dates and times ($PostTime) 
        in notification messages.
    $NotifyHeaders - any additional message headers to be sent.
    $NotifyParameters - any additional parameters to be passed to PHP's
        mail() function.
    
    Script maintained by Petko YOTOV www.pmwiki.org/petko
*/

SDV($NotifyDelay, 0);
SDV($NotifySquelch, 10800);
SDV($NotifyFile, "$WorkDir/.notifylist");
SDV($NotifyListPageFmt, '$SiteAdminGroup.NotifyList');
SDV($NotifySubjectFmt, '[$WikiTitle] recent notify posts');
SDV($NotifyBodyFmt, 
  "Recent \$WikiTitle posts:\n" 
  . "  \$ScriptUrl/$[{\$SiteGroup}/AllRecentChanges]\n\n\$NotifyItems\n");
SDV($NotifyTimeFmt, $TimeFmt);
SDV($NotifyItemFmt, 
  ' * {$FullName} . . . $PostTime by {$Author}');
SDV($NotifyHeaders, '');
SDV($NotifyParameters, '');

if (@$NotifyFrom)
  $NotifyHeaders = "From: $NotifyFrom\r\n$NotifyHeaders";

$EditFunctions[] = 'PostNotify';

##   check if we need to do notifications
if ($action != 'edit' && $action != 'postupload') NotifyCheck($pagename);


function NotifyCheck($pagename) {
  global $NotifyFile, $Now, $LastModTime;
  $nfp = @fopen($NotifyFile, 'r');
  if (!$nfp) return;
  $nextevent = fgets($nfp);
  fclose($nfp);
  if ($Now < $nextevent && $LastModTime < filemtime($NotifyFile)) return;
  register_shutdown_function('NotifyUpdate', $pagename, getcwd());
}

    
function PostNotify($pagename, &$page, &$new) {
  global $IsPagePosted;
  if ($IsPagePosted) 
    register_shutdown_function('NotifyUpdate', $pagename, getcwd());
}


function NotifyUpdate($pagename, $dir='') {
  global $NotifyList, $NotifyListPageFmt, $NotifyFile, $IsPagePosted, $IsUploadPosted,
    $FmtV, $NotifyTimeFmt, $NotifyItemFmt, $SearchPatterns, $MailFunction,
    $NotifySquelch, $NotifyDelay, $Now, $Charset, $EnableNotifySubjectEncode,
    $NotifySubjectFmt, $NotifyBodyFmt, $NotifyHeaders, $NotifyParameters,
    $NotifyRelatedTrailFmt;

  $abort = ignore_user_abort(true);
  if ($dir) { flush(); chdir($dir); }

  $GLOBALS['EnableRedirect'] = 0;

  ##   Read in the current notify configuration
  $pn = FmtPageName($NotifyListPageFmt, $pagename);
  $npage = ReadPage($pn, READPAGE_CURRENT);
  preg_match_all('/^[\s*:#>-]*(notify[:=].*)/m', strval(@$npage['text']), $nlist);
  $nlist = array_merge((array)@$NotifyList, (array)@$nlist[1]);
  if (!$nlist) return;

  ##   make sure other processes are locked out
  Lock(2);

  ##   let's load the current .notifylist table
  $nfile = FmtPageName($NotifyFile, $pagename);
  $nfp = @fopen($nfile, 'r');
  if ($nfp) {
    ##   get our current squelch and delay timestamps
    clearstatcache();
    $sz = filesize($nfile);
    list($nextevent, $firstpost) = explode(' ', rtrim(fgets($nfp, $sz)));
    ##   restore our notify array
    $notify = unserialize(fgets($nfp, $sz));
    fclose($nfp);
  }
  if (!is_array($notify)) $notify = array();

  ##   if this is for a newly posted page, get its information
  if ($IsPagePosted || $IsUploadPosted) {
    $page = ReadPage($pagename, READPAGE_CURRENT);
    $FmtV['$PostTime'] = PSFT($NotifyTimeFmt, $Now);
    $item = $tzitem = urlencode(FmtPageName($NotifyItemFmt, $pagename));
    if ($firstpost < 1) $firstpost = $Now;
  }
  
  foreach($nlist as $n) {
    $opt = ParseArgs($n);
    $mailto = preg_split('/[\s,]+/', $opt['notify']);
    if (!$mailto) continue;
    if (@$opt['squelch']) 
      foreach($mailto as $m) $squelch[$m] = $opt['squelch'];
    if (!$IsPagePosted) continue;
    if (@$opt['link']) {
      $link = MakePageName($pagename, $opt['link']);
      if (!preg_match("/(^|,)$link(,|$)/i", $page['targets'])) continue;
    }
    $pats = @(array)$SearchPatterns[$opt['list']];
    if (@$opt['group']) $pats[] = FixGlob($opt['group'], '$1$2.*');
    if (@$opt['name']) $pats[] = FixGlob($opt['name'], '$1*.$2');
    if ($pats && !MatchPageNames($pagename, $pats)) continue;
    if (@$opt['trail']) {
      $trail = ReadTrail($pagename, $opt['trail']);
      for ($i=0; $i<count($trail); $i++) {
        $tpn = $trail[$i]['pagename'];
        if ($pagename == $tpn) break;
        if (IsEnabled($NotifyRelatedTrailFmt)) {
          $pat = FmtPageName($NotifyRelatedTrailFmt, $tpn);
          if (MatchPageNames($pagename, $pat)) break;
        }
      }
      if ($i >= count($trail)) continue;
    }
    if (@$opt['tz']) {
      $FmtV['$PostTime'] = PSFT($NotifyTimeFmt, $Now, @$opt['locale'], $opt['tz']);
      $tzitem = urlencode(FmtPageName($NotifyItemFmt, $pagename));
    }
    foreach($mailto as $m) { $notify[$m][] = $tzitem; }
  }

  $nnow = time();
  if ($nnow < $firstpost + $NotifyDelay) 
    $nextevent = $firstpost + $NotifyDelay;
  else {
    $firstpost = 0;
    $nextevent = $nnow + 86400;
    $mailto = array_keys($notify);
    $subject = FmtPageName($NotifySubjectFmt, $pagename);
    if(IsEnabled($EnableNotifySubjectEncode, 0)
      && preg_match("/[^\x20-\x7E]/", $subject))
        $subject = strtoupper("=?$Charset?B?"). base64_encode($subject)."?=";
    $body = FmtPageName($NotifyBodyFmt, $pagename);
    foreach ($mailto as $m) {
      $msquelch = @$notify[$m]['lastmail'] +
                    ((@$squelch[$m]) ? $squelch[$m] : $NotifySquelch);
      if ($nnow < $msquelch) {
        if ($msquelch < $nextevent && count($notify[$m])>1)
          $nextevent = $msquelch;
        continue;
      }
      unset($notify[$m]['lastmail']);
      if (!$notify[$m]) { unset($notify[$m]); continue; }
      $mbody = str_replace('$NotifyItems',   
                           urldecode(implode("\n", $notify[$m])), $body);
      SDV($MailFunction, 'mail');
      if ($NotifyParameters && !@ini_get('safe_mode'))
        $MailFunction($m, $subject, $mbody, $NotifyHeaders, $NotifyParameters);
      else 
        $MailFunction($m, $subject, $mbody, $NotifyHeaders);
      $notify[$m] = array('lastmail' => $nnow);
    }
  }

  ##   save the updated notify status
  $nfp = @fopen($nfile, "w");
  if ($nfp) {
    fputs($nfp, "$nextevent $firstpost\n");
    fputs($nfp, serialize($notify) . "\n");
    fclose($nfp);
  }
  Lock(0);
  return true;
}