[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

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

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