isenabled()) { return 0; } /** @var AuthPlugin $auth */ global $auth; global $conf; global $USERINFO; /** @var Input $INPUT */ global $INPUT; $count = 0; $subscriptions = $subscriberManager->subscribers($page, null, ['digest', 'list']); // remember current user info $olduinfo = $USERINFO; $olduser = $INPUT->server->str('REMOTE_USER'); foreach ($subscriptions as $target => $users) { if (!$this->lock($target)) { continue; } foreach ($users as $user => $info) { [$style, $lastupdate] = $info; $lastupdate = (int)$lastupdate; if ($lastupdate + $conf['subscribe_time'] > time()) { // Less than the configured time period passed since last // update. continue; } // Work as the user to make sure ACLs apply correctly $USERINFO = $auth->getUserData($user); $INPUT->server->set('REMOTE_USER', $user); if ($USERINFO === false) { continue; } if (!$USERINFO['mail']) { continue; } if (str_ends_with($target, ':')) { // subscription target is a namespace, get all changes within $changes = getRecentsSince($lastupdate, null, getNS($target)); } else { // single page subscription, check ACL ourselves if (auth_quickaclcheck($target) < AUTH_READ) { continue; } $meta = p_get_metadata($target); $changes = [$meta['last_change']]; } // Filter out pages only changed in small and own edits $change_ids = []; foreach ($changes as $rev) { $n = 0; $pagelog = new PageChangeLog($rev['id']); while ( !is_null($rev) && $rev['date'] >= $lastupdate && ($INPUT->server->str('REMOTE_USER') === $rev['user'] || $rev['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) ) { $revisions = $pagelog->getRevisions($n++, 1); $rev = ($revisions !== []) ? $pagelog->getRevisionInfo($revisions[0]) : null; } if (!is_null($rev) && $rev['date'] >= $lastupdate) { // Some change was not a minor one and not by myself $change_ids[] = $rev['id']; } } // send it if ($style === 'digest') { foreach ($change_ids as $change_id) { $this->sendDigest( $USERINFO['mail'], $change_id, $lastupdate ); $count++; } } elseif ($style === 'list') { $this->sendList($USERINFO['mail'], $change_ids, $target); $count++; } // TODO: Handle duplicate subscriptions. // Update notification time. $subscriberManager->add($target, $user, $style, time()); } $this->unlock($target); } // restore current user info $USERINFO = $olduinfo; $INPUT->server->set('REMOTE_USER', $olduser); return $count; } /** * Lock subscription info * * We don't use io_lock() her because we do not wait for the lock and use a larger stale time * * @param string $id The target page or namespace, specified by id; Namespaces * are identified by appending a colon. * * @return bool true, if you got a succesful lock * @author Adrian Lang */ protected function lock($id) { global $conf; $lock = $conf['lockdir'] . '/_subscr_' . md5($id) . '.lock'; if (is_dir($lock) && time() - @filemtime($lock) > 60 * 5) { // looks like a stale lock - remove it @rmdir($lock); } // try creating the lock directory if (!@mkdir($lock)) { return false; } if ($conf['dperm']) { chmod($lock, $conf['dperm']); } return true; } /** * Unlock subscription info * * @param string $id The target page or namespace, specified by id; Namespaces * are identified by appending a colon. * * @return bool * @author Adrian Lang */ protected function unlock($id) { global $conf; $lock = $conf['lockdir'] . '/_subscr_' . md5($id) . '.lock'; return @rmdir($lock); } /** * Send a digest mail * * Sends a digest mail showing a bunch of changes of a single page. Basically the same as sendPageDiff() * but determines the last known revision first * * @param string $subscriber_mail The target mail address * @param string $id The ID * @param int $lastupdate Time of the last notification * * @return bool * @author Adrian Lang * */ protected function sendDigest($subscriber_mail, $id, $lastupdate) { $pagelog = new PageChangeLog($id); $n = 0; do { $rev = $pagelog->getRevisions($n++, 1); $rev = ($rev !== []) ? $rev[0] : null; } while (!is_null($rev) && $rev > $lastupdate); // TODO I'm not happy with the following line and passing $this->mailer around. Not sure how to solve it better $pageSubSender = new PageSubscriptionSender($this->mailer); return $pageSubSender->sendPageDiff( $subscriber_mail, 'subscr_digest', $id, $rev ); } /** * Send a list mail * * Sends a list mail showing a list of changed pages. * * @param string $subscriber_mail The target mail address * @param array $ids Array of ids * @param string $ns_id The id of the namespace * * @return bool true if a mail was sent * @author Adrian Lang * */ protected function sendList($subscriber_mail, $ids, $ns_id) { if ($ids === []) { return false; } $tlist = ''; $hlist = '
    '; foreach ($ids as $id) { $link = wl($id, [], true); $tlist .= '* ' . $link . NL; $hlist .= '
  • ' . hsc($id) . '
  • ' . NL; } $hlist .= '
'; $id = prettyprint_id($ns_id); $trep = [ 'DIFF' => rtrim($tlist), 'PAGE' => $id, 'SUBSCRIBE' => wl($id, ['do' => 'subscribe'], true, '&'), ]; $hrep = [ 'DIFF' => $hlist, ]; return $this->send( $subscriber_mail, 'subscribe_list', $ns_id, 'subscr_list', $trep, $hrep ); } }