dokuwiki/inc/Subscriptions/BulkSubscriptionSender.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 ($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
);
}
}