These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | use dokuwiki\ChangeLog\PageChangeLog; |
||
0 ignored issues
–
show
|
|||
4 | |||
5 | /** |
||
6 | * Class for handling (email) subscriptions |
||
7 | * |
||
8 | * @author Adrian Lang <[email protected]> |
||
9 | * @author Andreas Gohr <[email protected]> |
||
10 | * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) |
||
11 | */ |
||
12 | class Subscription { |
||
13 | |||
14 | /** |
||
15 | * Check if subscription system is enabled |
||
16 | * |
||
17 | * @return bool |
||
18 | */ |
||
19 | public function isenabled() { |
||
20 | return actionOK('subscribe'); |
||
21 | } |
||
22 | |||
23 | /** |
||
24 | * Return the subscription meta file for the given ID |
||
25 | * |
||
26 | * @author Adrian Lang <[email protected]> |
||
27 | * |
||
28 | * @param string $id The target page or namespace, specified by id; Namespaces |
||
29 | * are identified by appending a colon. |
||
30 | * @return string |
||
31 | */ |
||
32 | protected function file($id) { |
||
33 | $meta_fname = '.mlist'; |
||
34 | if((substr($id, -1, 1) === ':')) { |
||
35 | $meta_froot = getNS($id); |
||
36 | $meta_fname = '/'.$meta_fname; |
||
37 | } else { |
||
38 | $meta_froot = $id; |
||
39 | } |
||
40 | return metaFN((string) $meta_froot, $meta_fname); |
||
41 | } |
||
42 | |||
43 | /** |
||
44 | * Lock subscription info |
||
45 | * |
||
46 | * We don't use io_lock() her because we do not wait for the lock and use a larger stale time |
||
47 | * |
||
48 | * @author Adrian Lang <[email protected]> |
||
49 | * @param string $id The target page or namespace, specified by id; Namespaces |
||
50 | * are identified by appending a colon. |
||
51 | * @return bool true, if you got a succesful lock |
||
52 | */ |
||
53 | protected function lock($id) { |
||
54 | global $conf; |
||
55 | |||
56 | $lock = $conf['lockdir'].'/_subscr_'.md5($id).'.lock'; |
||
57 | |||
58 | if(is_dir($lock) && time() - @filemtime($lock) > 60 * 5) { |
||
59 | // looks like a stale lock - remove it |
||
60 | @rmdir($lock); |
||
61 | } |
||
62 | |||
63 | // try creating the lock directory |
||
64 | if(!@mkdir($lock, $conf['dmode'])) { |
||
65 | return false; |
||
66 | } |
||
67 | |||
68 | if(!empty($conf['dperm'])) chmod($lock, $conf['dperm']); |
||
69 | return true; |
||
70 | } |
||
71 | |||
72 | /** |
||
73 | * Unlock subscription info |
||
74 | * |
||
75 | * @author Adrian Lang <[email protected]> |
||
76 | * @param string $id The target page or namespace, specified by id; Namespaces |
||
77 | * are identified by appending a colon. |
||
78 | * @return bool |
||
79 | */ |
||
80 | protected function unlock($id) { |
||
81 | global $conf; |
||
82 | $lock = $conf['lockdir'].'/_subscr_'.md5($id).'.lock'; |
||
83 | return @rmdir($lock); |
||
84 | } |
||
85 | |||
86 | /** |
||
87 | * Construct a regular expression for parsing a subscription definition line |
||
88 | * |
||
89 | * @author Andreas Gohr <[email protected]> |
||
90 | * |
||
91 | * @param string|array $user |
||
92 | * @param string|array $style |
||
93 | * @param string|array $data |
||
94 | * @return string complete regexp including delimiters |
||
95 | * @throws Exception when no data is passed |
||
96 | */ |
||
97 | protected function buildregex($user = null, $style = null, $data = null) { |
||
98 | // always work with arrays |
||
99 | $user = (array) $user; |
||
100 | $style = (array) $style; |
||
101 | $data = (array) $data; |
||
102 | |||
103 | // clean |
||
104 | $user = array_filter(array_map('trim', $user)); |
||
105 | $style = array_filter(array_map('trim', $style)); |
||
106 | $data = array_filter(array_map('trim', $data)); |
||
107 | |||
108 | // user names are encoded |
||
109 | $user = array_map('auth_nameencode', $user); |
||
110 | |||
111 | // quote |
||
112 | $user = array_map('preg_quote_cb', $user); |
||
113 | $style = array_map('preg_quote_cb', $style); |
||
114 | $data = array_map('preg_quote_cb', $data); |
||
115 | |||
116 | // join |
||
117 | $user = join('|', $user); |
||
118 | $style = join('|', $style); |
||
119 | $data = join('|', $data); |
||
120 | |||
121 | // any data at all? |
||
122 | if($user.$style.$data === '') throw new Exception('no data passed'); |
||
123 | |||
124 | // replace empty values, set which ones are optional |
||
125 | $sopt = ''; |
||
126 | $dopt = ''; |
||
127 | if($user === '') { |
||
128 | $user = '\S+'; |
||
129 | } |
||
130 | if($style === '') { |
||
131 | $style = '\S+'; |
||
132 | $sopt = '?'; |
||
133 | } |
||
134 | if($data === '') { |
||
135 | $data = '\S+'; |
||
136 | $dopt = '?'; |
||
137 | } |
||
138 | |||
139 | // assemble |
||
140 | return "/^($user)(?:\\s+($style))$sopt(?:\\s+($data))$dopt$/"; |
||
141 | } |
||
142 | |||
143 | /** |
||
144 | * Recursively search for matching subscriptions |
||
145 | * |
||
146 | * This function searches all relevant subscription files for a page or |
||
147 | * namespace. |
||
148 | * |
||
149 | * @author Adrian Lang <[email protected]> |
||
150 | * |
||
151 | * @param string $page The target object’s (namespace or page) id |
||
152 | * @param string|array $user |
||
153 | * @param string|array $style |
||
154 | * @param string|array $data |
||
155 | * @return array |
||
156 | */ |
||
157 | public function subscribers($page, $user = null, $style = null, $data = null) { |
||
158 | if(!$this->isenabled()) return array(); |
||
159 | |||
160 | // Construct list of files which may contain relevant subscriptions. |
||
161 | $files = array(':' => $this->file(':')); |
||
162 | do { |
||
163 | $files[$page] = $this->file($page); |
||
164 | $page = getNS(rtrim($page, ':')).':'; |
||
165 | } while($page !== ':'); |
||
166 | |||
167 | $re = $this->buildregex($user, $style, $data); |
||
168 | |||
169 | // Handle files. |
||
170 | $result = array(); |
||
171 | foreach($files as $target => $file) { |
||
172 | if(!file_exists($file)) continue; |
||
173 | |||
174 | $lines = file($file); |
||
175 | foreach($lines as $line) { |
||
176 | // fix old style subscription files |
||
177 | if(strpos($line, ' ') === false) $line = trim($line)." every\n"; |
||
178 | |||
179 | // check for matching entries |
||
180 | if(!preg_match($re, $line, $m)) continue; |
||
181 | |||
182 | $u = rawurldecode($m[1]); // decode the user name |
||
183 | if(!isset($result[$target])) $result[$target] = array(); |
||
184 | $result[$target][$u] = array($m[2], $m[3]); // add to result |
||
185 | } |
||
186 | } |
||
187 | return array_reverse($result); |
||
188 | } |
||
189 | |||
190 | /** |
||
191 | * Adds a new subscription for the given page or namespace |
||
192 | * |
||
193 | * This will automatically overwrite any existent subscription for the given user on this |
||
194 | * *exact* page or namespace. It will *not* modify any subscription that may exist in higher namespaces. |
||
195 | * |
||
196 | * @param string $id The target page or namespace, specified by id; Namespaces |
||
197 | * are identified by appending a colon. |
||
198 | * @param string $user |
||
199 | * @param string $style |
||
200 | * @param string $data |
||
201 | * @throws Exception when user or style is empty |
||
202 | * @return bool |
||
203 | */ |
||
204 | public function add($id, $user, $style, $data = '') { |
||
205 | if(!$this->isenabled()) return false; |
||
206 | |||
207 | // delete any existing subscription |
||
208 | $this->remove($id, $user); |
||
209 | |||
210 | $user = auth_nameencode(trim($user)); |
||
211 | $style = trim($style); |
||
212 | $data = trim($data); |
||
213 | |||
214 | if(!$user) throw new Exception('no subscription user given'); |
||
215 | if(!$style) throw new Exception('no subscription style given'); |
||
216 | if(!$data) $data = time(); //always add current time for new subscriptions |
||
217 | |||
218 | $line = "$user $style $data\n"; |
||
219 | $file = $this->file($id); |
||
220 | return io_saveFile($file, $line, true); |
||
221 | } |
||
222 | |||
223 | /** |
||
224 | * Removes a subscription for the given page or namespace |
||
225 | * |
||
226 | * This removes all subscriptions matching the given criteria on the given page or |
||
227 | * namespace. It will *not* modify any subscriptions that may exist in higher |
||
228 | * namespaces. |
||
229 | * |
||
230 | * @param string $id The target object’s (namespace or page) id |
||
231 | * @param string|array $user |
||
232 | * @param string|array $style |
||
233 | * @param string|array $data |
||
234 | * @return bool |
||
235 | */ |
||
236 | public function remove($id, $user = null, $style = null, $data = null) { |
||
237 | if(!$this->isenabled()) return false; |
||
238 | |||
239 | $file = $this->file($id); |
||
240 | if(!file_exists($file)) return true; |
||
241 | |||
242 | $re = $this->buildregex($user, $style, $data); |
||
243 | return io_deleteFromFile($file, $re, true); |
||
244 | } |
||
245 | |||
246 | /** |
||
247 | * Get data for $INFO['subscribed'] |
||
248 | * |
||
249 | * $INFO['subscribed'] is either false if no subscription for the current page |
||
250 | * and user is in effect. Else it contains an array of arrays with the fields |
||
251 | * “target”, “style”, and optionally “data”. |
||
252 | * |
||
253 | * @param string $id Page ID, defaults to global $ID |
||
254 | * @param string $user User, defaults to $_SERVER['REMOTE_USER'] |
||
255 | * @return array|false |
||
256 | * @author Adrian Lang <[email protected]> |
||
257 | */ |
||
258 | public function user_subscription($id = '', $user = '') { |
||
259 | if(!$this->isenabled()) return false; |
||
260 | |||
261 | global $ID; |
||
262 | /** @var Input $INPUT */ |
||
263 | global $INPUT; |
||
264 | if(!$id) $id = $ID; |
||
265 | if(!$user) $user = $INPUT->server->str('REMOTE_USER'); |
||
266 | |||
267 | $subs = $this->subscribers($id, $user); |
||
268 | if(!count($subs)) return false; |
||
269 | |||
270 | $result = array(); |
||
271 | foreach($subs as $target => $info) { |
||
272 | $result[] = array( |
||
273 | 'target' => $target, |
||
274 | 'style' => $info[$user][0], |
||
275 | 'data' => $info[$user][1] |
||
276 | ); |
||
277 | } |
||
278 | |||
279 | return $result; |
||
280 | } |
||
281 | |||
282 | /** |
||
283 | * Send digest and list subscriptions |
||
284 | * |
||
285 | * This sends mails to all subscribers that have a subscription for namespaces above |
||
286 | * the given page if the needed $conf['subscribe_time'] has passed already. |
||
287 | * |
||
288 | * This function is called form lib/exe/indexer.php |
||
289 | * |
||
290 | * @param string $page |
||
291 | * @return int number of sent mails |
||
292 | */ |
||
293 | public function send_bulk($page) { |
||
294 | if(!$this->isenabled()) return 0; |
||
295 | |||
296 | /** @var DokuWiki_Auth_Plugin $auth */ |
||
297 | global $auth; |
||
298 | global $conf; |
||
299 | global $USERINFO; |
||
300 | /** @var Input $INPUT */ |
||
301 | global $INPUT; |
||
302 | $count = 0; |
||
303 | |||
304 | $subscriptions = $this->subscribers($page, null, array('digest', 'list')); |
||
305 | |||
306 | // remember current user info |
||
307 | $olduinfo = $USERINFO; |
||
308 | $olduser = $INPUT->server->str('REMOTE_USER'); |
||
309 | |||
310 | foreach($subscriptions as $target => $users) { |
||
311 | if(!$this->lock($target)) continue; |
||
312 | |||
313 | foreach($users as $user => $info) { |
||
314 | list($style, $lastupdate) = $info; |
||
315 | |||
316 | $lastupdate = (int) $lastupdate; |
||
317 | if($lastupdate + $conf['subscribe_time'] > time()) { |
||
318 | // Less than the configured time period passed since last |
||
319 | // update. |
||
320 | continue; |
||
321 | } |
||
322 | |||
323 | // Work as the user to make sure ACLs apply correctly |
||
324 | $USERINFO = $auth->getUserData($user); |
||
325 | $INPUT->server->set('REMOTE_USER',$user); |
||
326 | if($USERINFO === false) continue; |
||
327 | if(!$USERINFO['mail']) continue; |
||
328 | |||
329 | if(substr($target, -1, 1) === ':') { |
||
330 | // subscription target is a namespace, get all changes within |
||
331 | $changes = getRecentsSince($lastupdate, null, getNS($target)); |
||
332 | } else { |
||
333 | // single page subscription, check ACL ourselves |
||
334 | if(auth_quickaclcheck($target) < AUTH_READ) continue; |
||
335 | $meta = p_get_metadata($target); |
||
336 | $changes = array($meta['last_change']); |
||
337 | } |
||
338 | |||
339 | // Filter out pages only changed in small and own edits |
||
340 | $change_ids = array(); |
||
341 | foreach($changes as $rev) { |
||
342 | $n = 0; |
||
343 | while(!is_null($rev) && $rev['date'] >= $lastupdate && |
||
344 | ($INPUT->server->str('REMOTE_USER') === $rev['user'] || |
||
345 | $rev['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT)) { |
||
346 | $pagelog = new PageChangeLog($rev['id']); |
||
347 | $rev = $pagelog->getRevisions($n++, 1); |
||
348 | $rev = (count($rev) > 0) ? $rev[0] : null; |
||
349 | } |
||
350 | |||
351 | if(!is_null($rev) && $rev['date'] >= $lastupdate) { |
||
352 | // Some change was not a minor one and not by myself |
||
353 | $change_ids[] = $rev['id']; |
||
354 | } |
||
355 | } |
||
356 | |||
357 | // send it |
||
358 | if($style === 'digest') { |
||
359 | foreach($change_ids as $change_id) { |
||
360 | $this->send_digest( |
||
361 | $USERINFO['mail'], $change_id, |
||
362 | $lastupdate |
||
363 | ); |
||
364 | $count++; |
||
365 | } |
||
366 | } elseif($style === 'list') { |
||
367 | $this->send_list($USERINFO['mail'], $change_ids, $target); |
||
368 | $count++; |
||
369 | } |
||
370 | // TODO: Handle duplicate subscriptions. |
||
371 | |||
372 | // Update notification time. |
||
373 | $this->add($target, $user, $style, time()); |
||
374 | } |
||
375 | $this->unlock($target); |
||
376 | } |
||
377 | |||
378 | // restore current user info |
||
379 | $USERINFO = $olduinfo; |
||
380 | $INPUT->server->set('REMOTE_USER',$olduser); |
||
381 | return $count; |
||
382 | } |
||
383 | |||
384 | /** |
||
385 | * Send the diff for some page change |
||
386 | * |
||
387 | * @param string $subscriber_mail The target mail address |
||
388 | * @param string $template Mail template ('subscr_digest', 'subscr_single', 'mailtext', ...) |
||
389 | * @param string $id Page for which the notification is |
||
390 | * @param int|null $rev Old revision if any |
||
391 | * @param string $summary Change summary if any |
||
392 | * @return bool true if successfully sent |
||
393 | */ |
||
394 | public function send_diff($subscriber_mail, $template, $id, $rev = null, $summary = '') { |
||
395 | global $DIFF_INLINESTYLES; |
||
396 | |||
397 | // prepare replacements (keys not set in hrep will be taken from trep) |
||
398 | $trep = array( |
||
399 | 'PAGE' => $id, |
||
400 | 'NEWPAGE' => wl($id, '', true, '&'), |
||
401 | 'SUMMARY' => $summary, |
||
402 | 'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&') |
||
403 | ); |
||
404 | $hrep = array(); |
||
405 | |||
406 | if($rev) { |
||
407 | $subject = 'changed'; |
||
408 | $trep['OLDPAGE'] = wl($id, "rev=$rev", true, '&'); |
||
409 | |||
410 | $old_content = rawWiki($id, $rev); |
||
411 | $new_content = rawWiki($id); |
||
412 | |||
413 | $df = new Diff(explode("\n", $old_content), |
||
414 | explode("\n", $new_content)); |
||
415 | $dformat = new UnifiedDiffFormatter(); |
||
416 | $tdiff = $dformat->format($df); |
||
417 | |||
418 | $DIFF_INLINESTYLES = true; |
||
419 | $df = new Diff(explode("\n", $old_content), |
||
420 | explode("\n", $new_content)); |
||
421 | $dformat = new InlineDiffFormatter(); |
||
422 | $hdiff = $dformat->format($df); |
||
423 | $hdiff = '<table>'.$hdiff.'</table>'; |
||
424 | $DIFF_INLINESTYLES = false; |
||
425 | } else { |
||
426 | $subject = 'newpage'; |
||
427 | $trep['OLDPAGE'] = '---'; |
||
428 | $tdiff = rawWiki($id); |
||
429 | $hdiff = nl2br(hsc($tdiff)); |
||
430 | } |
||
431 | |||
432 | $trep['DIFF'] = $tdiff; |
||
433 | $hrep['DIFF'] = $hdiff; |
||
434 | |||
435 | $headers = array('Message-Id' => $this->getMessageID($id)); |
||
436 | if ($rev) { |
||
437 | $headers['In-Reply-To'] = $this->getMessageID($id, $rev); |
||
438 | } |
||
439 | |||
440 | return $this->send( |
||
441 | $subscriber_mail, $subject, $id, |
||
442 | $template, $trep, $hrep, $headers |
||
443 | ); |
||
444 | } |
||
445 | |||
446 | /** |
||
447 | * Send the diff for some media change |
||
448 | * |
||
449 | * @fixme this should embed thumbnails of images in HTML version |
||
450 | * |
||
451 | * @param string $subscriber_mail The target mail address |
||
452 | * @param string $template Mail template ('uploadmail', ...) |
||
453 | * @param string $id Media file for which the notification is |
||
454 | * @param int|bool $rev Old revision if any |
||
455 | */ |
||
456 | public function send_media_diff($subscriber_mail, $template, $id, $rev = false) { |
||
457 | global $conf; |
||
458 | |||
459 | $file = mediaFN($id); |
||
460 | list($mime, /* $ext */) = mimetype($id); |
||
461 | |||
462 | $trep = array( |
||
463 | 'MIME' => $mime, |
||
464 | 'MEDIA' => ml($id,'',true,'&',true), |
||
465 | 'SIZE' => filesize_h(filesize($file)), |
||
466 | ); |
||
467 | |||
468 | if ($rev && $conf['mediarevisions']) { |
||
469 | $trep['OLD'] = ml($id, "rev=$rev", true, '&', true); |
||
470 | } else { |
||
471 | $trep['OLD'] = '---'; |
||
472 | } |
||
473 | |||
474 | $headers = array('Message-Id' => $this->getMessageID($id, @filemtime($file))); |
||
475 | if ($rev) { |
||
476 | $headers['In-Reply-To'] = $this->getMessageID($id, $rev); |
||
477 | } |
||
478 | |||
479 | $this->send($subscriber_mail, 'upload', $id, $template, $trep, null, $headers); |
||
480 | |||
481 | } |
||
482 | |||
483 | /** |
||
484 | * Send a notify mail on new registration |
||
485 | * |
||
486 | * @author Andreas Gohr <[email protected]> |
||
487 | * |
||
488 | * @param string $login login name of the new user |
||
489 | * @param string $fullname full name of the new user |
||
490 | * @param string $email email address of the new user |
||
491 | * @return bool true if a mail was sent |
||
492 | */ |
||
493 | public function send_register($login, $fullname, $email) { |
||
494 | global $conf; |
||
495 | if(empty($conf['registernotify'])) return false; |
||
496 | |||
497 | $trep = array( |
||
498 | 'NEWUSER' => $login, |
||
499 | 'NEWNAME' => $fullname, |
||
500 | 'NEWEMAIL' => $email, |
||
501 | ); |
||
502 | |||
503 | return $this->send( |
||
504 | $conf['registernotify'], |
||
505 | 'new_user', |
||
506 | $login, |
||
507 | 'registermail', |
||
508 | $trep |
||
509 | ); |
||
510 | } |
||
511 | |||
512 | /** |
||
513 | * Send a digest mail |
||
514 | * |
||
515 | * Sends a digest mail showing a bunch of changes of a single page. Basically the same as send_diff() |
||
516 | * but determines the last known revision first |
||
517 | * |
||
518 | * @author Adrian Lang <[email protected]> |
||
519 | * |
||
520 | * @param string $subscriber_mail The target mail address |
||
521 | * @param string $id The ID |
||
522 | * @param int $lastupdate Time of the last notification |
||
523 | * @return bool |
||
524 | */ |
||
525 | protected function send_digest($subscriber_mail, $id, $lastupdate) { |
||
526 | $pagelog = new PageChangeLog($id); |
||
527 | $n = 0; |
||
528 | do { |
||
529 | $rev = $pagelog->getRevisions($n++, 1); |
||
530 | $rev = (count($rev) > 0) ? $rev[0] : null; |
||
531 | } while(!is_null($rev) && $rev > $lastupdate); |
||
532 | |||
533 | return $this->send_diff( |
||
534 | $subscriber_mail, |
||
535 | 'subscr_digest', |
||
536 | $id, $rev |
||
537 | ); |
||
538 | } |
||
539 | |||
540 | /** |
||
541 | * Send a list mail |
||
542 | * |
||
543 | * Sends a list mail showing a list of changed pages. |
||
544 | * |
||
545 | * @author Adrian Lang <[email protected]> |
||
546 | * |
||
547 | * @param string $subscriber_mail The target mail address |
||
548 | * @param array $ids Array of ids |
||
549 | * @param string $ns_id The id of the namespace |
||
550 | * @return bool true if a mail was sent |
||
551 | */ |
||
552 | protected function send_list($subscriber_mail, $ids, $ns_id) { |
||
553 | if(count($ids) === 0) return false; |
||
554 | |||
555 | $tlist = ''; |
||
556 | $hlist = '<ul>'; |
||
557 | foreach($ids as $id) { |
||
558 | $link = wl($id, array(), true); |
||
559 | $tlist .= '* '.$link.NL; |
||
560 | $hlist .= '<li><a href="'.$link.'">'.hsc($id).'</a></li>'.NL; |
||
561 | } |
||
562 | $hlist .= '</ul>'; |
||
563 | |||
564 | $id = prettyprint_id($ns_id); |
||
565 | $trep = array( |
||
566 | 'DIFF' => rtrim($tlist), |
||
567 | 'PAGE' => $id, |
||
568 | 'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&') |
||
569 | ); |
||
570 | $hrep = array( |
||
571 | 'DIFF' => $hlist |
||
572 | ); |
||
573 | |||
574 | return $this->send( |
||
575 | $subscriber_mail, |
||
576 | 'subscribe_list', |
||
577 | $ns_id, |
||
578 | 'subscr_list', $trep, $hrep |
||
579 | ); |
||
580 | } |
||
581 | |||
582 | /** |
||
583 | * Helper function for sending a mail |
||
584 | * |
||
585 | * @author Adrian Lang <[email protected]> |
||
586 | * |
||
587 | * @param string $subscriber_mail The target mail address |
||
588 | * @param string $subject The lang id of the mail subject (without the |
||
589 | * prefix “mail_”) |
||
590 | * @param string $context The context of this mail, eg. page or namespace id |
||
591 | * @param string $template The name of the mail template |
||
592 | * @param array $trep Predefined parameters used to parse the |
||
593 | * template (in text format) |
||
594 | * @param array $hrep Predefined parameters used to parse the |
||
595 | * template (in HTML format), null to default to $trep |
||
596 | * @param array $headers Additional mail headers in the form 'name' => 'value' |
||
597 | * @return bool |
||
598 | */ |
||
599 | protected function send($subscriber_mail, $subject, $context, $template, $trep, $hrep = null, $headers = array()) { |
||
600 | global $lang; |
||
601 | global $conf; |
||
602 | |||
603 | $text = rawLocale($template); |
||
604 | $subject = $lang['mail_'.$subject].' '.$context; |
||
605 | $mail = new Mailer(); |
||
606 | $mail->bcc($subscriber_mail); |
||
607 | $mail->subject($subject); |
||
608 | $mail->setBody($text, $trep, $hrep); |
||
609 | if(in_array($template, array('subscr_list', 'subscr_digest'))){ |
||
610 | $mail->from($conf['mailfromnobody']); |
||
611 | } |
||
612 | if(isset($trep['SUBSCRIBE'])) { |
||
613 | $mail->setHeader('List-Unsubscribe', '<'.$trep['SUBSCRIBE'].'>', false); |
||
614 | } |
||
615 | |||
616 | foreach ($headers as $header => $value) { |
||
617 | $mail->setHeader($header, $value); |
||
618 | } |
||
619 | |||
620 | return $mail->send(); |
||
621 | } |
||
622 | |||
623 | /** |
||
624 | * Get a valid message id for a certain $id and revision (or the current revision) |
||
625 | * |
||
626 | * @param string $id The id of the page (or media file) the message id should be for |
||
627 | * @param string $rev The revision of the page, set to the current revision of the page $id if not set |
||
628 | * @return string |
||
629 | */ |
||
630 | protected function getMessageID($id, $rev = null) { |
||
631 | static $listid = null; |
||
632 | if (is_null($listid)) { |
||
633 | $server = parse_url(DOKU_URL, PHP_URL_HOST); |
||
634 | $listid = join('.', array_reverse(explode('/', DOKU_BASE))).$server; |
||
635 | $listid = urlencode($listid); |
||
636 | $listid = strtolower(trim($listid, '.')); |
||
637 | } |
||
638 | |||
639 | if (is_null($rev)) { |
||
640 | $rev = @filemtime(wikiFN($id)); |
||
641 | } |
||
642 | |||
643 | return "<$id?rev=$rev@$listid>"; |
||
644 | } |
||
645 | |||
646 | /** |
||
647 | * Default callback for COMMON_NOTIFY_ADDRESSLIST |
||
648 | * |
||
649 | * Aggregates all email addresses of user who have subscribed the given page with 'every' style |
||
650 | * |
||
651 | * @author Steven Danz <[email protected]> |
||
652 | * @author Adrian Lang <[email protected]> |
||
653 | * |
||
654 | * @todo move the whole functionality into this class, trigger SUBSCRIPTION_NOTIFY_ADDRESSLIST instead, |
||
655 | * use an array for the addresses within it |
||
656 | * |
||
657 | * @param array &$data Containing the entries: |
||
658 | * - $id (the page id), |
||
659 | * - $self (whether the author should be notified, |
||
660 | * - $addresslist (current email address list) |
||
661 | * - $replacements (array of additional string substitutions, @KEY@ to be replaced by value) |
||
662 | */ |
||
663 | public function notifyaddresses(&$data) { |
||
664 | if(!$this->isenabled()) return; |
||
665 | |||
666 | /** @var DokuWiki_Auth_Plugin $auth */ |
||
667 | global $auth; |
||
668 | global $conf; |
||
669 | /** @var Input $INPUT */ |
||
670 | global $INPUT; |
||
671 | |||
672 | $id = $data['id']; |
||
673 | $self = $data['self']; |
||
674 | $addresslist = $data['addresslist']; |
||
675 | |||
676 | $subscriptions = $this->subscribers($id, null, 'every'); |
||
677 | |||
678 | $result = array(); |
||
679 | foreach($subscriptions as $target => $users) { |
||
680 | foreach($users as $user => $info) { |
||
681 | $userinfo = $auth->getUserData($user); |
||
682 | if($userinfo === false) continue; |
||
683 | if(!$userinfo['mail']) continue; |
||
684 | if(!$self && $user == $INPUT->server->str('REMOTE_USER')) continue; //skip our own changes |
||
685 | |||
686 | $level = auth_aclcheck($id, $user, $userinfo['grps']); |
||
687 | if($level >= AUTH_READ) { |
||
688 | if(strcasecmp($userinfo['mail'], $conf['notify']) != 0) { //skip user who get notified elsewhere |
||
689 | $result[$user] = $userinfo['mail']; |
||
690 | } |
||
691 | } |
||
692 | } |
||
693 | } |
||
694 | $data['addresslist'] = trim($addresslist.','.implode(',', $result), ','); |
||
695 | } |
||
696 | } |
||
697 |
Let’s assume that you have a directory layout like this:
and let’s assume the following content of
Bar.php
:If both files
OtherDir/Foo.php
andSomeDir/Foo.php
are loaded in the same runtime, you will see a PHP error such as the following:PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php
However, as
OtherDir/Foo.php
does not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/Bar.php
, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias: