262 lines
8.1 KiB
PHP
262 lines
8.1 KiB
PHP
|
<?php
|
||
|
|
||
|
|
||
|
namespace dokuwiki\Subscriptions;
|
||
|
|
||
|
|
||
|
use dokuwiki\ChangeLog\PageChangeLog;
|
||
|
use dokuwiki\Input\Input;
|
||
|
use DokuWiki_Auth_Plugin;
|
||
|
|
||
|
class BulkSubscriptionSender extends SubscriptionSender
|
||
|
{
|
||
|
|
||
|
/**
|
||
|
* 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 sendBulk($page)
|
||
|
{
|
||
|
$subscriberManager = new SubscriberManager();
|
||
|
if (!$subscriberManager->isenabled()) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/** @var DokuWiki_Auth_Plugin $auth */
|
||
|
global $auth;
|
||
|
global $conf;
|
||
|
global $USERINFO;
|
||
|
/** @var Input $INPUT */
|
||
|
global $INPUT;
|
||
|
$count = 0;
|
||
|
|
||
|
$subscriptions = $subscriberManager->subscribers($page, null, ['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 = [$meta['last_change']];
|
||
|
}
|
||
|
|
||
|
// Filter out pages only changed in small and own edits
|
||
|
$change_ids = [];
|
||
|
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->sendDigest(
|
||
|
$USERINFO['mail'],
|
||
|
$change_id,
|
||
|
$lastupdate
|
||
|
);
|
||
|
$count++;
|
||
|
}
|
||
|
} else {
|
||
|
if ($style === 'list') {
|
||
|
$this->sendList($USERINFO['mail'], $change_ids, $target);
|
||
|
$count++;
|
||
|
}
|
||
|
}
|
||
|
// TODO: Handle duplicate subscriptions.
|
||
|
|
||
|
// Update notification time.
|
||
|
$subscriberManager->add($target, $user, $style, time());
|
||
|
}
|
||
|
$this->unlock($target);
|
||
|
}
|
||
|
|
||
|
// restore current user info
|
||
|
$USERINFO = $olduinfo;
|
||
|
$INPUT->server->set('REMOTE_USER', $olduser);
|
||
|
return $count;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Lock subscription info
|
||
|
*
|
||
|
* We don't use io_lock() her because we do not wait for the lock and use a larger stale time
|
||
|
*
|
||
|
* @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
|
||
|
* @author Adrian Lang <lang@cosmocode.de>
|
||
|
*/
|
||
|
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
|
||
|
*
|
||
|
* @param string $id The target page or namespace, specified by id; Namespaces
|
||
|
* are identified by appending a colon.
|
||
|
*
|
||
|
* @return bool
|
||
|
* @author Adrian Lang <lang@cosmocode.de>
|
||
|
*/
|
||
|
protected function unlock($id)
|
||
|
{
|
||
|
global $conf;
|
||
|
$lock = $conf['lockdir'] . '/_subscr_' . md5($id) . '.lock';
|
||
|
return @rmdir($lock);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Send a digest mail
|
||
|
*
|
||
|
* Sends a digest mail showing a bunch of changes of a single page. Basically the same as sendPageDiff()
|
||
|
* but determines the last known revision first
|
||
|
*
|
||
|
* @param string $subscriber_mail The target mail address
|
||
|
* @param string $id The ID
|
||
|
* @param int $lastupdate Time of the last notification
|
||
|
*
|
||
|
* @return bool
|
||
|
* @author Adrian Lang <lang@cosmocode.de>
|
||
|
*
|
||
|
*/
|
||
|
protected function sendDigest($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);
|
||
|
|
||
|
// TODO I'm not happy with the following line and passing $this->mailer around. Not sure how to solve it better
|
||
|
$pageSubSender = new PageSubscriptionSender($this->mailer);
|
||
|
return $pageSubSender->sendPageDiff(
|
||
|
$subscriber_mail,
|
||
|
'subscr_digest',
|
||
|
$id,
|
||
|
$rev
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Send a list mail
|
||
|
*
|
||
|
* Sends a list mail showing a list of changed pages.
|
||
|
*
|
||
|
* @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
|
||
|
* @author Adrian Lang <lang@cosmocode.de>
|
||
|
*
|
||
|
*/
|
||
|
protected function sendList($subscriber_mail, $ids, $ns_id)
|
||
|
{
|
||
|
if (count($ids) === 0) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$tlist = '';
|
||
|
$hlist = '<ul>';
|
||
|
foreach ($ids as $id) {
|
||
|
$link = wl($id, [], true);
|
||
|
$tlist .= '* ' . $link . NL;
|
||
|
$hlist .= '<li><a href="' . $link . '">' . hsc($id) . '</a></li>' . NL;
|
||
|
}
|
||
|
$hlist .= '</ul>';
|
||
|
|
||
|
$id = prettyprint_id($ns_id);
|
||
|
$trep = [
|
||
|
'DIFF' => rtrim($tlist),
|
||
|
'PAGE' => $id,
|
||
|
'SUBSCRIBE' => wl($id, ['do' => 'subscribe'], true, '&'),
|
||
|
];
|
||
|
$hrep = [
|
||
|
'DIFF' => $hlist,
|
||
|
];
|
||
|
|
||
|
return $this->send(
|
||
|
$subscriber_mail,
|
||
|
'subscribe_list',
|
||
|
$ns_id,
|
||
|
'subscr_list',
|
||
|
$trep,
|
||
|
$hrep
|
||
|
);
|
||
|
}
|
||
|
}
|