1479c05b1SMichael Große<?php 2479c05b1SMichael Große 3479c05b1SMichael Großenamespace dokuwiki\Subscriptions; 4479c05b1SMichael Große 5479c05b1SMichael Großeuse dokuwiki\Input\Input; 6479c05b1SMichael Großeuse DokuWiki_Auth_Plugin; 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 80479c05b1SMichael Große */ 819c22b77cSMichael Große public function remove($id, $user = null, $style = null, $data = null) 829c22b77cSMichael Große { 839c22b77cSMichael Große if (!$this->isenabled()) { 849c22b77cSMichael Große return false; 859c22b77cSMichael Große } 86479c05b1SMichael Große 87479c05b1SMichael Große $file = $this->file($id); 889c22b77cSMichael Große if (!file_exists($file)) { 899c22b77cSMichael Große return true; 909c22b77cSMichael Große } 91479c05b1SMichael Große 92479c05b1SMichael Große $regexBuilder = new SubscriberRegexBuilder(); 93479c05b1SMichael Große $re = $regexBuilder->buildRegex($user, $style, $data); 94479c05b1SMichael Große return io_deleteFromFile($file, $re, true); 95479c05b1SMichael Große } 96479c05b1SMichael Große 97479c05b1SMichael Große /** 98479c05b1SMichael Große * Get data for $INFO['subscribed'] 99479c05b1SMichael Große * 100479c05b1SMichael Große * $INFO['subscribed'] is either false if no subscription for the current page 101479c05b1SMichael Große * and user is in effect. Else it contains an array of arrays with the fields 102479c05b1SMichael Große * “target”, “style”, and optionally “data”. 103479c05b1SMichael Große * 1049c22b77cSMichael Große * @author Adrian Lang <lang@cosmocode.de> 1059c22b77cSMichael Große * 106479c05b1SMichael Große * @param string $id Page ID, defaults to global $ID 107479c05b1SMichael Große * @param string $user User, defaults to $_SERVER['REMOTE_USER'] 1089c22b77cSMichael Große * 109479c05b1SMichael Große * @return array|false 110479c05b1SMichael Große */ 1119c22b77cSMichael Große public function userSubscription($id = '', $user = '') 1129c22b77cSMichael Große { 1139c22b77cSMichael Große if (!$this->isenabled()) { 1149c22b77cSMichael Große return false; 1159c22b77cSMichael Große } 116479c05b1SMichael Große 117479c05b1SMichael Große global $ID; 118479c05b1SMichael Große /** @var Input $INPUT */ 119479c05b1SMichael Große global $INPUT; 1209c22b77cSMichael Große if (!$id) { 1219c22b77cSMichael Große $id = $ID; 1229c22b77cSMichael Große } 1239c22b77cSMichael Große if (!$user) { 1249c22b77cSMichael Große $user = $INPUT->server->str('REMOTE_USER'); 1259c22b77cSMichael Große } 126479c05b1SMichael Große 127*9efb6f4aSPhy if (empty($user)) { 128*9efb6f4aSPhy // not logged in 129*9efb6f4aSPhy return false; 130*9efb6f4aSPhy } 131*9efb6f4aSPhy 132479c05b1SMichael Große $subs = $this->subscribers($id, $user); 1339c22b77cSMichael Große if (!count($subs)) { 1349c22b77cSMichael Große return false; 1359c22b77cSMichael Große } 136479c05b1SMichael Große 1379c22b77cSMichael Große $result = []; 138479c05b1SMichael Große foreach ($subs as $target => $info) { 1399c22b77cSMichael Große $result[] = [ 140479c05b1SMichael Große 'target' => $target, 141479c05b1SMichael Große 'style' => $info[$user][0], 1429c22b77cSMichael Große 'data' => $info[$user][1], 1439c22b77cSMichael Große ]; 144479c05b1SMichael Große } 145479c05b1SMichael Große 146479c05b1SMichael Große return $result; 147479c05b1SMichael Große } 148479c05b1SMichael Große 149479c05b1SMichael Große /** 150479c05b1SMichael Große * Recursively search for matching subscriptions 151479c05b1SMichael Große * 152479c05b1SMichael Große * This function searches all relevant subscription files for a page or 153479c05b1SMichael Große * namespace. 154479c05b1SMichael Große * 155479c05b1SMichael Große * @author Adrian Lang <lang@cosmocode.de> 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 163479c05b1SMichael Große */ 1649c22b77cSMichael Große public function subscribers($page, $user = null, $style = null, $data = null) 1659c22b77cSMichael Große { 1669c22b77cSMichael Große if (!$this->isenabled()) { 1679c22b77cSMichael Große return []; 1689c22b77cSMichael Große } 169479c05b1SMichael Große 170479c05b1SMichael Große // Construct list of files which may contain relevant subscriptions. 1719c22b77cSMichael Große $files = [':' => $this->file(':')]; 172479c05b1SMichael Große do { 173479c05b1SMichael Große $files[$page] = $this->file($page); 174479c05b1SMichael Große $page = getNS(rtrim($page, ':')) . ':'; 175479c05b1SMichael Große } while ($page !== ':'); 176479c05b1SMichael Große 177479c05b1SMichael Große $regexBuilder = new SubscriberRegexBuilder(); 178479c05b1SMichael Große $re = $regexBuilder->buildRegex($user, $style, $data); 179479c05b1SMichael Große 180479c05b1SMichael Große // Handle files. 1819c22b77cSMichael Große $result = []; 182479c05b1SMichael Große foreach ($files as $target => $file) { 1839c22b77cSMichael Große if (!file_exists($file)) { 1849c22b77cSMichael Große continue; 1859c22b77cSMichael Große } 186479c05b1SMichael Große 187479c05b1SMichael Große $lines = file($file); 188479c05b1SMichael Große foreach ($lines as $line) { 189479c05b1SMichael Große // fix old style subscription files 1909c22b77cSMichael Große if (strpos($line, ' ') === false) { 1919c22b77cSMichael Große $line = trim($line) . " every\n"; 1929c22b77cSMichael Große } 193479c05b1SMichael Große 194479c05b1SMichael Große // check for matching entries 1959c22b77cSMichael Große if (!preg_match($re, $line, $m)) { 1969c22b77cSMichael Große continue; 1979c22b77cSMichael Große } 198479c05b1SMichael Große 199479c05b1SMichael Große $u = rawurldecode($m[1]); // decode the user name 2009c22b77cSMichael Große if (!isset($result[$target])) { 2019c22b77cSMichael Große $result[$target] = []; 2029c22b77cSMichael Große } 2039c22b77cSMichael Große $result[$target][$u] = [$m[2], $m[3]]; // add to result 204479c05b1SMichael Große } 205479c05b1SMichael Große } 206479c05b1SMichael Große return array_reverse($result); 207479c05b1SMichael Große } 208479c05b1SMichael Große 209479c05b1SMichael Große /** 210479c05b1SMichael Große * Default callback for COMMON_NOTIFY_ADDRESSLIST 211479c05b1SMichael Große * 212479c05b1SMichael Große * Aggregates all email addresses of user who have subscribed the given page with 'every' style 213479c05b1SMichael Große * 214479c05b1SMichael Große * @author Adrian Lang <lang@cosmocode.de> 2159c22b77cSMichael Große * @author Steven Danz <steven-danz@kc.rr.com> 216479c05b1SMichael Große * 217479c05b1SMichael Große * @todo move the whole functionality into this class, trigger SUBSCRIPTION_NOTIFY_ADDRESSLIST instead, 218479c05b1SMichael Große * use an array for the addresses within it 219479c05b1SMichael Große * 220479c05b1SMichael Große * @param array &$data Containing the entries: 221479c05b1SMichael Große * - $id (the page id), 222479c05b1SMichael Große * - $self (whether the author should be notified, 223479c05b1SMichael Große * - $addresslist (current email address list) 224479c05b1SMichael Große * - $replacements (array of additional string substitutions, @KEY@ to be replaced by value) 225479c05b1SMichael Große */ 2269c22b77cSMichael Große public function notifyAddresses(&$data) 2279c22b77cSMichael Große { 2289c22b77cSMichael Große if (!$this->isenabled()) { 2299c22b77cSMichael Große return; 2309c22b77cSMichael Große } 231479c05b1SMichael Große 232479c05b1SMichael Große /** @var DokuWiki_Auth_Plugin $auth */ 233479c05b1SMichael Große global $auth; 234479c05b1SMichael Große global $conf; 235479c05b1SMichael Große /** @var \Input $INPUT */ 236479c05b1SMichael Große global $INPUT; 237479c05b1SMichael Große 238479c05b1SMichael Große $id = $data['id']; 239479c05b1SMichael Große $self = $data['self']; 240479c05b1SMichael Große $addresslist = $data['addresslist']; 241479c05b1SMichael Große 242479c05b1SMichael Große $subscriptions = $this->subscribers($id, null, 'every'); 243479c05b1SMichael Große 2449c22b77cSMichael Große $result = []; 245479c05b1SMichael Große foreach ($subscriptions as $target => $users) { 246479c05b1SMichael Große foreach ($users as $user => $info) { 247479c05b1SMichael Große $userinfo = $auth->getUserData($user); 2489c22b77cSMichael Große if ($userinfo === false) { 2499c22b77cSMichael Große continue; 2509c22b77cSMichael Große } 2519c22b77cSMichael Große if (!$userinfo['mail']) { 2529c22b77cSMichael Große continue; 2539c22b77cSMichael Große } 2549c22b77cSMichael Große if (!$self && $user == $INPUT->server->str('REMOTE_USER')) { 2559c22b77cSMichael Große continue; 2569c22b77cSMichael Große } //skip our own changes 257479c05b1SMichael Große 258479c05b1SMichael Große $level = auth_aclcheck($id, $user, $userinfo['grps']); 259479c05b1SMichael Große if ($level >= AUTH_READ) { 260479c05b1SMichael Große if (strcasecmp($userinfo['mail'], $conf['notify']) != 0) { //skip user who get notified elsewhere 261479c05b1SMichael Große $result[$user] = $userinfo['mail']; 262479c05b1SMichael Große } 263479c05b1SMichael Große } 264479c05b1SMichael Große } 265479c05b1SMichael Große } 266479c05b1SMichael Große $data['addresslist'] = trim($addresslist . ',' . implode(',', $result), ','); 267479c05b1SMichael Große } 268479c05b1SMichael Große 269479c05b1SMichael Große /** 270479c05b1SMichael Große * Return the subscription meta file for the given ID 271479c05b1SMichael Große * 272479c05b1SMichael Große * @author Adrian Lang <lang@cosmocode.de> 273479c05b1SMichael Große * 274479c05b1SMichael Große * @param string $id The target page or namespace, specified by id; Namespaces 275479c05b1SMichael Große * are identified by appending a colon. 2769c22b77cSMichael Große * 277479c05b1SMichael Große * @return string 278479c05b1SMichael Große */ 2799c22b77cSMichael Große protected function file($id) 2809c22b77cSMichael Große { 281479c05b1SMichael Große $meta_fname = '.mlist'; 282479c05b1SMichael Große if ((substr($id, -1, 1) === ':')) { 283479c05b1SMichael Große $meta_froot = getNS($id); 284479c05b1SMichael Große $meta_fname = '/' . $meta_fname; 285479c05b1SMichael Große } else { 286479c05b1SMichael Große $meta_froot = $id; 287479c05b1SMichael Große } 288479c05b1SMichael Große return metaFN((string)$meta_froot, $meta_fname); 289479c05b1SMichael Große } 290479c05b1SMichael Große} 291