123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- <?php
- namespace dokuwiki\Subscriptions;
- use dokuwiki\ChangeLog\PageChangeLog;
- use dokuwiki\Input\Input;
- use DokuWiki_Auth_Plugin;
- class BulkSubscriptionSender extends SubscriptionSender
- {
- /**
- * Send digest and list subscriptions
- *
- * This sends mails to all subscribers that have a subscription for namespaces above
- * the given page if the needed $conf['subscribe_time'] has passed already.
- *
- * This function is called form lib/exe/indexer.php
- *
- * @param string $page
- *
- * @return int number of sent mails
- */
- public function sendBulk($page)
- {
- $subscriberManager = new SubscriberManager();
- if (!$subscriberManager->isenabled()) {
- return 0;
- }
- /** @var DokuWiki_Auth_Plugin $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) {
- list($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 (substr($target, -1, 1) === ':') {
- // 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 = (count($revisions) > 0) ? $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++;
- }
- } else {
- if ($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 <lang@cosmocode.de>
- */
- 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 <lang@cosmocode.de>
- */
- 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 <lang@cosmocode.de>
- *
- */
- protected function sendDigest($subscriber_mail, $id, $lastupdate)
- {
- $pagelog = new PageChangeLog($id);
- $n = 0;
- do {
- $rev = $pagelog->getRevisions($n++, 1);
- $rev = (count($rev) > 0) ? $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 <lang@cosmocode.de>
- *
- */
- protected function sendList($subscriber_mail, $ids, $ns_id)
- {
- if (count($ids) === 0) {
- return false;
- }
- $tlist = '';
- $hlist = '<ul>';
- foreach ($ids as $id) {
- $link = wl($id, [], true);
- $tlist .= '* ' . $link . NL;
- $hlist .= '<li><a href="' . $link . '">' . hsc($id) . '</a></li>' . NL;
- }
- $hlist .= '</ul>';
- $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
- );
- }
- }
|