[ 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                      ) {
  92                          $pagelog = new PageChangeLog($rev['id']);
  93                          $rev = $pagelog->getRevisions($n++, 1);
  94                          $rev = (count($rev) > 0) ? $rev[0] : null;
  95                      }
  96  
  97                      if (!is_null($rev) && $rev['date'] >= $lastupdate) {
  98                          // Some change was not a minor one and not by myself
  99                          $change_ids[] = $rev['id'];
 100                      }
 101                  }
 102  
 103                  // send it
 104                  if ($style === 'digest') {
 105                      foreach ($change_ids as $change_id) {
 106                          $this->sendDigest(
 107                              $USERINFO['mail'],
 108                              $change_id,
 109                              $lastupdate
 110                          );
 111                          $count++;
 112                      }
 113                  } else {
 114                      if ($style === 'list') {
 115                          $this->sendList($USERINFO['mail'], $change_ids, $target);
 116                          $count++;
 117                      }
 118                  }
 119                  // TODO: Handle duplicate subscriptions.
 120  
 121                  // Update notification time.
 122                  $subscriberManager->add($target, $user, $style, time());
 123              }
 124              $this->unlock($target);
 125          }
 126  
 127          // restore current user info
 128          $USERINFO = $olduinfo;
 129          $INPUT->server->set('REMOTE_USER', $olduser);
 130          return $count;
 131      }
 132  
 133      /**
 134       * Lock subscription info
 135       *
 136       * We don't use io_lock() her because we do not wait for the lock and use a larger stale time
 137       *
 138       * @param string $id The target page or namespace, specified by id; Namespaces
 139       *                   are identified by appending a colon.
 140       *
 141       * @return bool true, if you got a succesful lock
 142       * @author Adrian Lang <lang@cosmocode.de>
 143       */
 144      protected function lock($id)
 145      {
 146          global $conf;
 147  
 148          $lock = $conf['lockdir'] . '/_subscr_' . md5($id) . '.lock';
 149  
 150          if (is_dir($lock) && time() - @filemtime($lock) > 60 * 5) {
 151              // looks like a stale lock - remove it
 152              @rmdir($lock);
 153          }
 154  
 155          // try creating the lock directory
 156          if (!@mkdir($lock)) {
 157              return false;
 158          }
 159  
 160          if ($conf['dperm']) {
 161              chmod($lock, $conf['dperm']);
 162          }
 163          return true;
 164      }
 165  
 166      /**
 167       * Unlock subscription info
 168       *
 169       * @param string $id The target page or namespace, specified by id; Namespaces
 170       *                   are identified by appending a colon.
 171       *
 172       * @return bool
 173       * @author Adrian Lang <lang@cosmocode.de>
 174       */
 175      protected function unlock($id)
 176      {
 177          global $conf;
 178          $lock = $conf['lockdir'] . '/_subscr_' . md5($id) . '.lock';
 179          return @rmdir($lock);
 180      }
 181  
 182      /**
 183       * Send a digest mail
 184       *
 185       * Sends a digest mail showing a bunch of changes of a single page. Basically the same as sendPageDiff()
 186       * but determines the last known revision first
 187       *
 188       * @param string $subscriber_mail The target mail address
 189       * @param string $id              The ID
 190       * @param int    $lastupdate      Time of the last notification
 191       *
 192       * @return bool
 193       * @author Adrian Lang <lang@cosmocode.de>
 194       *
 195       */
 196      protected function sendDigest($subscriber_mail, $id, $lastupdate)
 197      {
 198          $pagelog = new PageChangeLog($id);
 199          $n = 0;
 200          do {
 201              $rev = $pagelog->getRevisions($n++, 1);
 202              $rev = (count($rev) > 0) ? $rev[0] : null;
 203          } while (!is_null($rev) && $rev > $lastupdate);
 204  
 205          // TODO I'm not happy with the following line and passing $this->mailer around. Not sure how to solve it better
 206          $pageSubSender = new PageSubscriptionSender($this->mailer);
 207          return $pageSubSender->sendPageDiff(
 208              $subscriber_mail,
 209              'subscr_digest',
 210              $id,
 211              $rev
 212          );
 213      }
 214  
 215      /**
 216       * Send a list mail
 217       *
 218       * Sends a list mail showing a list of changed pages.
 219       *
 220       * @param string $subscriber_mail The target mail address
 221       * @param array  $ids             Array of ids
 222       * @param string $ns_id           The id of the namespace
 223       *
 224       * @return bool true if a mail was sent
 225       * @author Adrian Lang <lang@cosmocode.de>
 226       *
 227       */
 228      protected function sendList($subscriber_mail, $ids, $ns_id)
 229      {
 230          if (count($ids) === 0) {
 231              return false;
 232          }
 233  
 234          $tlist = '';
 235          $hlist = '<ul>';
 236          foreach ($ids as $id) {
 237              $link = wl($id, [], true);
 238              $tlist .= '* ' . $link . NL;
 239              $hlist .= '<li><a href="' . $link . '">' . hsc($id) . '</a></li>' . NL;
 240          }
 241          $hlist .= '</ul>';
 242  
 243          $id = prettyprint_id($ns_id);
 244          $trep = [
 245              'DIFF' => rtrim($tlist),
 246              'PAGE' => $id,
 247              'SUBSCRIBE' => wl($id, ['do' => 'subscribe'], true, '&'),
 248          ];
 249          $hrep = [
 250              'DIFF' => $hlist,
 251          ];
 252  
 253          return $this->send(
 254              $subscriber_mail,
 255              'subscribe_list',
 256              $ns_id,
 257              'subscr_list',
 258              $trep,
 259              $hrep
 260          );
 261      }
 262  }