1*479c05b1SMichael Große<?php 2*479c05b1SMichael Große 3*479c05b1SMichael Großenamespace dokuwiki\Subscriptions; 4*479c05b1SMichael Große 5*479c05b1SMichael Großeuse dokuwiki\Input\Input; 6*479c05b1SMichael Großeuse DokuWiki_Auth_Plugin; 7*479c05b1SMichael Großeuse Exception; 8*479c05b1SMichael Große 9*479c05b1SMichael Großeclass SubscriberManager 10*479c05b1SMichael Große{ 11*479c05b1SMichael Große 12*479c05b1SMichael Große /** 13*479c05b1SMichael Große * Check if subscription system is enabled 14*479c05b1SMichael Große * 15*479c05b1SMichael Große * @return bool 16*479c05b1SMichael Große */ 17*479c05b1SMichael Große public function isenabled() { 18*479c05b1SMichael Große return actionOK('subscribe'); 19*479c05b1SMichael Große } 20*479c05b1SMichael Große 21*479c05b1SMichael Große /** 22*479c05b1SMichael Große * Adds a new subscription for the given page or namespace 23*479c05b1SMichael Große * 24*479c05b1SMichael Große * This will automatically overwrite any existent subscription for the given user on this 25*479c05b1SMichael Große * *exact* page or namespace. It will *not* modify any subscription that may exist in higher namespaces. 26*479c05b1SMichael Große * 27*479c05b1SMichael Große * @param string $id The target page or namespace, specified by id; Namespaces 28*479c05b1SMichael Große * are identified by appending a colon. 29*479c05b1SMichael Große * @param string $user 30*479c05b1SMichael Große * @param string $style 31*479c05b1SMichael Große * @param string $data 32*479c05b1SMichael Große * @throws Exception when user or style is empty 33*479c05b1SMichael Große * @return bool 34*479c05b1SMichael Große */ 35*479c05b1SMichael Große public function add($id, $user, $style, $data = '') { 36*479c05b1SMichael Große if(!$this->isenabled()) return false; 37*479c05b1SMichael Große 38*479c05b1SMichael Große // delete any existing subscription 39*479c05b1SMichael Große $this->remove($id, $user); 40*479c05b1SMichael Große 41*479c05b1SMichael Große $user = auth_nameencode(trim($user)); 42*479c05b1SMichael Große $style = trim($style); 43*479c05b1SMichael Große $data = trim($data); 44*479c05b1SMichael Große 45*479c05b1SMichael Große if(!$user) throw new Exception('no subscription user given'); 46*479c05b1SMichael Große if(!$style) throw new Exception('no subscription style given'); 47*479c05b1SMichael Große if(!$data) $data = time(); //always add current time for new subscriptions 48*479c05b1SMichael Große 49*479c05b1SMichael Große $line = "$user $style $data\n"; 50*479c05b1SMichael Große $file = $this->file($id); 51*479c05b1SMichael Große return io_saveFile($file, $line, true); 52*479c05b1SMichael Große } 53*479c05b1SMichael Große 54*479c05b1SMichael Große 55*479c05b1SMichael Große /** 56*479c05b1SMichael Große * Removes a subscription for the given page or namespace 57*479c05b1SMichael Große * 58*479c05b1SMichael Große * This removes all subscriptions matching the given criteria on the given page or 59*479c05b1SMichael Große * namespace. It will *not* modify any subscriptions that may exist in higher 60*479c05b1SMichael Große * namespaces. 61*479c05b1SMichael Große * 62*479c05b1SMichael Große * @param string $id The target object’s (namespace or page) id 63*479c05b1SMichael Große * @param string|array $user 64*479c05b1SMichael Große * @param string|array $style 65*479c05b1SMichael Große * @param string|array $data 66*479c05b1SMichael Große * @return bool 67*479c05b1SMichael Große */ 68*479c05b1SMichael Große public function remove($id, $user = null, $style = null, $data = null) { 69*479c05b1SMichael Große if(!$this->isenabled()) return false; 70*479c05b1SMichael Große 71*479c05b1SMichael Große $file = $this->file($id); 72*479c05b1SMichael Große if(!file_exists($file)) return true; 73*479c05b1SMichael Große 74*479c05b1SMichael Große $regexBuilder = new SubscriberRegexBuilder(); 75*479c05b1SMichael Große $re = $regexBuilder->buildRegex($user, $style, $data); 76*479c05b1SMichael Große return io_deleteFromFile($file, $re, true); 77*479c05b1SMichael Große } 78*479c05b1SMichael Große 79*479c05b1SMichael Große /** 80*479c05b1SMichael Große * Get data for $INFO['subscribed'] 81*479c05b1SMichael Große * 82*479c05b1SMichael Große * $INFO['subscribed'] is either false if no subscription for the current page 83*479c05b1SMichael Große * and user is in effect. Else it contains an array of arrays with the fields 84*479c05b1SMichael Große * “target”, “style”, and optionally “data”. 85*479c05b1SMichael Große * 86*479c05b1SMichael Große * @param string $id Page ID, defaults to global $ID 87*479c05b1SMichael Große * @param string $user User, defaults to $_SERVER['REMOTE_USER'] 88*479c05b1SMichael Große * @return array|false 89*479c05b1SMichael Große * @author Adrian Lang <lang@cosmocode.de> 90*479c05b1SMichael Große */ 91*479c05b1SMichael Große public function userSubscription($id = '', $user = '') { 92*479c05b1SMichael Große if(!$this->isenabled()) return false; 93*479c05b1SMichael Große 94*479c05b1SMichael Große global $ID; 95*479c05b1SMichael Große /** @var Input $INPUT */ 96*479c05b1SMichael Große global $INPUT; 97*479c05b1SMichael Große if(!$id) $id = $ID; 98*479c05b1SMichael Große if(!$user) $user = $INPUT->server->str('REMOTE_USER'); 99*479c05b1SMichael Große 100*479c05b1SMichael Große $subs = $this->subscribers($id, $user); 101*479c05b1SMichael Große if(!count($subs)) return false; 102*479c05b1SMichael Große 103*479c05b1SMichael Große $result = array(); 104*479c05b1SMichael Große foreach($subs as $target => $info) { 105*479c05b1SMichael Große $result[] = array( 106*479c05b1SMichael Große 'target' => $target, 107*479c05b1SMichael Große 'style' => $info[$user][0], 108*479c05b1SMichael Große 'data' => $info[$user][1] 109*479c05b1SMichael Große ); 110*479c05b1SMichael Große } 111*479c05b1SMichael Große 112*479c05b1SMichael Große return $result; 113*479c05b1SMichael Große } 114*479c05b1SMichael Große 115*479c05b1SMichael Große /** 116*479c05b1SMichael Große * Recursively search for matching subscriptions 117*479c05b1SMichael Große * 118*479c05b1SMichael Große * This function searches all relevant subscription files for a page or 119*479c05b1SMichael Große * namespace. 120*479c05b1SMichael Große * 121*479c05b1SMichael Große * @author Adrian Lang <lang@cosmocode.de> 122*479c05b1SMichael Große * 123*479c05b1SMichael Große * @param string $page The target object’s (namespace or page) id 124*479c05b1SMichael Große * @param string|array $user 125*479c05b1SMichael Große * @param string|array $style 126*479c05b1SMichael Große * @param string|array $data 127*479c05b1SMichael Große * @return array 128*479c05b1SMichael Große */ 129*479c05b1SMichael Große public function subscribers($page, $user = null, $style = null, $data = null) { 130*479c05b1SMichael Große if(!$this->isenabled()) return array(); 131*479c05b1SMichael Große 132*479c05b1SMichael Große // Construct list of files which may contain relevant subscriptions. 133*479c05b1SMichael Große $files = array(':' => $this->file(':')); 134*479c05b1SMichael Große do { 135*479c05b1SMichael Große $files[$page] = $this->file($page); 136*479c05b1SMichael Große $page = getNS(rtrim($page, ':')).':'; 137*479c05b1SMichael Große } while($page !== ':'); 138*479c05b1SMichael Große 139*479c05b1SMichael Große $regexBuilder = new SubscriberRegexBuilder(); 140*479c05b1SMichael Große $re = $regexBuilder->buildRegex($user, $style, $data); 141*479c05b1SMichael Große 142*479c05b1SMichael Große // Handle files. 143*479c05b1SMichael Große $result = array(); 144*479c05b1SMichael Große foreach($files as $target => $file) { 145*479c05b1SMichael Große if(!file_exists($file)) continue; 146*479c05b1SMichael Große 147*479c05b1SMichael Große $lines = file($file); 148*479c05b1SMichael Große foreach($lines as $line) { 149*479c05b1SMichael Große // fix old style subscription files 150*479c05b1SMichael Große if(strpos($line, ' ') === false) $line = trim($line)." every\n"; 151*479c05b1SMichael Große 152*479c05b1SMichael Große // check for matching entries 153*479c05b1SMichael Große if(!preg_match($re, $line, $m)) continue; 154*479c05b1SMichael Große 155*479c05b1SMichael Große $u = rawurldecode($m[1]); // decode the user name 156*479c05b1SMichael Große if(!isset($result[$target])) $result[$target] = array(); 157*479c05b1SMichael Große $result[$target][$u] = array($m[2], $m[3]); // add to result 158*479c05b1SMichael Große } 159*479c05b1SMichael Große } 160*479c05b1SMichael Große return array_reverse($result); 161*479c05b1SMichael Große } 162*479c05b1SMichael Große 163*479c05b1SMichael Große /** 164*479c05b1SMichael Große * Default callback for COMMON_NOTIFY_ADDRESSLIST 165*479c05b1SMichael Große * 166*479c05b1SMichael Große * Aggregates all email addresses of user who have subscribed the given page with 'every' style 167*479c05b1SMichael Große * 168*479c05b1SMichael Große * @author Steven Danz <steven-danz@kc.rr.com> 169*479c05b1SMichael Große * @author Adrian Lang <lang@cosmocode.de> 170*479c05b1SMichael Große * 171*479c05b1SMichael Große * @todo move the whole functionality into this class, trigger SUBSCRIPTION_NOTIFY_ADDRESSLIST instead, 172*479c05b1SMichael Große * use an array for the addresses within it 173*479c05b1SMichael Große * 174*479c05b1SMichael Große * @param array &$data Containing the entries: 175*479c05b1SMichael Große * - $id (the page id), 176*479c05b1SMichael Große * - $self (whether the author should be notified, 177*479c05b1SMichael Große * - $addresslist (current email address list) 178*479c05b1SMichael Große * - $replacements (array of additional string substitutions, @KEY@ to be replaced by value) 179*479c05b1SMichael Große */ 180*479c05b1SMichael Große public function notifyAddresses(&$data) { 181*479c05b1SMichael Große if(!$this->isenabled()) return; 182*479c05b1SMichael Große 183*479c05b1SMichael Große /** @var DokuWiki_Auth_Plugin $auth */ 184*479c05b1SMichael Große global $auth; 185*479c05b1SMichael Große global $conf; 186*479c05b1SMichael Große /** @var \Input $INPUT */ 187*479c05b1SMichael Große global $INPUT; 188*479c05b1SMichael Große 189*479c05b1SMichael Große $id = $data['id']; 190*479c05b1SMichael Große $self = $data['self']; 191*479c05b1SMichael Große $addresslist = $data['addresslist']; 192*479c05b1SMichael Große 193*479c05b1SMichael Große $subscriptions = $this->subscribers($id, null, 'every'); 194*479c05b1SMichael Große 195*479c05b1SMichael Große $result = array(); 196*479c05b1SMichael Große foreach($subscriptions as $target => $users) { 197*479c05b1SMichael Große foreach($users as $user => $info) { 198*479c05b1SMichael Große $userinfo = $auth->getUserData($user); 199*479c05b1SMichael Große if($userinfo === false) continue; 200*479c05b1SMichael Große if(!$userinfo['mail']) continue; 201*479c05b1SMichael Große if(!$self && $user == $INPUT->server->str('REMOTE_USER')) continue; //skip our own changes 202*479c05b1SMichael Große 203*479c05b1SMichael Große $level = auth_aclcheck($id, $user, $userinfo['grps']); 204*479c05b1SMichael Große if($level >= AUTH_READ) { 205*479c05b1SMichael Große if(strcasecmp($userinfo['mail'], $conf['notify']) != 0) { //skip user who get notified elsewhere 206*479c05b1SMichael Große $result[$user] = $userinfo['mail']; 207*479c05b1SMichael Große } 208*479c05b1SMichael Große } 209*479c05b1SMichael Große } 210*479c05b1SMichael Große } 211*479c05b1SMichael Große $data['addresslist'] = trim($addresslist.','.implode(',', $result), ','); 212*479c05b1SMichael Große } 213*479c05b1SMichael Große 214*479c05b1SMichael Große /** 215*479c05b1SMichael Große * Return the subscription meta file for the given ID 216*479c05b1SMichael Große * 217*479c05b1SMichael Große * @author Adrian Lang <lang@cosmocode.de> 218*479c05b1SMichael Große * 219*479c05b1SMichael Große * @param string $id The target page or namespace, specified by id; Namespaces 220*479c05b1SMichael Große * are identified by appending a colon. 221*479c05b1SMichael Große * @return string 222*479c05b1SMichael Große */ 223*479c05b1SMichael Große protected function file($id) { 224*479c05b1SMichael Große $meta_fname = '.mlist'; 225*479c05b1SMichael Große if((substr($id, -1, 1) === ':')) { 226*479c05b1SMichael Große $meta_froot = getNS($id); 227*479c05b1SMichael Große $meta_fname = '/'.$meta_fname; 228*479c05b1SMichael Große } else { 229*479c05b1SMichael Große $meta_froot = $id; 230*479c05b1SMichael Große } 231*479c05b1SMichael Große return metaFN((string) $meta_froot, $meta_fname); 232*479c05b1SMichael Große } 233*479c05b1SMichael Große 234*479c05b1SMichael Große} 235