[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

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

   1  <?php
   2  
   3  namespace dokuwiki\Subscriptions;
   4  
   5  use dokuwiki\Input\Input;
   6  use DokuWiki_Auth_Plugin;
   7  use Exception;
   8  
   9  class SubscriberManager
  10  {
  11  
  12      /**
  13       * Check if subscription system is enabled
  14       *
  15       * @return bool
  16       */
  17      public function isenabled()
  18      {
  19          return actionOK('subscribe');
  20      }
  21  
  22      /**
  23       * Adds a new subscription for the given page or namespace
  24       *
  25       * This will automatically overwrite any existent subscription for the given user on this
  26       * *exact* page or namespace. It will *not* modify any subscription that may exist in higher namespaces.
  27       *
  28       * @throws Exception when user or style is empty
  29       *
  30       * @param string $id The target page or namespace, specified by id; Namespaces
  31       *                   are identified by appending a colon.
  32       * @param string $user
  33       * @param string $style
  34       * @param string $data
  35       *
  36       * @return bool
  37       */
  38      public function add($id, $user, $style, $data = '')
  39      {
  40          if (!$this->isenabled()) {
  41              return false;
  42          }
  43  
  44          // delete any existing subscription
  45          $this->remove($id, $user);
  46  
  47          $user = auth_nameencode(trim($user));
  48          $style = trim($style);
  49          $data = trim($data);
  50  
  51          if (!$user) {
  52              throw new Exception('no subscription user given');
  53          }
  54          if (!$style) {
  55              throw new Exception('no subscription style given');
  56          }
  57          if (!$data) {
  58              $data = time();
  59          } //always add current time for new subscriptions
  60  
  61          $line = "$user $style $data\n";
  62          $file = $this->file($id);
  63          return io_saveFile($file, $line, true);
  64      }
  65  
  66  
  67      /**
  68       * Removes a subscription for the given page or namespace
  69       *
  70       * This removes all subscriptions matching the given criteria on the given page or
  71       * namespace. It will *not* modify any subscriptions that may exist in higher
  72       * namespaces.
  73       *
  74       * @param string       $id The target object’s (namespace or page) id
  75       * @param string|array $user
  76       * @param string|array $style
  77       * @param string|array $data
  78       *
  79       * @return bool
  80       */
  81      public function remove($id, $user = null, $style = null, $data = null)
  82      {
  83          if (!$this->isenabled()) {
  84              return false;
  85          }
  86  
  87          $file = $this->file($id);
  88          if (!file_exists($file)) {
  89              return true;
  90          }
  91  
  92          $regexBuilder = new SubscriberRegexBuilder();
  93          $re = $regexBuilder->buildRegex($user, $style, $data);
  94          return io_deleteFromFile($file, $re, true);
  95      }
  96  
  97      /**
  98       * Get data for $INFO['subscribed']
  99       *
 100       * $INFO['subscribed'] is either false if no subscription for the current page
 101       * and user is in effect. Else it contains an array of arrays with the fields
 102       * “target”, “style”, and optionally “data”.
 103       *
 104       * @author Adrian Lang <lang@cosmocode.de>
 105       *
 106       * @param string $id   Page ID, defaults to global $ID
 107       * @param string $user User, defaults to $_SERVER['REMOTE_USER']
 108       *
 109       * @return array|false
 110       */
 111      public function userSubscription($id = '', $user = '')
 112      {
 113          if (!$this->isenabled()) {
 114              return false;
 115          }
 116  
 117          global $ID;
 118          /** @var Input $INPUT */
 119          global $INPUT;
 120          if (!$id) {
 121              $id = $ID;
 122          }
 123          if (!$user) {
 124              $user = $INPUT->server->str('REMOTE_USER');
 125          }
 126  
 127          if (empty($user)) {
 128              // not logged in
 129              return false;
 130          }
 131  
 132          $subs = $this->subscribers($id, $user);
 133          if (!count($subs)) {
 134              return false;
 135          }
 136  
 137          $result = [];
 138          foreach ($subs as $target => $info) {
 139              $result[] = [
 140                  'target' => $target,
 141                  'style' => $info[$user][0],
 142                  'data' => $info[$user][1],
 143              ];
 144          }
 145  
 146          return $result;
 147      }
 148  
 149      /**
 150       * Recursively search for matching subscriptions
 151       *
 152       * This function searches all relevant subscription files for a page or
 153       * namespace.
 154       *
 155       * @author Adrian Lang <lang@cosmocode.de>
 156       *
 157       * @param string       $page The target object’s (namespace or page) id
 158       * @param string|array $user
 159       * @param string|array $style
 160       * @param string|array $data
 161       *
 162       * @return array
 163       */
 164      public function subscribers($page, $user = null, $style = null, $data = null)
 165      {
 166          if (!$this->isenabled()) {
 167              return [];
 168          }
 169  
 170          // Construct list of files which may contain relevant subscriptions.
 171          $files = [':' => $this->file(':')];
 172          do {
 173              $files[$page] = $this->file($page);
 174              $page = getNS(rtrim($page, ':')) . ':';
 175          } while ($page !== ':');
 176  
 177          $regexBuilder = new SubscriberRegexBuilder();
 178          $re = $regexBuilder->buildRegex($user, $style, $data);
 179  
 180          // Handle files.
 181          $result = [];
 182          foreach ($files as $target => $file) {
 183              if (!file_exists($file)) {
 184                  continue;
 185              }
 186  
 187              $lines = file($file);
 188              foreach ($lines as $line) {
 189                  // fix old style subscription files
 190                  if (strpos($line, ' ') === false) {
 191                      $line = trim($line) . " every\n";
 192                  }
 193  
 194                  // check for matching entries
 195                  if (!preg_match($re, $line, $m)) {
 196                      continue;
 197                  }
 198  
 199                  // if no last sent is set, use 0
 200                  if (!isset($m[3])) {
 201                      $m[3] = 0;
 202                  }
 203  
 204                  $u = rawurldecode($m[1]); // decode the user name
 205                  if (!isset($result[$target])) {
 206                      $result[$target] = [];
 207                  }
 208                  $result[$target][$u] = [$m[2], $m[3]]; // add to result
 209              }
 210          }
 211          return array_reverse($result);
 212      }
 213  
 214      /**
 215       * Default callback for COMMON_NOTIFY_ADDRESSLIST
 216       *
 217       * Aggregates all email addresses of user who have subscribed the given page with 'every' style
 218       *
 219       * @author Adrian Lang <lang@cosmocode.de>
 220       * @author Steven Danz <steven-danz@kc.rr.com>
 221       *
 222       * @todo   move the whole functionality into this class, trigger SUBSCRIPTION_NOTIFY_ADDRESSLIST instead,
 223       *         use an array for the addresses within it
 224       *
 225       * @param array &$data Containing the entries:
 226       *                     - $id (the page id),
 227       *                     - $self (whether the author should be notified,
 228       *                     - $addresslist (current email address list)
 229       *                     - $replacements (array of additional string substitutions, @KEY@ to be replaced by value)
 230       */
 231      public function notifyAddresses(&$data)
 232      {
 233          if (!$this->isenabled()) {
 234              return;
 235          }
 236  
 237          /** @var DokuWiki_Auth_Plugin $auth */
 238          global $auth;
 239          global $conf;
 240          /** @var \Input $INPUT */
 241          global $INPUT;
 242  
 243          $id = $data['id'];
 244          $self = $data['self'];
 245          $addresslist = $data['addresslist'];
 246  
 247          $subscriptions = $this->subscribers($id, null, 'every');
 248  
 249          $result = [];
 250          foreach ($subscriptions as $target => $users) {
 251              foreach ($users as $user => $info) {
 252                  $userinfo = $auth->getUserData($user);
 253                  if ($userinfo === false) {
 254                      continue;
 255                  }
 256                  if (!$userinfo['mail']) {
 257                      continue;
 258                  }
 259                  if (!$self && $user == $INPUT->server->str('REMOTE_USER')) {
 260                      continue;
 261                  } //skip our own changes
 262  
 263                  $level = auth_aclcheck($id, $user, $userinfo['grps']);
 264                  if ($level >= AUTH_READ) {
 265                      if (strcasecmp($userinfo['mail'], $conf['notify']) != 0) { //skip user who get notified elsewhere
 266                          $result[$user] = $userinfo['mail'];
 267                      }
 268                  }
 269              }
 270          }
 271          $data['addresslist'] = trim($addresslist . ',' . implode(',', $result), ',');
 272      }
 273  
 274      /**
 275       * Return the subscription meta file for the given ID
 276       *
 277       * @author Adrian Lang <lang@cosmocode.de>
 278       *
 279       * @param string $id The target page or namespace, specified by id; Namespaces
 280       *                   are identified by appending a colon.
 281       *
 282       * @return string
 283       */
 284      protected function file($id)
 285      {
 286          $meta_fname = '.mlist';
 287          if ((substr($id, -1, 1) === ':')) {
 288              $meta_froot = getNS($id);
 289              $meta_fname = '/' . $meta_fname;
 290          } else {
 291              $meta_froot = $id;
 292          }
 293          return metaFN((string)$meta_froot, $meta_fname);
 294      }
 295  }