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