123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- <?php
- namespace dokuwiki\Subscriptions;
- use dokuwiki\Input\Input;
- use DokuWiki_Auth_Plugin;
- use Exception;
- class SubscriberManager
- {
- /**
- * Check if subscription system is enabled
- *
- * @return bool
- */
- public function isenabled()
- {
- return actionOK('subscribe');
- }
- /**
- * Adds a new subscription for the given page or namespace
- *
- * This will automatically overwrite any existent subscription for the given user on this
- * *exact* page or namespace. It will *not* modify any subscription that may exist in higher namespaces.
- *
- * @throws Exception when user or style is empty
- *
- * @param string $id The target page or namespace, specified by id; Namespaces
- * are identified by appending a colon.
- * @param string $user
- * @param string $style
- * @param string $data
- *
- * @return bool
- */
- public function add($id, $user, $style, $data = '')
- {
- if (!$this->isenabled()) {
- return false;
- }
- // delete any existing subscription
- $this->remove($id, $user);
- $user = auth_nameencode(trim($user));
- $style = trim($style);
- $data = trim($data);
- if (!$user) {
- throw new Exception('no subscription user given');
- }
- if (!$style) {
- throw new Exception('no subscription style given');
- }
- if (!$data) {
- $data = time();
- } //always add current time for new subscriptions
- $line = "$user $style $data\n";
- $file = $this->file($id);
- return io_saveFile($file, $line, true);
- }
- /**
- * Removes a subscription for the given page or namespace
- *
- * This removes all subscriptions matching the given criteria on the given page or
- * namespace. It will *not* modify any subscriptions that may exist in higher
- * namespaces.
- *
- * @param string $id The target object’s (namespace or page) id
- * @param string|array $user
- * @param string|array $style
- * @param string|array $data
- *
- * @return bool
- */
- public function remove($id, $user = null, $style = null, $data = null)
- {
- if (!$this->isenabled()) {
- return false;
- }
- $file = $this->file($id);
- if (!file_exists($file)) {
- return true;
- }
- $regexBuilder = new SubscriberRegexBuilder();
- $re = $regexBuilder->buildRegex($user, $style, $data);
- return io_deleteFromFile($file, $re, true);
- }
- /**
- * Get data for $INFO['subscribed']
- *
- * $INFO['subscribed'] is either false if no subscription for the current page
- * and user is in effect. Else it contains an array of arrays with the fields
- * “target”, “style”, and optionally “data”.
- *
- * @author Adrian Lang <lang@cosmocode.de>
- *
- * @param string $id Page ID, defaults to global $ID
- * @param string $user User, defaults to $_SERVER['REMOTE_USER']
- *
- * @return array|false
- */
- public function userSubscription($id = '', $user = '')
- {
- if (!$this->isenabled()) {
- return false;
- }
- global $ID;
- /** @var Input $INPUT */
- global $INPUT;
- if (!$id) {
- $id = $ID;
- }
- if (!$user) {
- $user = $INPUT->server->str('REMOTE_USER');
- }
- if (empty($user)) {
- // not logged in
- return false;
- }
- $subs = $this->subscribers($id, $user);
- if (!count($subs)) {
- return false;
- }
- $result = [];
- foreach ($subs as $target => $info) {
- $result[] = [
- 'target' => $target,
- 'style' => $info[$user][0],
- 'data' => $info[$user][1],
- ];
- }
- return $result;
- }
- /**
- * Recursively search for matching subscriptions
- *
- * This function searches all relevant subscription files for a page or
- * namespace.
- *
- * @author Adrian Lang <lang@cosmocode.de>
- *
- * @param string $page The target object’s (namespace or page) id
- * @param string|array $user
- * @param string|array $style
- * @param string|array $data
- *
- * @return array
- */
- public function subscribers($page, $user = null, $style = null, $data = null)
- {
- if (!$this->isenabled()) {
- return [];
- }
- // Construct list of files which may contain relevant subscriptions.
- $files = [':' => $this->file(':')];
- do {
- $files[$page] = $this->file($page);
- $page = getNS(rtrim($page, ':')) . ':';
- } while ($page !== ':');
- $regexBuilder = new SubscriberRegexBuilder();
- $re = $regexBuilder->buildRegex($user, $style, $data);
- // Handle files.
- $result = [];
- foreach ($files as $target => $file) {
- if (!file_exists($file)) {
- continue;
- }
- $lines = file($file);
- foreach ($lines as $line) {
- // fix old style subscription files
- if (strpos($line, ' ') === false) {
- $line = trim($line) . " every\n";
- }
- // check for matching entries
- if (!preg_match($re, $line, $m)) {
- continue;
- }
- // if no last sent is set, use 0
- if (!isset($m[3])) {
- $m[3] = 0;
- }
- $u = rawurldecode($m[1]); // decode the user name
- if (!isset($result[$target])) {
- $result[$target] = [];
- }
- $result[$target][$u] = [$m[2], $m[3]]; // add to result
- }
- }
- return array_reverse($result);
- }
- /**
- * Default callback for COMMON_NOTIFY_ADDRESSLIST
- *
- * Aggregates all email addresses of user who have subscribed the given page with 'every' style
- *
- * @author Adrian Lang <lang@cosmocode.de>
- * @author Steven Danz <steven-danz@kc.rr.com>
- *
- * @todo move the whole functionality into this class, trigger SUBSCRIPTION_NOTIFY_ADDRESSLIST instead,
- * use an array for the addresses within it
- *
- * @param array &$data Containing the entries:
- * - $id (the page id),
- * - $self (whether the author should be notified,
- * - $addresslist (current email address list)
- * - $replacements (array of additional string substitutions, @KEY@ to be replaced by value)
- */
- public function notifyAddresses(&$data)
- {
- if (!$this->isenabled()) {
- return;
- }
- /** @var DokuWiki_Auth_Plugin $auth */
- global $auth;
- global $conf;
- /** @var \Input $INPUT */
- global $INPUT;
- $id = $data['id'];
- $self = $data['self'];
- $addresslist = $data['addresslist'];
- $subscriptions = $this->subscribers($id, null, 'every');
- $result = [];
- foreach ($subscriptions as $target => $users) {
- foreach ($users as $user => $info) {
- $userinfo = $auth->getUserData($user);
- if ($userinfo === false) {
- continue;
- }
- if (!$userinfo['mail']) {
- continue;
- }
- if (!$self && $user == $INPUT->server->str('REMOTE_USER')) {
- continue;
- } //skip our own changes
- $level = auth_aclcheck($id, $user, $userinfo['grps']);
- if ($level >= AUTH_READ) {
- if (strcasecmp($userinfo['mail'], $conf['notify']) != 0) { //skip user who get notified elsewhere
- $result[$user] = $userinfo['mail'];
- }
- }
- }
- }
- $data['addresslist'] = trim($addresslist . ',' . implode(',', $result), ',');
- }
- /**
- * Return the subscription meta file for the given ID
- *
- * @author Adrian Lang <lang@cosmocode.de>
- *
- * @param string $id The target page or namespace, specified by id; Namespaces
- * are identified by appending a colon.
- *
- * @return string
- */
- protected function file($id)
- {
- $meta_fname = '.mlist';
- if ((substr($id, -1, 1) === ':')) {
- $meta_froot = getNS($id);
- $meta_fname = '/' . $meta_fname;
- } else {
- $meta_froot = $id;
- }
- return metaFN((string)$meta_froot, $meta_fname);
- }
- }
|