693 lines
23 KiB
PHP
693 lines
23 KiB
PHP
<?php
|
||
/**
|
||
* Class for handling (email) subscriptions
|
||
*
|
||
* @author Adrian Lang <lang@cosmocode.de>
|
||
* @author Andreas Gohr <andi@splitbrain.org>
|
||
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
||
*/
|
||
class Subscription {
|
||
|
||
/**
|
||
* Check if subscription system is enabled
|
||
*
|
||
* @return bool
|
||
*/
|
||
public function isenabled() {
|
||
return actionOK('subscribe');
|
||
}
|
||
|
||
/**
|
||
* Return the subscription meta file for the given ID
|
||
*
|
||
* @author Adrian Lang <lang@cosmocode.de>
|
||
*
|
||
* @param string $id The target page or namespace, specified by id; Namespaces
|
||
* are identified by appending a colon.
|
||
* @return string
|
||
*/
|
||
protected function file($id) {
|
||
$meta_fname = '.mlist';
|
||
if((substr($id, -1, 1) === ':')) {
|
||
$meta_froot = getNS($id);
|
||
$meta_fname = '/'.$meta_fname;
|
||
} else {
|
||
$meta_froot = $id;
|
||
}
|
||
return metaFN((string) $meta_froot, $meta_fname);
|
||
}
|
||
|
||
/**
|
||
* Lock subscription info
|
||
*
|
||
* We don't use io_lock() her because we do not wait for the lock and use a larger stale time
|
||
*
|
||
* @author Adrian Lang <lang@cosmocode.de>
|
||
* @param string $id The target page or namespace, specified by id; Namespaces
|
||
* are identified by appending a colon.
|
||
* @return bool true, if you got a succesful lock
|
||
*/
|
||
protected function lock($id) {
|
||
global $conf;
|
||
|
||
$lock = $conf['lockdir'].'/_subscr_'.md5($id).'.lock';
|
||
|
||
if(is_dir($lock) && time() - @filemtime($lock) > 60 * 5) {
|
||
// looks like a stale lock - remove it
|
||
@rmdir($lock);
|
||
}
|
||
|
||
// try creating the lock directory
|
||
if(!@mkdir($lock, $conf['dmode'])) {
|
||
return false;
|
||
}
|
||
|
||
if(!empty($conf['dperm'])) chmod($lock, $conf['dperm']);
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Unlock subscription info
|
||
*
|
||
* @author Adrian Lang <lang@cosmocode.de>
|
||
* @param string $id The target page or namespace, specified by id; Namespaces
|
||
* are identified by appending a colon.
|
||
* @return bool
|
||
*/
|
||
protected function unlock($id) {
|
||
global $conf;
|
||
$lock = $conf['lockdir'].'/_subscr_'.md5($id).'.lock';
|
||
return @rmdir($lock);
|
||
}
|
||
|
||
/**
|
||
* Construct a regular expression for parsing a subscription definition line
|
||
*
|
||
* @author Andreas Gohr <andi@splitbrain.org>
|
||
*
|
||
* @param string|array $user
|
||
* @param string|array $style
|
||
* @param string|array $data
|
||
* @return string complete regexp including delimiters
|
||
* @throws Exception when no data is passed
|
||
*/
|
||
protected function buildregex($user = null, $style = null, $data = null) {
|
||
// always work with arrays
|
||
$user = (array) $user;
|
||
$style = (array) $style;
|
||
$data = (array) $data;
|
||
|
||
// clean
|
||
$user = array_filter(array_map('trim', $user));
|
||
$style = array_filter(array_map('trim', $style));
|
||
$data = array_filter(array_map('trim', $data));
|
||
|
||
// user names are encoded
|
||
$user = array_map('auth_nameencode', $user);
|
||
|
||
// quote
|
||
$user = array_map('preg_quote_cb', $user);
|
||
$style = array_map('preg_quote_cb', $style);
|
||
$data = array_map('preg_quote_cb', $data);
|
||
|
||
// join
|
||
$user = join('|', $user);
|
||
$style = join('|', $style);
|
||
$data = join('|', $data);
|
||
|
||
// any data at all?
|
||
if($user.$style.$data === '') throw new Exception('no data passed');
|
||
|
||
// replace empty values, set which ones are optional
|
||
$sopt = '';
|
||
$dopt = '';
|
||
if($user === '') {
|
||
$user = '\S+';
|
||
}
|
||
if($style === '') {
|
||
$style = '\S+';
|
||
$sopt = '?';
|
||
}
|
||
if($data === '') {
|
||
$data = '\S+';
|
||
$dopt = '?';
|
||
}
|
||
|
||
// assemble
|
||
return "/^($user)(?:\\s+($style))$sopt(?:\\s+($data))$dopt$/";
|
||
}
|
||
|
||
/**
|
||
* Recursively search for matching subscriptions
|
||
*
|
||
* This function searches all relevant subscription files for a page or
|
||
* namespace.
|
||
*
|
||
* @author Adrian Lang <lang@cosmocode.de>
|
||
*
|
||
* @param string $page The target object’s (namespace or page) id
|
||
* @param string|array $user
|
||
* @param string|array $style
|
||
* @param string|array $data
|
||
* @return array
|
||
*/
|
||
public function subscribers($page, $user = null, $style = null, $data = null) {
|
||
if(!$this->isenabled()) return array();
|
||
|
||
// Construct list of files which may contain relevant subscriptions.
|
||
$files = array(':' => $this->file(':'));
|
||
do {
|
||
$files[$page] = $this->file($page);
|
||
$page = getNS(rtrim($page, ':')).':';
|
||
} while($page !== ':');
|
||
|
||
$re = $this->buildregex($user, $style, $data);
|
||
|
||
// Handle files.
|
||
$result = array();
|
||
foreach($files as $target => $file) {
|
||
if(!file_exists($file)) continue;
|
||
|
||
$lines = file($file);
|
||
foreach($lines as $line) {
|
||
// fix old style subscription files
|
||
if(strpos($line, ' ') === false) $line = trim($line)." every\n";
|
||
|
||
// check for matching entries
|
||
if(!preg_match($re, $line, $m)) continue;
|
||
|
||
$u = rawurldecode($m[1]); // decode the user name
|
||
if(!isset($result[$target])) $result[$target] = array();
|
||
$result[$target][$u] = array($m[2], $m[3]); // add to result
|
||
}
|
||
}
|
||
return array_reverse($result);
|
||
}
|
||
|
||
/**
|
||
* Adds a new subscription for the given page or namespace
|
||
*
|
||
* This will automatically overwrite any existent subscription for the given user on this
|
||
* *exact* page or namespace. It will *not* modify any subscription that may exist in higher namespaces.
|
||
*
|
||
* @param string $id The target page or namespace, specified by id; Namespaces
|
||
* are identified by appending a colon.
|
||
* @param string $user
|
||
* @param string $style
|
||
* @param string $data
|
||
* @throws Exception when user or style is empty
|
||
* @return bool
|
||
*/
|
||
public function add($id, $user, $style, $data = '') {
|
||
if(!$this->isenabled()) return false;
|
||
|
||
// delete any existing subscription
|
||
$this->remove($id, $user);
|
||
|
||
$user = auth_nameencode(trim($user));
|
||
$style = trim($style);
|
||
$data = trim($data);
|
||
|
||
if(!$user) throw new Exception('no subscription user given');
|
||
if(!$style) throw new Exception('no subscription style given');
|
||
if(!$data) $data = time(); //always add current time for new subscriptions
|
||
|
||
$line = "$user $style $data\n";
|
||
$file = $this->file($id);
|
||
return io_saveFile($file, $line, true);
|
||
}
|
||
|
||
/**
|
||
* Removes a subscription for the given page or namespace
|
||
*
|
||
* This removes all subscriptions matching the given criteria on the given page or
|
||
* namespace. It will *not* modify any subscriptions that may exist in higher
|
||
* namespaces.
|
||
*
|
||
* @param string $id The target object’s (namespace or page) id
|
||
* @param string|array $user
|
||
* @param string|array $style
|
||
* @param string|array $data
|
||
* @return bool
|
||
*/
|
||
public function remove($id, $user = null, $style = null, $data = null) {
|
||
if(!$this->isenabled()) return false;
|
||
|
||
$file = $this->file($id);
|
||
if(!file_exists($file)) return true;
|
||
|
||
$re = $this->buildregex($user, $style, $data);
|
||
return io_deleteFromFile($file, $re, true);
|
||
}
|
||
|
||
/**
|
||
* Get data for $INFO['subscribed']
|
||
*
|
||
* $INFO['subscribed'] is either false if no subscription for the current page
|
||
* and user is in effect. Else it contains an array of arrays with the fields
|
||
* “target”, “style”, and optionally “data”.
|
||
*
|
||
* @param string $id Page ID, defaults to global $ID
|
||
* @param string $user User, defaults to $_SERVER['REMOTE_USER']
|
||
* @return array
|
||
* @author Adrian Lang <lang@cosmocode.de>
|
||
*/
|
||
function user_subscription($id = '', $user = '') {
|
||
if(!$this->isenabled()) return false;
|
||
|
||
global $ID;
|
||
/** @var Input $INPUT */
|
||
global $INPUT;
|
||
if(!$id) $id = $ID;
|
||
if(!$user) $user = $INPUT->server->str('REMOTE_USER');
|
||
|
||
$subs = $this->subscribers($id, $user);
|
||
if(!count($subs)) return false;
|
||
|
||
$result = array();
|
||
foreach($subs as $target => $info) {
|
||
$result[] = array(
|
||
'target' => $target,
|
||
'style' => $info[$user][0],
|
||
'data' => $info[$user][1]
|
||
);
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* Send digest and list subscriptions
|
||
*
|
||
* This sends mails to all subscribers that have a subscription for namespaces above
|
||
* the given page if the needed $conf['subscribe_time'] has passed already.
|
||
*
|
||
* This function is called form lib/exe/indexer.php
|
||
*
|
||
* @param string $page
|
||
* @return int number of sent mails
|
||
*/
|
||
public function send_bulk($page) {
|
||
if(!$this->isenabled()) return 0;
|
||
|
||
/** @var DokuWiki_Auth_Plugin $auth */
|
||
global $auth;
|
||
global $conf;
|
||
global $USERINFO;
|
||
/** @var Input $INPUT */
|
||
global $INPUT;
|
||
$count = 0;
|
||
|
||
$subscriptions = $this->subscribers($page, null, array('digest', 'list'));
|
||
|
||
// remember current user info
|
||
$olduinfo = $USERINFO;
|
||
$olduser = $INPUT->server->str('REMOTE_USER');
|
||
|
||
foreach($subscriptions as $target => $users) {
|
||
if(!$this->lock($target)) continue;
|
||
|
||
foreach($users as $user => $info) {
|
||
list($style, $lastupdate) = $info;
|
||
|
||
$lastupdate = (int) $lastupdate;
|
||
if($lastupdate + $conf['subscribe_time'] > time()) {
|
||
// Less than the configured time period passed since last
|
||
// update.
|
||
continue;
|
||
}
|
||
|
||
// Work as the user to make sure ACLs apply correctly
|
||
$USERINFO = $auth->getUserData($user);
|
||
$INPUT->server->set('REMOTE_USER',$user);
|
||
if($USERINFO === false) continue;
|
||
if(!$USERINFO['mail']) continue;
|
||
|
||
if(substr($target, -1, 1) === ':') {
|
||
// subscription target is a namespace, get all changes within
|
||
$changes = getRecentsSince($lastupdate, null, getNS($target));
|
||
} else {
|
||
// single page subscription, check ACL ourselves
|
||
if(auth_quickaclcheck($target) < AUTH_READ) continue;
|
||
$meta = p_get_metadata($target);
|
||
$changes = array($meta['last_change']);
|
||
}
|
||
|
||
// Filter out pages only changed in small and own edits
|
||
$change_ids = array();
|
||
foreach($changes as $rev) {
|
||
$n = 0;
|
||
while(!is_null($rev) && $rev['date'] >= $lastupdate &&
|
||
($INPUT->server->str('REMOTE_USER') === $rev['user'] ||
|
||
$rev['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT)) {
|
||
$pagelog = new PageChangeLog($rev['id']);
|
||
$rev = $pagelog->getRevisions($n++, 1);
|
||
$rev = (count($rev) > 0) ? $rev[0] : null;
|
||
}
|
||
|
||
if(!is_null($rev) && $rev['date'] >= $lastupdate) {
|
||
// Some change was not a minor one and not by myself
|
||
$change_ids[] = $rev['id'];
|
||
}
|
||
}
|
||
|
||
// send it
|
||
if($style === 'digest') {
|
||
foreach($change_ids as $change_id) {
|
||
$this->send_digest(
|
||
$USERINFO['mail'], $change_id,
|
||
$lastupdate
|
||
);
|
||
$count++;
|
||
}
|
||
} elseif($style === 'list') {
|
||
$this->send_list($USERINFO['mail'], $change_ids, $target);
|
||
$count++;
|
||
}
|
||
// TODO: Handle duplicate subscriptions.
|
||
|
||
// Update notification time.
|
||
$this->add($target, $user, $style, time());
|
||
}
|
||
$this->unlock($target);
|
||
}
|
||
|
||
// restore current user info
|
||
$USERINFO = $olduinfo;
|
||
$INPUT->server->set('REMOTE_USER',$olduser);
|
||
return $count;
|
||
}
|
||
|
||
/**
|
||
* Send the diff for some page change
|
||
*
|
||
* @param string $subscriber_mail The target mail address
|
||
* @param string $template Mail template ('subscr_digest', 'subscr_single', 'mailtext', ...)
|
||
* @param string $id Page for which the notification is
|
||
* @param int|null $rev Old revision if any
|
||
* @param string $summary Change summary if any
|
||
* @return bool true if successfully sent
|
||
*/
|
||
public function send_diff($subscriber_mail, $template, $id, $rev = null, $summary = '') {
|
||
global $DIFF_INLINESTYLES;
|
||
|
||
// prepare replacements (keys not set in hrep will be taken from trep)
|
||
$trep = array(
|
||
'PAGE' => $id,
|
||
'NEWPAGE' => wl($id, '', true, '&'),
|
||
'SUMMARY' => $summary,
|
||
'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&')
|
||
);
|
||
$hrep = array();
|
||
|
||
if($rev) {
|
||
$subject = 'changed';
|
||
$trep['OLDPAGE'] = wl($id, "rev=$rev", true, '&');
|
||
|
||
$old_content = rawWiki($id, $rev);
|
||
$new_content = rawWiki($id);
|
||
|
||
$df = new Diff(explode("\n", $old_content),
|
||
explode("\n", $new_content));
|
||
$dformat = new UnifiedDiffFormatter();
|
||
$tdiff = $dformat->format($df);
|
||
|
||
$DIFF_INLINESTYLES = true;
|
||
$df = new Diff(explode("\n", $old_content),
|
||
explode("\n", $new_content));
|
||
$dformat = new InlineDiffFormatter();
|
||
$hdiff = $dformat->format($df);
|
||
$hdiff = '<table>'.$hdiff.'</table>';
|
||
$DIFF_INLINESTYLES = false;
|
||
} else {
|
||
$subject = 'newpage';
|
||
$trep['OLDPAGE'] = '---';
|
||
$tdiff = rawWiki($id);
|
||
$hdiff = nl2br(hsc($tdiff));
|
||
}
|
||
|
||
$trep['DIFF'] = $tdiff;
|
||
$hrep['DIFF'] = $hdiff;
|
||
|
||
$headers = array('Message-Id' => $this->getMessageID($id));
|
||
if ($rev) {
|
||
$headers['In-Reply-To'] = $this->getMessageID($id, $rev);
|
||
}
|
||
|
||
return $this->send(
|
||
$subscriber_mail, $subject, $id,
|
||
$template, $trep, $hrep, $headers
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Send the diff for some media change
|
||
*
|
||
* @fixme this should embed thumbnails of images in HTML version
|
||
*
|
||
* @param string $subscriber_mail The target mail address
|
||
* @param string $template Mail template ('uploadmail', ...)
|
||
* @param string $id Media file for which the notification is
|
||
* @param int|bool $rev Old revision if any
|
||
*/
|
||
public function send_media_diff($subscriber_mail, $template, $id, $rev = false) {
|
||
global $conf;
|
||
|
||
$file = mediaFN($id);
|
||
list($mime, /* $ext */) = mimetype($id);
|
||
|
||
$trep = array(
|
||
'MIME' => $mime,
|
||
'MEDIA' => ml($id,'',true,'&',true),
|
||
'SIZE' => filesize_h(filesize($file)),
|
||
);
|
||
|
||
if ($rev && $conf['mediarevisions']) {
|
||
$trep['OLD'] = ml($id, "rev=$rev", true, '&', true);
|
||
} else {
|
||
$trep['OLD'] = '---';
|
||
}
|
||
|
||
$headers = array('Message-Id' => $this->getMessageID($id, @filemtime($file)));
|
||
if ($rev) {
|
||
$headers['In-Reply-To'] = $this->getMessageID($id, $rev);
|
||
}
|
||
|
||
$this->send($subscriber_mail, 'upload', $id, $template, $trep, null, $headers);
|
||
|
||
}
|
||
|
||
/**
|
||
* Send a notify mail on new registration
|
||
*
|
||
* @author Andreas Gohr <andi@splitbrain.org>
|
||
*
|
||
* @param string $login login name of the new user
|
||
* @param string $fullname full name of the new user
|
||
* @param string $email email address of the new user
|
||
* @return bool true if a mail was sent
|
||
*/
|
||
public function send_register($login, $fullname, $email) {
|
||
global $conf;
|
||
if(empty($conf['registernotify'])) return false;
|
||
|
||
$trep = array(
|
||
'NEWUSER' => $login,
|
||
'NEWNAME' => $fullname,
|
||
'NEWEMAIL' => $email,
|
||
);
|
||
|
||
return $this->send(
|
||
$conf['registernotify'],
|
||
'new_user',
|
||
$login,
|
||
'registermail',
|
||
$trep
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Send a digest mail
|
||
*
|
||
* Sends a digest mail showing a bunch of changes of a single page. Basically the same as send_diff()
|
||
* but determines the last known revision first
|
||
*
|
||
* @author Adrian Lang <lang@cosmocode.de>
|
||
*
|
||
* @param string $subscriber_mail The target mail address
|
||
* @param string $id The ID
|
||
* @param int $lastupdate Time of the last notification
|
||
* @return bool
|
||
*/
|
||
protected function send_digest($subscriber_mail, $id, $lastupdate) {
|
||
$pagelog = new PageChangeLog($id);
|
||
$n = 0;
|
||
do {
|
||
$rev = $pagelog->getRevisions($n++, 1);
|
||
$rev = (count($rev) > 0) ? $rev[0] : null;
|
||
} while(!is_null($rev) && $rev > $lastupdate);
|
||
|
||
return $this->send_diff(
|
||
$subscriber_mail,
|
||
'subscr_digest',
|
||
$id, $rev
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Send a list mail
|
||
*
|
||
* Sends a list mail showing a list of changed pages.
|
||
*
|
||
* @author Adrian Lang <lang@cosmocode.de>
|
||
*
|
||
* @param string $subscriber_mail The target mail address
|
||
* @param array $ids Array of ids
|
||
* @param string $ns_id The id of the namespace
|
||
* @return bool true if a mail was sent
|
||
*/
|
||
protected function send_list($subscriber_mail, $ids, $ns_id) {
|
||
if(count($ids) === 0) return false;
|
||
|
||
$tlist = '';
|
||
$hlist = '<ul>';
|
||
foreach($ids as $id) {
|
||
$link = wl($id, array(), true);
|
||
$tlist .= '* '.$link.NL;
|
||
$hlist .= '<li><a href="'.$link.'">'.hsc($id).'</a></li>'.NL;
|
||
}
|
||
$hlist .= '</ul>';
|
||
|
||
$id = prettyprint_id($ns_id);
|
||
$trep = array(
|
||
'DIFF' => rtrim($tlist),
|
||
'PAGE' => $id,
|
||
'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&')
|
||
);
|
||
$hrep = array(
|
||
'DIFF' => $hlist
|
||
);
|
||
|
||
return $this->send(
|
||
$subscriber_mail,
|
||
'subscribe_list',
|
||
$ns_id,
|
||
'subscr_list', $trep, $hrep
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Helper function for sending a mail
|
||
*
|
||
* @author Adrian Lang <lang@cosmocode.de>
|
||
*
|
||
* @param string $subscriber_mail The target mail address
|
||
* @param string $subject The lang id of the mail subject (without the
|
||
* prefix “mail_”)
|
||
* @param string $context The context of this mail, eg. page or namespace id
|
||
* @param string $template The name of the mail template
|
||
* @param array $trep Predefined parameters used to parse the
|
||
* template (in text format)
|
||
* @param array $hrep Predefined parameters used to parse the
|
||
* template (in HTML format), null to default to $trep
|
||
* @param array $headers Additional mail headers in the form 'name' => 'value'
|
||
* @return bool
|
||
*/
|
||
protected function send($subscriber_mail, $subject, $context, $template, $trep, $hrep = null, $headers = array()) {
|
||
global $lang;
|
||
global $conf;
|
||
|
||
$text = rawLocale($template);
|
||
$subject = $lang['mail_'.$subject].' '.$context;
|
||
$mail = new Mailer();
|
||
$mail->bcc($subscriber_mail);
|
||
$mail->subject($subject);
|
||
$mail->setBody($text, $trep, $hrep);
|
||
if(in_array($template, array('subscr_list', 'subscr_digest'))){
|
||
$mail->from($conf['mailfromnobody']);
|
||
}
|
||
if(isset($trep['SUBSCRIBE'])) {
|
||
$mail->setHeader('List-Unsubscribe', '<'.$trep['SUBSCRIBE'].'>', false);
|
||
}
|
||
|
||
foreach ($headers as $header => $value) {
|
||
$mail->setHeader($header, $value);
|
||
}
|
||
|
||
return $mail->send();
|
||
}
|
||
|
||
/**
|
||
* Get a valid message id for a certain $id and revision (or the current revision)
|
||
*
|
||
* @param string $id The id of the page (or media file) the message id should be for
|
||
* @param string $rev The revision of the page, set to the current revision of the page $id if not set
|
||
* @return string
|
||
*/
|
||
protected function getMessageID($id, $rev = null) {
|
||
static $listid = null;
|
||
if (is_null($listid)) {
|
||
$server = parse_url(DOKU_URL, PHP_URL_HOST);
|
||
$listid = join('.', array_reverse(explode('/', DOKU_BASE))).$server;
|
||
$listid = urlencode($listid);
|
||
$listid = strtolower(trim($listid, '.'));
|
||
}
|
||
|
||
if (is_null($rev)) {
|
||
$rev = @filemtime(wikiFN($id));
|
||
}
|
||
|
||
return "<$id?rev=$rev@$listid>";
|
||
}
|
||
|
||
/**
|
||
* Default callback for COMMON_NOTIFY_ADDRESSLIST
|
||
*
|
||
* Aggregates all email addresses of user who have subscribed the given page with 'every' style
|
||
*
|
||
* @author Steven Danz <steven-danz@kc.rr.com>
|
||
* @author Adrian Lang <lang@cosmocode.de>
|
||
*
|
||
* @todo move the whole functionality into this class, trigger SUBSCRIPTION_NOTIFY_ADDRESSLIST instead,
|
||
* use an array for the addresses within it
|
||
*
|
||
* @param array &$data Containing the entries:
|
||
* - $id (the page id),
|
||
* - $self (whether the author should be notified,
|
||
* - $addresslist (current email address list)
|
||
* - $replacements (array of additional string substitutions, @KEY@ to be replaced by value)
|
||
*/
|
||
public function notifyaddresses(&$data) {
|
||
if(!$this->isenabled()) return;
|
||
|
||
/** @var DokuWiki_Auth_Plugin $auth */
|
||
global $auth;
|
||
global $conf;
|
||
/** @var Input $INPUT */
|
||
global $INPUT;
|
||
|
||
$id = $data['id'];
|
||
$self = $data['self'];
|
||
$addresslist = $data['addresslist'];
|
||
|
||
$subscriptions = $this->subscribers($id, null, 'every');
|
||
|
||
$result = array();
|
||
foreach($subscriptions as $target => $users) {
|
||
foreach($users as $user => $info) {
|
||
$userinfo = $auth->getUserData($user);
|
||
if($userinfo === false) continue;
|
||
if(!$userinfo['mail']) continue;
|
||
if(!$self && $user == $INPUT->server->str('REMOTE_USER')) continue; //skip our own changes
|
||
|
||
$level = auth_aclcheck($id, $user, $userinfo['grps']);
|
||
if($level >= AUTH_READ) {
|
||
if(strcasecmp($userinfo['mail'], $conf['notify']) != 0) { //skip user who get notified elsewhere
|
||
$result[$user] = $userinfo['mail'];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
$data['addresslist'] = trim($addresslist.','.implode(',', $result), ',');
|
||
}
|
||
}
|