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 */ 17*9c22b77cSMichael Große public function isenabled() 18*9c22b77cSMichael 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 * 28*9c22b77cSMichael Große * @throws Exception when user or style is empty 29*9c22b77cSMichael 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 35*9c22b77cSMichael Große * 36479c05b1SMichael Große * @return bool 37479c05b1SMichael Große */ 38*9c22b77cSMichael Große public function add($id, $user, $style, $data = '') 39*9c22b77cSMichael Große { 40*9c22b77cSMichael Große if (!$this->isenabled()) { 41*9c22b77cSMichael Große return false; 42*9c22b77cSMichael 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 51*9c22b77cSMichael Große if (!$user) { 52*9c22b77cSMichael Große throw new Exception('no subscription user given'); 53*9c22b77cSMichael Große } 54*9c22b77cSMichael Große if (!$style) { 55*9c22b77cSMichael Große throw new Exception('no subscription style given'); 56*9c22b77cSMichael Große } 57*9c22b77cSMichael Große if (!$data) { 58*9c22b77cSMichael Große $data = time(); 59*9c22b77cSMichael 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 78*9c22b77cSMichael Große * 79479c05b1SMichael Große * @return bool 80479c05b1SMichael Große */ 81*9c22b77cSMichael Große public function remove($id, $user = null, $style = null, $data = null) 82*9c22b77cSMichael Große { 83*9c22b77cSMichael Große if (!$this->isenabled()) { 84*9c22b77cSMichael Große return false; 85*9c22b77cSMichael Große } 86479c05b1SMichael Große 87479c05b1SMichael Große $file = $this->file($id); 88*9c22b77cSMichael Große if (!file_exists($file)) { 89*9c22b77cSMichael Große return true; 90*9c22b77cSMichael 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 * 104*9c22b77cSMichael Große * @author Adrian Lang <lang@cosmocode.de> 105*9c22b77cSMichael 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'] 108*9c22b77cSMichael Große * 109479c05b1SMichael Große * @return array|false 110479c05b1SMichael Große */ 111*9c22b77cSMichael Große public function userSubscription($id = '', $user = '') 112*9c22b77cSMichael Große { 113*9c22b77cSMichael Große if (!$this->isenabled()) { 114*9c22b77cSMichael Große return false; 115*9c22b77cSMichael Große } 116479c05b1SMichael Große 117479c05b1SMichael Große global $ID; 118479c05b1SMichael Große /** @var Input $INPUT */ 119479c05b1SMichael Große global $INPUT; 120*9c22b77cSMichael Große if (!$id) { 121*9c22b77cSMichael Große $id = $ID; 122*9c22b77cSMichael Große } 123*9c22b77cSMichael Große if (!$user) { 124*9c22b77cSMichael Große $user = $INPUT->server->str('REMOTE_USER'); 125*9c22b77cSMichael Große } 126479c05b1SMichael Große 127479c05b1SMichael Große $subs = $this->subscribers($id, $user); 128*9c22b77cSMichael Große if (!count($subs)) { 129*9c22b77cSMichael Große return false; 130*9c22b77cSMichael Große } 131479c05b1SMichael Große 132*9c22b77cSMichael Große $result = []; 133479c05b1SMichael Große foreach ($subs as $target => $info) { 134*9c22b77cSMichael Große $result[] = [ 135479c05b1SMichael Große 'target' => $target, 136479c05b1SMichael Große 'style' => $info[$user][0], 137*9c22b77cSMichael Große 'data' => $info[$user][1], 138*9c22b77cSMichael Große ]; 139479c05b1SMichael Große } 140479c05b1SMichael Große 141479c05b1SMichael Große return $result; 142479c05b1SMichael Große } 143479c05b1SMichael Große 144479c05b1SMichael Große /** 145479c05b1SMichael Große * Recursively search for matching subscriptions 146479c05b1SMichael Große * 147479c05b1SMichael Große * This function searches all relevant subscription files for a page or 148479c05b1SMichael Große * namespace. 149479c05b1SMichael Große * 150479c05b1SMichael Große * @author Adrian Lang <lang@cosmocode.de> 151479c05b1SMichael Große * 152479c05b1SMichael Große * @param string $page The target object’s (namespace or page) id 153479c05b1SMichael Große * @param string|array $user 154479c05b1SMichael Große * @param string|array $style 155479c05b1SMichael Große * @param string|array $data 156*9c22b77cSMichael Große * 157479c05b1SMichael Große * @return array 158479c05b1SMichael Große */ 159*9c22b77cSMichael Große public function subscribers($page, $user = null, $style = null, $data = null) 160*9c22b77cSMichael Große { 161*9c22b77cSMichael Große if (!$this->isenabled()) { 162*9c22b77cSMichael Große return []; 163*9c22b77cSMichael Große } 164479c05b1SMichael Große 165479c05b1SMichael Große // Construct list of files which may contain relevant subscriptions. 166*9c22b77cSMichael Große $files = [':' => $this->file(':')]; 167479c05b1SMichael Große do { 168479c05b1SMichael Große $files[$page] = $this->file($page); 169479c05b1SMichael Große $page = getNS(rtrim($page, ':')) . ':'; 170479c05b1SMichael Große } while ($page !== ':'); 171479c05b1SMichael Große 172479c05b1SMichael Große $regexBuilder = new SubscriberRegexBuilder(); 173479c05b1SMichael Große $re = $regexBuilder->buildRegex($user, $style, $data); 174479c05b1SMichael Große 175479c05b1SMichael Große // Handle files. 176*9c22b77cSMichael Große $result = []; 177479c05b1SMichael Große foreach ($files as $target => $file) { 178*9c22b77cSMichael Große if (!file_exists($file)) { 179*9c22b77cSMichael Große continue; 180*9c22b77cSMichael Große } 181479c05b1SMichael Große 182479c05b1SMichael Große $lines = file($file); 183479c05b1SMichael Große foreach ($lines as $line) { 184479c05b1SMichael Große // fix old style subscription files 185*9c22b77cSMichael Große if (strpos($line, ' ') === false) { 186*9c22b77cSMichael Große $line = trim($line) . " every\n"; 187*9c22b77cSMichael Große } 188479c05b1SMichael Große 189479c05b1SMichael Große // check for matching entries 190*9c22b77cSMichael Große if (!preg_match($re, $line, $m)) { 191*9c22b77cSMichael Große continue; 192*9c22b77cSMichael Große } 193479c05b1SMichael Große 194479c05b1SMichael Große $u = rawurldecode($m[1]); // decode the user name 195*9c22b77cSMichael Große if (!isset($result[$target])) { 196*9c22b77cSMichael Große $result[$target] = []; 197*9c22b77cSMichael Große } 198*9c22b77cSMichael Große $result[$target][$u] = [$m[2], $m[3]]; // add to result 199479c05b1SMichael Große } 200479c05b1SMichael Große } 201479c05b1SMichael Große return array_reverse($result); 202479c05b1SMichael Große } 203479c05b1SMichael Große 204479c05b1SMichael Große /** 205479c05b1SMichael Große * Default callback for COMMON_NOTIFY_ADDRESSLIST 206479c05b1SMichael Große * 207479c05b1SMichael Große * Aggregates all email addresses of user who have subscribed the given page with 'every' style 208479c05b1SMichael Große * 209479c05b1SMichael Große * @author Adrian Lang <lang@cosmocode.de> 210*9c22b77cSMichael Große * @author Steven Danz <steven-danz@kc.rr.com> 211479c05b1SMichael Große * 212479c05b1SMichael Große * @todo move the whole functionality into this class, trigger SUBSCRIPTION_NOTIFY_ADDRESSLIST instead, 213479c05b1SMichael Große * use an array for the addresses within it 214479c05b1SMichael Große * 215479c05b1SMichael Große * @param array &$data Containing the entries: 216479c05b1SMichael Große * - $id (the page id), 217479c05b1SMichael Große * - $self (whether the author should be notified, 218479c05b1SMichael Große * - $addresslist (current email address list) 219479c05b1SMichael Große * - $replacements (array of additional string substitutions, @KEY@ to be replaced by value) 220479c05b1SMichael Große */ 221*9c22b77cSMichael Große public function notifyAddresses(&$data) 222*9c22b77cSMichael Große { 223*9c22b77cSMichael Große if (!$this->isenabled()) { 224*9c22b77cSMichael Große return; 225*9c22b77cSMichael Große } 226479c05b1SMichael Große 227479c05b1SMichael Große /** @var DokuWiki_Auth_Plugin $auth */ 228479c05b1SMichael Große global $auth; 229479c05b1SMichael Große global $conf; 230479c05b1SMichael Große /** @var \Input $INPUT */ 231479c05b1SMichael Große global $INPUT; 232479c05b1SMichael Große 233479c05b1SMichael Große $id = $data['id']; 234479c05b1SMichael Große $self = $data['self']; 235479c05b1SMichael Große $addresslist = $data['addresslist']; 236479c05b1SMichael Große 237479c05b1SMichael Große $subscriptions = $this->subscribers($id, null, 'every'); 238479c05b1SMichael Große 239*9c22b77cSMichael Große $result = []; 240479c05b1SMichael Große foreach ($subscriptions as $target => $users) { 241479c05b1SMichael Große foreach ($users as $user => $info) { 242479c05b1SMichael Große $userinfo = $auth->getUserData($user); 243*9c22b77cSMichael Große if ($userinfo === false) { 244*9c22b77cSMichael Große continue; 245*9c22b77cSMichael Große } 246*9c22b77cSMichael Große if (!$userinfo['mail']) { 247*9c22b77cSMichael Große continue; 248*9c22b77cSMichael Große } 249*9c22b77cSMichael Große if (!$self && $user == $INPUT->server->str('REMOTE_USER')) { 250*9c22b77cSMichael Große continue; 251*9c22b77cSMichael Große } //skip our own changes 252479c05b1SMichael Große 253479c05b1SMichael Große $level = auth_aclcheck($id, $user, $userinfo['grps']); 254479c05b1SMichael Große if ($level >= AUTH_READ) { 255479c05b1SMichael Große if (strcasecmp($userinfo['mail'], $conf['notify']) != 0) { //skip user who get notified elsewhere 256479c05b1SMichael Große $result[$user] = $userinfo['mail']; 257479c05b1SMichael Große } 258479c05b1SMichael Große } 259479c05b1SMichael Große } 260479c05b1SMichael Große } 261479c05b1SMichael Große $data['addresslist'] = trim($addresslist . ',' . implode(',', $result), ','); 262479c05b1SMichael Große } 263479c05b1SMichael Große 264479c05b1SMichael Große /** 265479c05b1SMichael Große * Return the subscription meta file for the given ID 266479c05b1SMichael Große * 267479c05b1SMichael Große * @author Adrian Lang <lang@cosmocode.de> 268479c05b1SMichael Große * 269479c05b1SMichael Große * @param string $id The target page or namespace, specified by id; Namespaces 270479c05b1SMichael Große * are identified by appending a colon. 271*9c22b77cSMichael Große * 272479c05b1SMichael Große * @return string 273479c05b1SMichael Große */ 274*9c22b77cSMichael Große protected function file($id) 275*9c22b77cSMichael Große { 276479c05b1SMichael Große $meta_fname = '.mlist'; 277479c05b1SMichael Große if ((substr($id, -1, 1) === ':')) { 278479c05b1SMichael Große $meta_froot = getNS($id); 279479c05b1SMichael Große $meta_fname = '/' . $meta_fname; 280479c05b1SMichael Große } else { 281479c05b1SMichael Große $meta_froot = $id; 282479c05b1SMichael Große } 283479c05b1SMichael Große return metaFN((string)$meta_froot, $meta_fname); 284479c05b1SMichael Große } 285479c05b1SMichael Große} 286