update some plugin classes in phpdocs, reformatting
[dokuwiki.git] / inc / Subscriptions / BulkSubscriptionSender.php
blob76f0997a4f5dd8948c47837b8cb3341d370e0089
1 <?php
4 namespace dokuwiki\Subscriptions;
7 use dokuwiki\ChangeLog\PageChangeLog;
8 use dokuwiki\Extension\AuthPlugin;
9 use dokuwiki\Input\Input;
10 use Exception;
12 class BulkSubscriptionSender extends SubscriptionSender
15 /**
16 * Send digest and list subscriptions
18 * This sends mails to all subscribers that have a subscription for namespaces above
19 * the given page if the needed $conf['subscribe_time'] has passed already.
21 * This function is called form lib/exe/indexer.php
23 * @param string $page
24 * @return int number of sent mails
25 * @throws Exception
27 public function sendBulk($page)
29 $subscriberManager = new SubscriberManager();
30 if (!$subscriberManager->isenabled()) {
31 return 0;
34 /** @var AuthPlugin $auth */
35 global $auth;
36 global $conf;
37 global $USERINFO;
38 /** @var Input $INPUT */
39 global $INPUT;
40 $count = 0;
42 $subscriptions = $subscriberManager->subscribers($page, null, ['digest', 'list']);
44 // remember current user info
45 $olduinfo = $USERINFO;
46 $olduser = $INPUT->server->str('REMOTE_USER');
48 foreach ($subscriptions as $target => $users) {
49 if (!$this->lock($target)) {
50 continue;
53 foreach ($users as $user => $info) {
54 [$style, $lastupdate] = $info;
56 $lastupdate = (int)$lastupdate;
57 if ($lastupdate + $conf['subscribe_time'] > time()) {
58 // Less than the configured time period passed since last
59 // update.
60 continue;
63 // Work as the user to make sure ACLs apply correctly
64 $USERINFO = $auth->getUserData($user);
65 $INPUT->server->set('REMOTE_USER', $user);
66 if ($USERINFO === false) {
67 continue;
69 if (!$USERINFO['mail']) {
70 continue;
73 if (substr($target, -1, 1) === ':') {
74 // subscription target is a namespace, get all changes within
75 $changes = getRecentsSince($lastupdate, null, getNS($target));
76 } else {
77 // single page subscription, check ACL ourselves
78 if (auth_quickaclcheck($target) < AUTH_READ) {
79 continue;
81 $meta = p_get_metadata($target);
82 $changes = [$meta['last_change']];
85 // Filter out pages only changed in small and own edits
86 $change_ids = [];
87 foreach ($changes as $rev) {
88 $n = 0;
89 $pagelog = new PageChangeLog($rev['id']);
90 while (!is_null($rev) && $rev['date'] >= $lastupdate &&
91 ($INPUT->server->str('REMOTE_USER') === $rev['user'] ||
92 $rev['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT)
93 ) {
94 $revisions = $pagelog->getRevisions($n++, 1);
95 $rev = ($revisions !== []) ? $pagelog->getRevisionInfo($revisions[0]) : null;
98 if (!is_null($rev) && $rev['date'] >= $lastupdate) {
99 // Some change was not a minor one and not by myself
100 $change_ids[] = $rev['id'];
104 // send it
105 if ($style === 'digest') {
106 foreach ($change_ids as $change_id) {
107 $this->sendDigest(
108 $USERINFO['mail'],
109 $change_id,
110 $lastupdate
112 $count++;
114 } elseif ($style === 'list') {
115 $this->sendList($USERINFO['mail'], $change_ids, $target);
116 $count++;
118 // TODO: Handle duplicate subscriptions.
120 // Update notification time.
121 $subscriberManager->add($target, $user, $style, time());
123 $this->unlock($target);
126 // restore current user info
127 $USERINFO = $olduinfo;
128 $INPUT->server->set('REMOTE_USER', $olduser);
129 return $count;
133 * Lock subscription info
135 * We don't use io_lock() her because we do not wait for the lock and use a larger stale time
137 * @param string $id The target page or namespace, specified by id; Namespaces
138 * are identified by appending a colon.
140 * @return bool true, if you got a succesful lock
141 * @author Adrian Lang <lang@cosmocode.de>
143 protected function lock($id)
145 global $conf;
147 $lock = $conf['lockdir'] . '/_subscr_' . md5($id) . '.lock';
149 if (is_dir($lock) && time() - @filemtime($lock) > 60 * 5) {
150 // looks like a stale lock - remove it
151 @rmdir($lock);
154 // try creating the lock directory
155 if (!@mkdir($lock)) {
156 return false;
159 if ($conf['dperm']) {
160 chmod($lock, $conf['dperm']);
162 return true;
166 * Unlock subscription info
168 * @param string $id The target page or namespace, specified by id; Namespaces
169 * are identified by appending a colon.
171 * @return bool
172 * @author Adrian Lang <lang@cosmocode.de>
174 protected function unlock($id)
176 global $conf;
177 $lock = $conf['lockdir'] . '/_subscr_' . md5($id) . '.lock';
178 return @rmdir($lock);
182 * Send a digest mail
184 * Sends a digest mail showing a bunch of changes of a single page. Basically the same as sendPageDiff()
185 * but determines the last known revision first
187 * @param string $subscriber_mail The target mail address
188 * @param string $id The ID
189 * @param int $lastupdate Time of the last notification
191 * @return bool
192 * @author Adrian Lang <lang@cosmocode.de>
195 protected function sendDigest($subscriber_mail, $id, $lastupdate)
197 $pagelog = new PageChangeLog($id);
198 $n = 0;
199 do {
200 $rev = $pagelog->getRevisions($n++, 1);
201 $rev = ($rev !== []) ? $rev[0] : null;
202 } while (!is_null($rev) && $rev > $lastupdate);
204 // TODO I'm not happy with the following line and passing $this->mailer around. Not sure how to solve it better
205 $pageSubSender = new PageSubscriptionSender($this->mailer);
206 return $pageSubSender->sendPageDiff(
207 $subscriber_mail,
208 'subscr_digest',
209 $id,
210 $rev
215 * Send a list mail
217 * Sends a list mail showing a list of changed pages.
219 * @param string $subscriber_mail The target mail address
220 * @param array $ids Array of ids
221 * @param string $ns_id The id of the namespace
223 * @return bool true if a mail was sent
224 * @author Adrian Lang <lang@cosmocode.de>
227 protected function sendList($subscriber_mail, $ids, $ns_id)
229 if ($ids === []) {
230 return false;
233 $tlist = '';
234 $hlist = '<ul>';
235 foreach ($ids as $id) {
236 $link = wl($id, [], true);
237 $tlist .= '* ' . $link . NL;
238 $hlist .= '<li><a href="' . $link . '">' . hsc($id) . '</a></li>' . NL;
240 $hlist .= '</ul>';
242 $id = prettyprint_id($ns_id);
243 $trep = [
244 'DIFF' => rtrim($tlist),
245 'PAGE' => $id,
246 'SUBSCRIBE' => wl($id, ['do' => 'subscribe'], true, '&'),
248 $hrep = [
249 'DIFF' => $hlist,
252 return $this->send(
253 $subscriber_mail,
254 'subscribe_list',
255 $ns_id,
256 'subscr_list',
257 $trep,
258 $hrep