Send mails with quota and fix opting out

This commit is contained in:
Carl Schwan 2021-09-02 14:08:30 +02:00
Родитель f7402757fb
Коммит 29a5a53ccb
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 06B35D38387B67BE
11 изменённых файлов: 160 добавлений и 41 удалений

Просмотреть файл

@ -80,6 +80,7 @@ class OptoutController extends Controller {
/**
* @PublicPage
* @NoCSRFRequired
* @UserRateThrottle
* @param bool $token
* @param bool $optedOut
@ -94,12 +95,17 @@ class OptoutController extends Controller {
]);
}
public function updateOptingOut(string $token, bool $optedOut): TemplateResponse {
/**
* @PublicPage
* @UserRateThrottle
* @param bool $token
* @return DataResponse
*/
public function updateOptingOut(string $token): Response {
try {
$this->service->uptadeOptedOutByToken($token, $optedOut);
return new PublicTemplateResponse($this->appName, 'public-opting-out', [
'token' => $token,
'optedOut' => $optedOut,
$this->service->uptadeOptedOutByToken($token, true);
return new DataResponse([
'done' => true
]);
} catch (NotFoundException $exception) {
return new NotFoundResponse();

Просмотреть файл

@ -48,29 +48,35 @@ class NotificationTrackerMapper extends QBMapper {
/**
* @throws \OCP\DB\Exception
*/
public function uptadeOptedOutByToken(string $token, bool $optedOut) {
public function updateOptedOutByToken(string $token, bool $optedOut): void {
$qb = $this->db->getQueryBuilder();
$qb->update($this->getTableName(), 'o')
->set('o.opted_out', $qb->createNamedParameter($optedOut))
->where('o.secret_token', $qb->createNamedParameter($token))
->executeStatement();
$qb->update($this->getTableName())
->set('opted_out', $qb->createNamedParameter($optedOut))
->where($qb->expr()->eq('secret_token', $qb->createNamedParameter($token)));
$qb->executeStatement();
}
/**
* @param \DateTimeInterface $date
* @return NotificationTracker[]
* @throws \OCP\DB\Exception
*/
public function findAllOlderThan(\DateTimeInterface $date, int $limit): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where(
$qb->expr()->lt('lastSendNotification', $qb->createNamedParameter($date->getTimestamp()))
)
->setMaxResults($limit);
try {
$qb->select('*')
->from($this->getTableName())
->where(
$qb->expr()->gt('lastSendNotification', $date->getTimestamp())
)
->andWhere(
$qb->expr()->eq('opted_out', false)
)
->setMaxResults($limit);
} catch (\Exception $e) {
echo 'rerre';
}
return $this->findEntities($qb);
}

Просмотреть файл

@ -29,12 +29,12 @@ namespace OCA\MonthlyNotifications\Jobs;
use OCA\MonthlyNotifications\Service\NotificationTrackerService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;
use OCP\Files\FileInfo;
use OCP\IConfig;
use OCP\IUser;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\Mail\IEMailTemplate;
use OCP\Mail\IMailer;
use OCP\User\Backend\IGetRealUIDBackend;
class SendNotifications extends TimedJob {
/** @var NotificationTrackerService $service */
@ -52,29 +52,42 @@ class SendNotifications extends TimedJob {
* @var IMailer
*/
private $mailer;
/**
* @var IL10N
*/
private $l;
/**
* @var IURLGenerator
*/
private $generator;
public function __construct($appName,
ITimeFactory $time,
NotificationTrackerService $service,
IUserManager $userManager,
IConfig $config,
IMailer $mailer) {
$this->setInterval(60 * 60 * 3); // every 3 hours
IMailer $mailer,
IL10N $l,
IURLGenerator $generator) {
parent::__construct($time);
//$this->setInterval(60 * 60 * 3); // every 3 hours
$this->service = $service;
$this->userManager = $userManager;
$this->config = $config;
$this->appName = $appName;
$this->mailer = $mailer;
$this->l = $l;
$this->generator = $generator;
}
/**
* @inheritDoc
*/
protected function run($argument): void {
$limit = $this->config->getAppValue($this->appName, 'max_mail_sent', 100);
$limit = (int)$this->config->getAppValue($this->appName, 'max_mail_sent', 100);
$trackedNotifications = $this->service->findAllOlderThan(new \DateTime("-1 month"), $limit);
//$trackedNotifications = $this->service->findAllOlderThan(new \DateTime("-1 month"), $limit);
$trackedNotifications = $this->service->findAllOlderThan(new \DateTime('now'), $limit);
foreach ($trackedNotifications as $trackedNotification) {
$message = $this->mailer->createMessage();
$user = $this->userManager->get($trackedNotification->getUserId());
@ -89,9 +102,39 @@ class SendNotifications extends TimedJob {
$emailTemplate = $this->mailer->createEMailTemplate('quote.notification');
$emailTemplate->addHeader();
$emailTemplate->addHeading('Welcome aboard');
$emailTemplate->addBodyText('Quota: ' . $user->getQuota());
$emailTemplate->addFooter('Optional footer text');
$emailTemplate->addHeading('Hello ' . $user->getDisplayName());
// make sure FS is setup before querying storage related stuff...
\OC_Util::setupFS($user->getUID());
$storageInfo = \OC_Helper::getStorageInfo('/');
if ($storageInfo['quota'] === FileInfo::SPACE_UNLIMITED) {
$totalSpace = $this->l->t('Unlimited');
} else {
$totalSpace = \OC_Helper::humanFileSize($storageInfo['total']);
}
$usedSpace = \OC_Helper::humanFileSize($storageInfo['used']);
if ($storageInfo['quota'] !== FileInfo::SPACE_UNLIMITED) {
$quota = \OC_Helper::humanFileSize($storageInfo['quota']);
$emailTemplate->addBodyText(
$this->l->t('You are currently using <strong>' . $usedSpace . '</strong> of <strong>' . $quota . '</strong>.'),
$this->l->t('You are currently using ' . $usedSpace . ' of ' . $quota . '.')
);
if ($storageInfo['usage_relative'] > 80) {
// todo display warning
$emailTemplate->addBodyText($this->l->t('You are using over 80% of your quota.'));
}
} else {
$emailTemplate->addBodyText(
$this->l->t('You are currently using <strong>' . $usedSpace . '</strong> of storage.'),
$this->l->t('You are currently using ' . $usedSpace . ' of storage.')
);
}
$emailTemplate->addFooter($this->l->t('You can unsubscribe by clicking on this link: <a href="%s">Unsubscribe</a>', [
$this->generator->getAbsoluteURL($this->generator->linkToRoute($this->appName.'.optout.displayOptingOutPage', [
'token' => $trackedNotification->getSecretToken()
]))
]));
$message->useTemplate($emailTemplate);
$this->mailer->send($message);
}

Просмотреть файл

@ -44,7 +44,6 @@ class Version23000Date2021090112000001 extends SimpleMigrationStep {
$table->addColumn('id', 'integer', [
'autoincrement' => true,
'notnull' => true,
'unique' => true,
]);
$table->addColumn('user_id', 'string', [
'notnull' => true,
@ -60,8 +59,8 @@ class Version23000Date2021090112000001 extends SimpleMigrationStep {
$table->addColumn('secret_token', 'string', [
'notnull' => true,
'default' => false,
'unique' => true,
]);
$table->addUniqueIndex(['user_id', 'secret_token']);
$table->setPrimaryKey(['id']);
}

Просмотреть файл

@ -60,7 +60,7 @@ class NotificationTrackerService {
try {
return $this->mapper->find($userId);
} catch(\Exception $e) {
$this->handleException($e);
return $this->create($userId, false, time());
}
}
@ -139,7 +139,7 @@ class NotificationTrackerService {
public function uptadeOptedOutByToken(string $token, bool $optedOut) {
try {
$this->mapper->uptadeOptedOutByToken($token, $optedOut);
$this->mapper->updateOptedOutByToken($token, $optedOut);
} catch (Exception $e) {
$this->handleException($e);
}

Просмотреть файл

@ -24,14 +24,39 @@ declare(strict_types=1);
namespace OCA\MonthlyNotifications\Settings;
use OCA\MonthlyNotifications\Service\NotificationTrackerService;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
use OCP\IUserSession;
use OCP\Settings\ISettings;
class PersonalSettings implements ISettings {
/**
* @var IInitialState
*/
private $initialState;
/**
* @var NotificationTrackerService
*/
private $service;
/**
* @var IUserSession
*/
private $userSession;
public function __construct(IInitialState $initialState,
NotificationTrackerService $service,
IUserSession $userSession) {
$this->initialState = $initialState;
$this->service = $service;
$this->userSession = $userSession;
}
/**
* @return TemplateResponse
*/
public function getForm(): TemplateResponse {
$this->initialState->provideInitialState('opted-out', $this->service->find($this->userSession->getUser()->getUID())->getOptedOut());
return new TemplateResponse('monthly_notifications', 'settings-personal', []);
}

Просмотреть файл

@ -71,16 +71,13 @@
]
},
"dependencies": {
"@juliushaertl/vue-richtext": "^0.3.3",
"@nextcloud/auth": "^1.3.0",
"@nextcloud/axios": "^1.6.0",
"@nextcloud/l10n": "^1.4.1",
"@nextcloud/logger": "^2.0.0",
"@nextcloud/moment": "^1.1.1",
"@nextcloud/paths": "^2.0.0",
"@nextcloud/router": "^2.0.0",
"@nextcloud/initial-state": "^2.0.0",
"@nextcloud/vue": "^3.9.0",
"@nextcloud/vue-dashboard": "^2.0.1",
"vue": "^2.6.12"
},
"devDependencies": {

Просмотреть файл

@ -6,9 +6,9 @@
</p>
<p>
<input id="send-notifications"
v-model="sendNotifications"
type="checkbox"
class="checkbox"
v-model="sendNotification">
class="checkbox">
<label for="send-notifications">{{ t('monthly_notifications', 'Send monthly summary') }}</label>
</p>
</div>
@ -17,23 +17,24 @@
<script>
import { generateUrl } from '@nextcloud/router'
import axios from '@nextcloud/axios'
import { loadState } from '@nextcloud/initial-state'
export default {
name: 'PersonalSettings',
data() {
return {
sendNotification: false,
sendNotifications: !loadState('monthly_notifications', 'opted-out', false),
}
},
watch: {
sendNotification() {
sendNotifications() {
this.saveSetting()
},
},
methods: {
async saveSetting() {
const data = {
optedOut: !this.sendNotification,
optedOut: !this.sendNotifications,
}
await axios.post(generateUrl('/apps/monthly_notifications/') + 'update', data)
},

33
src/main-public-optout.js Normal file
Просмотреть файл

@ -0,0 +1,33 @@
/**
* @copyright Copyright (c) 2021 Carl Schwan <carl@carlschwan.eu>
*
* @author Carl Schwan <carl@carlschwan.eu>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
import axios from '@nextcloud/axios'
import { generateUrl } from '@nextcloud/router'
(function() {
const queryString = window.location.search
const urlParams = new URLSearchParams(queryString)
axios.post(generateUrl('/apps/monthly_notifications/') + 'optout', {
token: urlParams.get('token'),
})
})()

Просмотреть файл

@ -22,5 +22,13 @@ declare(strict_types=1);
*
*/
script('monthly_notifications', 'send-opt-out');
/** @var \OCP\IL10N $l */
/** @var array $_ */
script('monthly_notifications', 'monthly_notifications-publicOptout');
?>
<div class="section">
<h2><?php echo $l->t('Monthly status update'); ?></h2>
<p><?php echo $l->t('You just unsubscribed from the monthly status update. You can any time subscribe again from your account.') ?></p>
</div>

Просмотреть файл

@ -24,6 +24,7 @@ const webpackConfig = require('@nextcloud/webpack-vue-config')
webpackConfig.entry = {
personalSettings: path.join(__dirname, 'src', 'main-personal-settings.js'),
publicOptout: path.join(__dirname, 'src', 'main-public-optout.js'),
}
module.exports = webpackConfig