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