[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/Subscriptions/ -> BulkSubscriptionSender.php (source)

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