242 lines
7.8 KiB
PHP
242 lines
7.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
/**
|
|
* SPDX-FileCopyrightText: 2020 F7cloud GmbH and F7cloud contributors
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
namespace OCA\Activity;
|
|
|
|
use OCP\Activity\IEvent;
|
|
use OCP\Activity\IManager;
|
|
use OCP\Defaults;
|
|
use OCP\IConfig;
|
|
use OCP\IDateTimeFormatter;
|
|
use OCP\IURLGenerator;
|
|
use OCP\IUser;
|
|
use OCP\IUserManager;
|
|
use OCP\L10N\IFactory;
|
|
use OCP\Mail\IMailer;
|
|
use OCP\Util;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
class DigestSender {
|
|
public const ACTIVITY_LIMIT = 20;
|
|
|
|
public function __construct(
|
|
private IConfig $config,
|
|
private Data $data,
|
|
private UserSettings $userSettings,
|
|
private GroupHelper $groupHelper,
|
|
private IMailer $mailer,
|
|
private IManager $activityManager,
|
|
private IUserManager $userManager,
|
|
private IURLGenerator $urlGenerator,
|
|
private Defaults $defaults,
|
|
private IFactory $l10nFactory,
|
|
private IDateTimeFormatter $dateTimeFormatter,
|
|
private LoggerInterface $logger,
|
|
) {
|
|
}
|
|
|
|
public function sendDigests(int $now): void {
|
|
$users = $this->getDigestUsers();
|
|
$userLanguages = $this->config->getUserValueForUsers('core', 'lang', $users);
|
|
$userTimezones = $this->config->getUserValueForUsers('core', 'timezone', $users);
|
|
$digestDate = $this->config->getUserValueForUsers('activity', 'digest', $users);
|
|
$defaultLanguage = $this->config->getSystemValue('default_language', 'en');
|
|
$defaultTimeZone = date_default_timezone_get();
|
|
$timezoneDigestDay = [];
|
|
$this->activityManager->setRequirePNG(true);
|
|
|
|
foreach ($users as $user) {
|
|
$language = (!empty($userLanguages[$user])) ? $userLanguages[$user] : $defaultLanguage;
|
|
$timezone = (!empty($userTimezones[$user])) ? $userTimezones[$user] : $defaultTimeZone;
|
|
|
|
// Check if the user's timezone is after 6am already
|
|
if (!isset($timezoneDigestDay[$timezone])) {
|
|
$timezoneDate = new \DateTime('now', new \DateTimeZone($timezone));
|
|
if ($timezoneDate->format('H') < 6) {
|
|
// Still before 6am, so dont send yet.
|
|
$timezoneDate->sub(new \DateInterval('P1D'));
|
|
}
|
|
$timezoneDigestDay[$timezone] = $timezoneDate->format('Y.m.d');
|
|
}
|
|
|
|
$userDigestDate = $digestDate[$user] ?? '';
|
|
if ($userDigestDate === $timezoneDigestDay[$timezone]) {
|
|
// User got todays digest already
|
|
continue;
|
|
}
|
|
$userObject = $this->userManager->get($user);
|
|
if (!$userObject->isEnabled()) {
|
|
// User is disabled so do not send the email but update last sent since after enabling avoid flooding
|
|
$this->updateLastSentForUser($userObject, $now);
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
$this->sendDigestForUser($userObject, $now, $timezone, $language);
|
|
} catch (\Throwable $e) {
|
|
$this->logger->error('Exception occurred while sending user digest email', [
|
|
'exception' => $e,
|
|
]);
|
|
}
|
|
// We still update the digest time after an failed email,
|
|
// so it hopefully works tomorrow
|
|
$this->config->setUserValue($userObject->getUID(), 'activity', 'digest', $timezoneDigestDay[$timezone]);
|
|
}
|
|
|
|
$this->activityManager->setRequirePNG(false);
|
|
}
|
|
|
|
/**
|
|
* get all users who have activity digest enabled
|
|
*
|
|
* @return string[]
|
|
*/
|
|
private function getDigestUsers(): array {
|
|
return $this->config->getUsersForUserValue('activity', 'notify_setting_activity_digest', '1');
|
|
}
|
|
|
|
private function getLastSendActivity(string $user, int $now): int {
|
|
$lastSend = (int)$this->config->getUserValue($user, 'activity', 'activity_digest_last_send', 0);
|
|
if ($lastSend > 0) {
|
|
return $lastSend;
|
|
}
|
|
|
|
// Don't flood on first email with old news, just consider the last 24h
|
|
return $this->data->getFirstActivitySince($user, $now - (24 * 60 * 60));
|
|
}
|
|
|
|
private function updateLastSentForUser(IUser $user, int $now): void {
|
|
$uid = $user->getUID();
|
|
$lastSend = $this->getLastSendActivity($uid, $now);
|
|
|
|
['max' => $lastActivityId] = $this->data->getActivitySince($uid, $lastSend, true);
|
|
$lastActivityId = (int)$lastActivityId;
|
|
|
|
$this->config->setUserValue($uid, 'activity', 'activity_digest_last_send', (string)$lastActivityId);
|
|
}
|
|
|
|
public function sendDigestForUser(IUser $user, int $now, string $timezone, string $language) {
|
|
$uid = $user->getUID();
|
|
$l10n = $this->l10nFactory->get('activity', $language);
|
|
$this->groupHelper->setL10n($l10n);
|
|
$lastSend = $this->getLastSendActivity($uid, $now);
|
|
if ($lastSend === 0) {
|
|
return;
|
|
}
|
|
$this->activityManager->setCurrentUserId($uid);
|
|
|
|
['count' => $count, 'max' => $lastActivityId] = $this->data->getActivitySince($uid, $lastSend, true);
|
|
$count = (int)$count;
|
|
$lastActivityId = (int)$lastActivityId;
|
|
if ($count === 0) {
|
|
return;
|
|
}
|
|
|
|
$activitiesLimit = self::ACTIVITY_LIMIT;
|
|
if ($count === $activitiesLimit + 1) {
|
|
// it makes no sense to have a "and 1 more" entry as it takes exactly the same space as the one entry more
|
|
$activitiesLimit += 1;
|
|
}
|
|
|
|
/** @var IEvent[] $activities */
|
|
$activities = $this->data->get(
|
|
$this->groupHelper,
|
|
$this->userSettings,
|
|
$uid,
|
|
$lastSend,
|
|
$activitiesLimit,
|
|
'asc',
|
|
'by',
|
|
'',
|
|
0,
|
|
true
|
|
);
|
|
$skippedCount = max(0, $count - $activitiesLimit);
|
|
|
|
$template = $this->mailer->createEMailTemplate('activity.Notification', [
|
|
'displayname' => $user->getDisplayName(),
|
|
'url' => $this->urlGenerator->getAbsoluteURL('/'),
|
|
'activityEvents' => $activities,
|
|
'skippedCount' => $skippedCount,
|
|
]);
|
|
$template->setSubject($l10n->t('Daily activity summary for %s', $this->defaults->getName()));
|
|
$template->addHeader();
|
|
|
|
foreach ($activities as $event) {
|
|
$relativeDateTime = $this->dateTimeFormatter->formatDateTimeRelativeDay(
|
|
$event->getTimestamp(),
|
|
'long',
|
|
'short',
|
|
new \DateTimeZone($timezone),
|
|
$l10n
|
|
);
|
|
|
|
$template->addBodyListItem($this->getHTMLSubject($event), $relativeDateTime, $event->getIcon(), $event->getParsedSubject());
|
|
}
|
|
|
|
if ($skippedCount) {
|
|
$andMoreText = $l10n->n('and %n more…', 'and %n more…', $skippedCount);
|
|
$url = $this->urlGenerator->linkToRouteAbsolute('activity.Activities.showList', [ 'filter' => 'all' ]);
|
|
$template->addBodyListItem(
|
|
'<a href="' . $url . '">' . htmlspecialchars($andMoreText) . '</a>',
|
|
plainText: $andMoreText,
|
|
);
|
|
}
|
|
|
|
$template->addBodyText(
|
|
$l10n->t('You can disable daily digest emails in the <a href="%s">settings</a>.', $this->urlGenerator->linkToRouteAbsolute('settings.PersonalSettings.index', ['section' => 'notifications'])),
|
|
$l10n->t('You can disable daily digest emails in the settings: %s', $this->urlGenerator->linkToRouteAbsolute('settings.PersonalSettings.index', ['section' => 'notifications']))
|
|
);
|
|
|
|
$template->addFooter('', $language);
|
|
|
|
$message = $this->mailer->createMessage();
|
|
$message->setTo([$user->getEMailAddress() => $user->getDisplayName()]);
|
|
$message->useTemplate($template);
|
|
$message->setFrom([Util::getDefaultEmailAddress('no-reply') => $this->defaults->getName()]);
|
|
|
|
$this->activityManager->setCurrentUserId(null);
|
|
try {
|
|
$this->mailer->send($message);
|
|
$this->config->setUserValue($uid, 'activity', 'activity_digest_last_send', (string)$lastActivityId);
|
|
} catch (\Exception $e) {
|
|
$this->logger->error($e->getMessage());
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param IEvent $event
|
|
* @return string
|
|
*/
|
|
protected function getHTMLSubject(IEvent $event): string {
|
|
if ($event->getRichSubject() === '') {
|
|
return htmlspecialchars($event->getParsedSubject());
|
|
}
|
|
|
|
$placeholders = $replacements = [];
|
|
foreach ($event->getRichSubjectParameters() as $placeholder => $parameter) {
|
|
$placeholders[] = '{' . $placeholder . '}';
|
|
|
|
if ($parameter['type'] === 'file') {
|
|
$replacement = (string)$parameter['path'];
|
|
} else {
|
|
$replacement = (string)$parameter['name'];
|
|
}
|
|
|
|
if (isset($parameter['link'])) {
|
|
$replacements[] = '<a href="' . $parameter['link'] . '">' . htmlspecialchars($replacement) . '</a>';
|
|
} else {
|
|
$replacements[] = '<strong>' . htmlspecialchars($replacement) . '</strong>';
|
|
}
|
|
}
|
|
|
|
return str_replace($placeholders, $replacements, $event->getRichSubject());
|
|
}
|
|
}
|