1479c05b1SMichael Große<?php 2479c05b1SMichael Große 3479c05b1SMichael Großenamespace dokuwiki\Subscriptions; 4479c05b1SMichael Große 5*51ee2399SGerrit Uitslaguse dokuwiki\Extension\AuthPlugin; 6479c05b1SMichael Großeuse dokuwiki\Input\Input; 7479c05b1SMichael Großeuse Exception; 8479c05b1SMichael Große 9479c05b1SMichael Großeclass SubscriberManager 10479c05b1SMichael Große{ 11479c05b1SMichael Große 12479c05b1SMichael Große /** 13479c05b1SMichael Große * Check if subscription system is enabled 14479c05b1SMichael Große * 15479c05b1SMichael Große * @return bool 16479c05b1SMichael Große */ 179c22b77cSMichael Große public function isenabled() 189c22b77cSMichael Große { 19479c05b1SMichael Große return actionOK('subscribe'); 20479c05b1SMichael Große } 21479c05b1SMichael Große 22479c05b1SMichael Große /** 23479c05b1SMichael Große * Adds a new subscription for the given page or namespace 24479c05b1SMichael Große * 25479c05b1SMichael Große * This will automatically overwrite any existent subscription for the given user on this 26479c05b1SMichael Große * *exact* page or namespace. It will *not* modify any subscription that may exist in higher namespaces. 27479c05b1SMichael Große * 289c22b77cSMichael Große * @throws Exception when user or style is empty 299c22b77cSMichael Große * 30479c05b1SMichael Große * @param string $id The target page or namespace, specified by id; Namespaces 31479c05b1SMichael Große * are identified by appending a colon. 32479c05b1SMichael Große * @param string $user 33479c05b1SMichael Große * @param string $style 34479c05b1SMichael Große * @param string $data 359c22b77cSMichael Große * 36479c05b1SMichael Große * @return bool 37479c05b1SMichael Große */ 389c22b77cSMichael Große public function add($id, $user, $style, $data = '') 399c22b77cSMichael Große { 409c22b77cSMichael Große if (!$this->isenabled()) { 419c22b77cSMichael Große return false; 429c22b77cSMichael Große } 43479c05b1SMichael Große 44479c05b1SMichael Große // delete any existing subscription 45479c05b1SMichael Große $this->remove($id, $user); 46479c05b1SMichael Große 47479c05b1SMichael Große $user = auth_nameencode(trim($user)); 48479c05b1SMichael Große $style = trim($style); 49479c05b1SMichael Große $data = trim($data); 50479c05b1SMichael Große 519c22b77cSMichael Große if (!$user) { 529c22b77cSMichael Große throw new Exception('no subscription user given'); 539c22b77cSMichael Große } 549c22b77cSMichael Große if (!$style) { 559c22b77cSMichael Große throw new Exception('no subscription style given'); 569c22b77cSMichael Große } 579c22b77cSMichael Große if (!$data) { 589c22b77cSMichael Große $data = time(); 599c22b77cSMichael Große } //always add current time for new subscriptions 60479c05b1SMichael Große 61479c05b1SMichael Große $line = "$user $style $data\n"; 62479c05b1SMichael Große $file = $this->file($id); 63479c05b1SMichael Große return io_saveFile($file, $line, true); 64479c05b1SMichael Große } 65479c05b1SMichael Große 66479c05b1SMichael Große 67479c05b1SMichael Große /** 68479c05b1SMichael Große * Removes a subscription for the given page or namespace 69479c05b1SMichael Große * 70479c05b1SMichael Große * This removes all subscriptions matching the given criteria on the given page or 71479c05b1SMichael Große * namespace. It will *not* modify any subscriptions that may exist in higher 72479c05b1SMichael Große * namespaces. 73479c05b1SMichael Große * 74479c05b1SMichael Große * @param string $id The target object’s (namespace or page) id 75479c05b1SMichael Große * @param string|array $user 76479c05b1SMichael Große * @param string|array $style 77479c05b1SMichael Große * @param string|array $data 789c22b77cSMichael Große * 79479c05b1SMichael Große * @return bool 80*51ee2399SGerrit Uitslag * @throws Exception 81479c05b1SMichael Große */ 829c22b77cSMichael Große public function remove($id, $user = null, $style = null, $data = null) 839c22b77cSMichael Große { 849c22b77cSMichael Große if (!$this->isenabled()) { 859c22b77cSMichael Große return false; 869c22b77cSMichael Große } 87479c05b1SMichael Große 88479c05b1SMichael Große $file = $this->file($id); 899c22b77cSMichael Große if (!file_exists($file)) { 909c22b77cSMichael Große return true; 919c22b77cSMichael Große } 92479c05b1SMichael Große 93479c05b1SMichael Große $regexBuilder = new SubscriberRegexBuilder(); 94479c05b1SMichael Große $re = $regexBuilder->buildRegex($user, $style, $data); 95479c05b1SMichael Große return io_deleteFromFile($file, $re, true); 96479c05b1SMichael Große } 97479c05b1SMichael Große 98479c05b1SMichael Große /** 99479c05b1SMichael Große * Get data for $INFO['subscribed'] 100479c05b1SMichael Große * 101479c05b1SMichael Große * $INFO['subscribed'] is either false if no subscription for the current page 102479c05b1SMichael Große * and user is in effect. Else it contains an array of arrays with the fields 103479c05b1SMichael Große * “target”, “style”, and optionally “data”. 104479c05b1SMichael Große * 105479c05b1SMichael Große * @param string $id Page ID, defaults to global $ID 106479c05b1SMichael Große * @param string $user User, defaults to $_SERVER['REMOTE_USER'] 1079c22b77cSMichael Große * 108479c05b1SMichael Große * @return array|false 109*51ee2399SGerrit Uitslag * @throws Exception 110*51ee2399SGerrit Uitslag * 111*51ee2399SGerrit Uitslag * @author Adrian Lang <lang@cosmocode.de> 112479c05b1SMichael Große */ 1139c22b77cSMichael Große public function userSubscription($id = '', $user = '') 1149c22b77cSMichael Große { 1159c22b77cSMichael Große if (!$this->isenabled()) { 1169c22b77cSMichael Große return false; 1179c22b77cSMichael Große } 118479c05b1SMichael Große 119479c05b1SMichael Große global $ID; 120479c05b1SMichael Große /** @var Input $INPUT */ 121479c05b1SMichael Große global $INPUT; 1229c22b77cSMichael Große if (!$id) { 1239c22b77cSMichael Große $id = $ID; 1249c22b77cSMichael Große } 1259c22b77cSMichael Große if (!$user) { 1269c22b77cSMichael Große $user = $INPUT->server->str('REMOTE_USER'); 1279c22b77cSMichael Große } 128479c05b1SMichael Große 1299efb6f4aSPhy if (empty($user)) { 1309efb6f4aSPhy // not logged in 1319efb6f4aSPhy return false; 1329efb6f4aSPhy } 1339efb6f4aSPhy 134479c05b1SMichael Große $subs = $this->subscribers($id, $user); 1355983e241SAndreas Gohr if ($subs === []) { 1369c22b77cSMichael Große return false; 1379c22b77cSMichael Große } 138479c05b1SMichael Große 1399c22b77cSMichael Große $result = []; 140479c05b1SMichael Große foreach ($subs as $target => $info) { 1419c22b77cSMichael Große $result[] = [ 142479c05b1SMichael Große 'target' => $target, 143479c05b1SMichael Große 'style' => $info[$user][0], 1449c22b77cSMichael Große 'data' => $info[$user][1], 1459c22b77cSMichael Große ]; 146479c05b1SMichael Große } 147479c05b1SMichael Große 148479c05b1SMichael Große return $result; 149479c05b1SMichael Große } 150479c05b1SMichael Große 151479c05b1SMichael Große /** 152479c05b1SMichael Große * Recursively search for matching subscriptions 153479c05b1SMichael Große * 154479c05b1SMichael Große * This function searches all relevant subscription files for a page or 155479c05b1SMichael Große * namespace. 156479c05b1SMichael Große * 157479c05b1SMichael Große * @param string $page The target object’s (namespace or page) id 158479c05b1SMichael Große * @param string|array $user 159479c05b1SMichael Große * @param string|array $style 160479c05b1SMichael Große * @param string|array $data 1619c22b77cSMichael Große * 162479c05b1SMichael Große * @return array 163*51ee2399SGerrit Uitslag * @throws Exception 164*51ee2399SGerrit Uitslag * 165*51ee2399SGerrit Uitslag * @author Adrian Lang <lang@cosmocode.de> 166*51ee2399SGerrit Uitslag * 167479c05b1SMichael Große */ 1689c22b77cSMichael Große public function subscribers($page, $user = null, $style = null, $data = null) 1699c22b77cSMichael Große { 1709c22b77cSMichael Große if (!$this->isenabled()) { 1719c22b77cSMichael Große return []; 1729c22b77cSMichael Große } 173479c05b1SMichael Große 174479c05b1SMichael Große // Construct list of files which may contain relevant subscriptions. 1759c22b77cSMichael Große $files = [':' => $this->file(':')]; 176479c05b1SMichael Große do { 177479c05b1SMichael Große $files[$page] = $this->file($page); 178479c05b1SMichael Große $page = getNS(rtrim($page, ':')) . ':'; 179479c05b1SMichael Große } while ($page !== ':'); 180479c05b1SMichael Große 181479c05b1SMichael Große $regexBuilder = new SubscriberRegexBuilder(); 182479c05b1SMichael Große $re = $regexBuilder->buildRegex($user, $style, $data); 183479c05b1SMichael Große 184479c05b1SMichael Große // Handle files. 1859c22b77cSMichael Große $result = []; 186479c05b1SMichael Große foreach ($files as $target => $file) { 1879c22b77cSMichael Große if (!file_exists($file)) { 1889c22b77cSMichael Große continue; 1899c22b77cSMichael Große } 190479c05b1SMichael Große 191479c05b1SMichael Große $lines = file($file); 192479c05b1SMichael Große foreach ($lines as $line) { 193479c05b1SMichael Große // fix old style subscription files 1949c22b77cSMichael Große if (strpos($line, ' ') === false) { 1959c22b77cSMichael Große $line = trim($line) . " every\n"; 1969c22b77cSMichael Große } 197479c05b1SMichael Große 198479c05b1SMichael Große // check for matching entries 1999c22b77cSMichael Große if (!preg_match($re, $line, $m)) { 2009c22b77cSMichael Große continue; 2019c22b77cSMichael Große } 202479c05b1SMichael Große 203062cf88bSAndreas Gohr // if no last sent is set, use 0 204062cf88bSAndreas Gohr if (!isset($m[3])) { 205062cf88bSAndreas Gohr $m[3] = 0; 206062cf88bSAndreas Gohr } 207062cf88bSAndreas Gohr 208479c05b1SMichael Große $u = rawurldecode($m[1]); // decode the user name 2099c22b77cSMichael Große if (!isset($result[$target])) { 2109c22b77cSMichael Große $result[$target] = []; 2119c22b77cSMichael Große } 2129c22b77cSMichael Große $result[$target][$u] = [$m[2], $m[3]]; // add to result 213479c05b1SMichael Große } 214479c05b1SMichael Große } 215479c05b1SMichael Große return array_reverse($result); 216479c05b1SMichael Große } 217479c05b1SMichael Große 218479c05b1SMichael Große /** 219479c05b1SMichael Große * Default callback for COMMON_NOTIFY_ADDRESSLIST 220479c05b1SMichael Große * 221479c05b1SMichael Große * Aggregates all email addresses of user who have subscribed the given page with 'every' style 222479c05b1SMichael Große * 223479c05b1SMichael Große * @param array &$data Containing the entries: 224479c05b1SMichael Große * - $id (the page id), 225479c05b1SMichael Große * - $self (whether the author should be notified, 226479c05b1SMichael Große * - $addresslist (current email address list) 227479c05b1SMichael Große * - $replacements (array of additional string substitutions, @KEY@ to be replaced by value) 228*51ee2399SGerrit Uitslag * @throws Exception 229*51ee2399SGerrit Uitslag * 230*51ee2399SGerrit Uitslag * @author Adrian Lang <lang@cosmocode.de> 231*51ee2399SGerrit Uitslag * @author Steven Danz <steven-danz@kc.rr.com> 232*51ee2399SGerrit Uitslag * 233*51ee2399SGerrit Uitslag * @todo move the whole functionality into this class, trigger SUBSCRIPTION_NOTIFY_ADDRESSLIST instead, 234*51ee2399SGerrit Uitslag * use an array for the addresses within it 235479c05b1SMichael Große */ 2369c22b77cSMichael Große public function notifyAddresses(&$data) 2379c22b77cSMichael Große { 2389c22b77cSMichael Große if (!$this->isenabled()) { 2399c22b77cSMichael Große return; 2409c22b77cSMichael Große } 241479c05b1SMichael Große 242*51ee2399SGerrit Uitslag /** @var AuthPlugin $auth */ 243479c05b1SMichael Große global $auth; 244479c05b1SMichael Große global $conf; 245479c05b1SMichael Große /** @var \Input $INPUT */ 246479c05b1SMichael Große global $INPUT; 247479c05b1SMichael Große 248479c05b1SMichael Große $id = $data['id']; 249479c05b1SMichael Große $self = $data['self']; 250479c05b1SMichael Große $addresslist = $data['addresslist']; 251479c05b1SMichael Große 252479c05b1SMichael Große $subscriptions = $this->subscribers($id, null, 'every'); 253479c05b1SMichael Große 2549c22b77cSMichael Große $result = []; 2555983e241SAndreas Gohr foreach ($subscriptions as $users) { 256479c05b1SMichael Große foreach ($users as $user => $info) { 257479c05b1SMichael Große $userinfo = $auth->getUserData($user); 2589c22b77cSMichael Große if ($userinfo === false) { 2599c22b77cSMichael Große continue; 2609c22b77cSMichael Große } 2619c22b77cSMichael Große if (!$userinfo['mail']) { 2629c22b77cSMichael Große continue; 2639c22b77cSMichael Große } 2649c22b77cSMichael Große if (!$self && $user == $INPUT->server->str('REMOTE_USER')) { 2659c22b77cSMichael Große continue; 2669c22b77cSMichael Große } //skip our own changes 267479c05b1SMichael Große 268479c05b1SMichael Große $level = auth_aclcheck($id, $user, $userinfo['grps']); 269479c05b1SMichael Große if ($level >= AUTH_READ) { 270479c05b1SMichael Große if (strcasecmp($userinfo['mail'], $conf['notify']) != 0) { //skip user who get notified elsewhere 271479c05b1SMichael Große $result[$user] = $userinfo['mail']; 272479c05b1SMichael Große } 273479c05b1SMichael Große } 274479c05b1SMichael Große } 275479c05b1SMichael Große } 276479c05b1SMichael Große $data['addresslist'] = trim($addresslist . ',' . implode(',', $result), ','); 277479c05b1SMichael Große } 278479c05b1SMichael Große 279479c05b1SMichael Große /** 280479c05b1SMichael Große * Return the subscription meta file for the given ID 281479c05b1SMichael Große * 282479c05b1SMichael Große * @author Adrian Lang <lang@cosmocode.de> 283479c05b1SMichael Große * 284479c05b1SMichael Große * @param string $id The target page or namespace, specified by id; Namespaces 285479c05b1SMichael Große * are identified by appending a colon. 2869c22b77cSMichael Große * 287479c05b1SMichael Große * @return string 288479c05b1SMichael Große */ 2899c22b77cSMichael Große protected function file($id) 2909c22b77cSMichael Große { 291479c05b1SMichael Große $meta_fname = '.mlist'; 292479c05b1SMichael Große if ((substr($id, -1, 1) === ':')) { 293479c05b1SMichael Große $meta_froot = getNS($id); 294479c05b1SMichael Große $meta_fname = '/' . $meta_fname; 295479c05b1SMichael Große } else { 296479c05b1SMichael Große $meta_froot = $id; 297479c05b1SMichael Große } 298479c05b1SMichael Große return metaFN((string)$meta_froot, $meta_fname); 299479c05b1SMichael Große } 300479c05b1SMichael Große} 301