From 00de78b6f1397ef53b794b898bd665c6c103e22e Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 15 Aug 2023 13:52:30 +0200 Subject: [PATCH] Also support Discord. --- app/Handlers/Events/Model/RuleHandler.php | 25 ++++--- app/Http/Controllers/Admin/HomeController.php | 3 +- .../Controllers/PreferencesController.php | 3 +- app/Notifications/Admin/TestNotification.php | 5 +- app/Notifications/Admin/UserInvitation.php | 5 +- app/Notifications/Admin/UserRegistration.php | 5 +- .../Admin/VersionCheckResult.php | 3 +- app/Notifications/User/BillReminder.php | 15 ++-- app/Notifications/User/NewAccessToken.php | 9 ++- app/Notifications/User/RuleActionFailed.php | 3 +- app/Notifications/User/UserLogin.php | 5 +- app/Support/Notifications/UrlValidator.php | 39 ++++++++++ config/firefly.php | 2 +- resources/assets/v2/boot/bootstrap.js | 1 + .../assets/v2/pages/transactions/create.js | 72 +++++++++++++++++++ .../transactions/shared/create-empty-split.js | 36 ++++++++++ resources/lang/en_US/firefly.php | 2 + 17 files changed, 200 insertions(+), 33 deletions(-) create mode 100644 app/Support/Notifications/UrlValidator.php create mode 100644 resources/assets/v2/pages/transactions/create.js create mode 100644 resources/assets/v2/pages/transactions/shared/create-empty-split.js diff --git a/app/Handlers/Events/Model/RuleHandler.php b/app/Handlers/Events/Model/RuleHandler.php index bbf63f33f7..933c08b0eb 100644 --- a/app/Handlers/Events/Model/RuleHandler.php +++ b/app/Handlers/Events/Model/RuleHandler.php @@ -26,6 +26,7 @@ namespace FireflyIII\Handlers\Events\Model; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject; use FireflyIII\Notifications\User\RuleActionFailed; +use FireflyIII\Support\Facades\Preferences; use Illuminate\Support\Facades\Notification; /** @@ -40,12 +41,16 @@ class RuleHandler */ public function ruleActionFailedOnArray(RuleActionFailedOnArray $event): void { - app('log')->debug('Now in ruleActionFailed'); $ruleAction = $event->ruleAction; $rule = $ruleAction->rule; - $journal = $event->journal; - $error = $event->error; - $user = $ruleAction->rule->user; + $preference = Preferences::getForUser($rule->user, 'notification_rule_action_failures', true)->data; + if (false === $preference) { + return; + } + app('log')->debug('Now in ruleActionFailedOnArray'); + $journal = $event->journal; + $error = $event->error; + $user = $ruleAction->rule->user; $mainMessage = trans('rules.main_message', ['rule' => $rule->title, 'action' => $ruleAction->action_type, 'group' => $journal['transaction_group_id'], 'error' => $error]); $groupTitle = $journal['description'] ?? ''; @@ -65,12 +70,16 @@ class RuleHandler */ public function ruleActionFailedOnObject(RuleActionFailedOnObject $event): void { - app('log')->debug('Now in ruleActionFailed'); $ruleAction = $event->ruleAction; $rule = $ruleAction->rule; - $journal = $event->journal; - $error = $event->error; - $user = $ruleAction->rule->user; + $preference = Preferences::getForUser($rule->user, 'notification_rule_action_failures', true)->data; + if (false === $preference) { + return; + } + app('log')->debug('Now in ruleActionFailedOnObject'); + $journal = $event->journal; + $error = $event->error; + $user = $ruleAction->rule->user; $mainMessage = trans('rules.main_message', ['rule' => $rule->title, 'action' => $ruleAction->action_type, 'group' => $journal->transaction_group_id, 'error' => $error]); $groupTitle = $journal->description ?? ''; diff --git a/app/Http/Controllers/Admin/HomeController.php b/app/Http/Controllers/Admin/HomeController.php index ca9292021a..38c5799cc6 100644 --- a/app/Http/Controllers/Admin/HomeController.php +++ b/app/Http/Controllers/Admin/HomeController.php @@ -27,6 +27,7 @@ use FireflyIII\Events\AdminRequestedTestMessage; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Middleware\IsDemoUser; use FireflyIII\Support\Facades\FireflyConfig; +use FireflyIII\Support\Notifications\UrlValidator; use FireflyIII\User; use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; @@ -99,7 +100,7 @@ class HomeController extends Controller if ('' === $url) { FireflyConfig::delete('slack_webhook_url'); } - if (str_starts_with($url, 'https://hooks.slack.com/services/')) { + if (UrlValidator::isValidWebhookURL($url)) { FireflyConfig::set('slack_webhook_url', $url); } diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index 0ae9480ccf..aec2828524 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -28,6 +28,7 @@ use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\Preference; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Support\Notifications\UrlValidator; use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -207,7 +208,7 @@ class PreferencesController extends Controller // slack URL: if (!auth()->user()->hasRole('demo')) { $url = (string)$request->get('slackUrl'); - if (str_starts_with($url, 'https://hooks.slack.com/services/')) { + if (UrlValidator::isValidWebhookURL($url)) { app('preferences')->set('slack_webhook_url', $url); } if ('' === $url) { diff --git a/app/Notifications/Admin/TestNotification.php b/app/Notifications/Admin/TestNotification.php index 0332f3519c..f4277b34cc 100644 --- a/app/Notifications/Admin/TestNotification.php +++ b/app/Notifications/Admin/TestNotification.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Notifications\Admin; +use FireflyIII\Support\Notifications\UrlValidator; use FireflyIII\User; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; @@ -99,9 +100,9 @@ class TestNotification extends Notification public function via($notifiable) { /** @var User|null $user */ - $user = auth()->user(); + $user = auth()->user(); $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; - if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) { + if (UrlValidator::isValidWebhookURL($slackUrl)) { return ['mail', 'slack']; } return ['mail']; diff --git a/app/Notifications/Admin/UserInvitation.php b/app/Notifications/Admin/UserInvitation.php index 9050b30863..19f4c5668a 100644 --- a/app/Notifications/Admin/UserInvitation.php +++ b/app/Notifications/Admin/UserInvitation.php @@ -25,6 +25,7 @@ declare(strict_types=1); namespace FireflyIII\Notifications\Admin; use FireflyIII\Models\InvitedUser; +use FireflyIII\Support\Notifications\UrlValidator; use FireflyIII\User; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; @@ -102,9 +103,9 @@ class UserInvitation extends Notification public function via($notifiable) { /** @var User|null $user */ - $user = auth()->user(); + $user = auth()->user(); $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; - if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) { + if (UrlValidator::isValidWebhookURL($slackUrl)) { return ['mail', 'slack']; } return ['mail']; diff --git a/app/Notifications/Admin/UserRegistration.php b/app/Notifications/Admin/UserRegistration.php index 8d0ebf5406..ab1174bd15 100644 --- a/app/Notifications/Admin/UserRegistration.php +++ b/app/Notifications/Admin/UserRegistration.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Notifications\Admin; +use FireflyIII\Support\Notifications\UrlValidator; use FireflyIII\User; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; @@ -99,9 +100,9 @@ class UserRegistration extends Notification public function via($notifiable) { /** @var User|null $user */ - $user = auth()->user(); + $user = auth()->user(); $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; - if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) { + if (UrlValidator::isValidWebhookURL($slackUrl)) { return ['mail', 'slack']; } return ['mail']; diff --git a/app/Notifications/Admin/VersionCheckResult.php b/app/Notifications/Admin/VersionCheckResult.php index 75b36ffb0e..3555f52538 100644 --- a/app/Notifications/Admin/VersionCheckResult.php +++ b/app/Notifications/Admin/VersionCheckResult.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Notifications\Admin; +use FireflyIII\Support\Notifications\UrlValidator; use FireflyIII\User; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; @@ -105,7 +106,7 @@ class VersionCheckResult extends Notification /** @var User|null $user */ $user = auth()->user(); $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; - if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) { + if (UrlValidator::isValidWebhookURL($slackUrl)) { return ['mail', 'slack']; } return ['mail']; diff --git a/app/Notifications/User/BillReminder.php b/app/Notifications/User/BillReminder.php index abe997248b..d3824aca52 100644 --- a/app/Notifications/User/BillReminder.php +++ b/app/Notifications/User/BillReminder.php @@ -25,6 +25,7 @@ declare(strict_types=1); namespace FireflyIII\Notifications\User; use FireflyIII\Models\Bill; +use FireflyIII\Support\Notifications\UrlValidator; use FireflyIII\User; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; @@ -38,8 +39,8 @@ class BillReminder extends Notification { use Queueable; - private Bill $bill; - private int $diff; + private Bill $bill; + private int $diff; private string $field; /** @@ -49,9 +50,9 @@ class BillReminder extends Notification */ public function __construct(Bill $bill, string $field, int $diff) { - $this->bill = $bill; + $this->bill = $bill; $this->field = $field; - $this->diff = $diff; + $this->diff = $diff; } /** @@ -101,7 +102,7 @@ class BillReminder extends Notification $message = (string)trans(sprintf('email.bill_warning_subject_now_%s', $this->field), ['diff' => $this->diff, 'name' => $this->bill->name]); } $bill = $this->bill; - $url = route('bills.show', [$bill->id]); + $url = route('bills.show', [$bill->id]); return (new SlackMessage()) ->warning() ->attachment(function ($attachment) use ($bill, $url) { @@ -120,9 +121,9 @@ class BillReminder extends Notification public function via($notifiable) { /** @var User|null $user */ - $user = auth()->user(); + $user = auth()->user(); $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; - if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) { + if (UrlValidator::isValidWebhookURL($slackUrl)) { return ['mail', 'slack']; } return ['mail']; diff --git a/app/Notifications/User/NewAccessToken.php b/app/Notifications/User/NewAccessToken.php index 69df8107a0..2279dc199b 100644 --- a/app/Notifications/User/NewAccessToken.php +++ b/app/Notifications/User/NewAccessToken.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Notifications\User; +use FireflyIII\Support\Notifications\UrlValidator; use FireflyIII\User; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; @@ -42,9 +43,7 @@ class NewAccessToken extends Notification * * @return void */ - public function __construct() - { - } + public function __construct() {} /** * Get the array representation of the notification. @@ -96,9 +95,9 @@ class NewAccessToken extends Notification public function via($notifiable) { /** @var User|null $user */ - $user = auth()->user(); + $user = auth()->user(); $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; - if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) { + if (UrlValidator::isValidWebhookURL($slackUrl)) { return ['mail', 'slack']; } return ['mail']; diff --git a/app/Notifications/User/RuleActionFailed.php b/app/Notifications/User/RuleActionFailed.php index ad2f12bcfe..caeef8a5b4 100644 --- a/app/Notifications/User/RuleActionFailed.php +++ b/app/Notifications/User/RuleActionFailed.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Notifications\User; +use FireflyIII\Support\Notifications\UrlValidator; use FireflyIII\User; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\SlackMessage; @@ -106,7 +107,7 @@ class RuleActionFailed extends Notification /** @var User|null $user */ $user = auth()->user(); $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; - if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) { + if (UrlValidator::isValidWebhookURL($slackUrl)) { app('log')->debug('Will send ruleActionFailed through Slack!'); return ['slack']; } diff --git a/app/Notifications/User/UserLogin.php b/app/Notifications/User/UserLogin.php index 4ede883cea..0e6ddd5cdb 100644 --- a/app/Notifications/User/UserLogin.php +++ b/app/Notifications/User/UserLogin.php @@ -25,6 +25,7 @@ declare(strict_types=1); namespace FireflyIII\Notifications\User; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Support\Notifications\UrlValidator; use FireflyIII\User; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; @@ -124,9 +125,9 @@ class UserLogin extends Notification public function via($notifiable) { /** @var User|null $user */ - $user = auth()->user(); + $user = auth()->user(); $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; - if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) { + if (UrlValidator::isValidWebhookURL($slackUrl)) { return ['mail', 'slack']; } return ['mail']; diff --git a/app/Support/Notifications/UrlValidator.php b/app/Support/Notifications/UrlValidator.php new file mode 100644 index 0000000000..b916271890 --- /dev/null +++ b/app/Support/Notifications/UrlValidator.php @@ -0,0 +1,39 @@ +. + */ + +namespace FireflyIII\Support\Notifications; + +/** + * Class UrlValidator + */ +class UrlValidator +{ + + /** + * @param string $url + * + * @return bool + */ + public static function isValidWebhookURL(string $url): bool + { + return str_starts_with($url, 'https://hooks.slack.com/services/') || str_starts_with($url, 'https://discord.com/api/webhooks/'); + } +} diff --git a/config/firefly.php b/config/firefly.php index d30a170711..e2d9d694fa 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -148,7 +148,7 @@ return [ 'update_minimum_age' => 7, // notifications - 'available_notifications' => ['bill_reminder', 'new_access_token', 'transaction_creation', 'user_login'], + 'available_notifications' => ['bill_reminder', 'new_access_token', 'transaction_creation', 'user_login', 'rule_action_failures'], 'admin_notifications' => ['admin_new_reg', 'user_new_reg', 'new_version', 'invite_created', 'invite_redeemed'], // enabled languages diff --git a/resources/assets/v2/boot/bootstrap.js b/resources/assets/v2/boot/bootstrap.js index a4b738b34b..984bb10e9e 100644 --- a/resources/assets/v2/boot/bootstrap.js +++ b/resources/assets/v2/boot/bootstrap.js @@ -29,6 +29,7 @@ import axios from 'axios'; import store from "store"; import observePlugin from 'store/plugins/observe'; import Alpine from "alpinejs"; +import * as bootstrap from 'bootstrap' store.addPlugin(observePlugin); window.store = store; diff --git a/resources/assets/v2/pages/transactions/create.js b/resources/assets/v2/pages/transactions/create.js new file mode 100644 index 0000000000..622b041334 --- /dev/null +++ b/resources/assets/v2/pages/transactions/create.js @@ -0,0 +1,72 @@ +/* + * create.js + * Copyright (c) 2023 james@firefly-iii.org + * + * This file is part of Firefly III (https://github.com/firefly-iii). + * + * 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 . + */ + +import '../../boot/bootstrap.js'; +import dates from '../../pages/shared/dates.js'; +import {createEmptySplit} from "./shared/create-empty-split.js"; +import formatMoney from "../../util/format-money.js"; + +let transactions = function () { + return { + count: 0, + totalAmount: 0, + entries: [], + init() { + this.entries.push(createEmptySplit()); + console.log('Ik ben init hoera'); + }, + addSplit() { + this.entries.push(createEmptySplit()); + }, + removeSplit(index) { + this.entries.splice(index, 1); + // fall back to index 0 + const triggerFirstTabEl = document.querySelector('#split-0-tab') + triggerFirstTabEl.click(); + //bootstrap.Tab.getInstance(triggerFirstTabEl).show() // Select first tab + }, + formattedTotalAmount() { + return formatMoney(this.totalAmount, 'EUR'); + } + } +} + +let comps = {transactions, dates}; + +function loadPage() { + Object.keys(comps).forEach(comp => { + console.log(`Loading page component "${comp}"`); + let data = comps[comp](); + Alpine.data(comp, () => data); + }); + Alpine.start(); +} + + +// wait for load until bootstrapped event is received. +document.addEventListener('firefly-iii-bootstrapped', () => { + console.log('Loaded through event listener.'); + loadPage(); +}); +// or is bootstrapped before event is triggered. +if (window.bootstrapped) { + console.log('Loaded through window variable.'); + loadPage(); +} diff --git a/resources/assets/v2/pages/transactions/shared/create-empty-split.js b/resources/assets/v2/pages/transactions/shared/create-empty-split.js new file mode 100644 index 0000000000..3947f396e5 --- /dev/null +++ b/resources/assets/v2/pages/transactions/shared/create-empty-split.js @@ -0,0 +1,36 @@ +/* + * create-empty-split.js + * Copyright (c) 2023 james@firefly-iii.org + * + * This file is part of Firefly III (https://github.com/firefly-iii). + * + * 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 . + */ + + +function getAccount() { + return { + id: '', + name: '', + }; +} + +export function createEmptySplit() { + return { + description: 'I am descr', + amount: '', + source_account: getAccount(), + destination_account: getAccount(), + }; +} diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index c8aed71d9f..7d08015d9d 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -1355,6 +1355,7 @@ return [ 'pref_notification_new_access_token' => 'Alert when a new API access token is created', 'pref_notification_transaction_creation' => 'Alert when a transaction is created automatically', 'pref_notification_user_login' => 'Alert when you login from a new location', + 'pref_notification_rule_action_failures' => 'Alert when rule actions fail to execute (Slack or Discord only)', 'pref_notifications' => 'Notifications', 'pref_notifications_help' => 'Indicate if these are notifications you would like to get. Some notifications may contain sensitive financial information.', 'slack_webhook_url' => 'Slack Webhook URL', @@ -2291,6 +2292,7 @@ return [ 'created_tag' => 'Tag ":tag" has been created!', 'transaction_journal_information' => 'Transaction information', + 'transaction_journal_amount' => 'Amount information', 'transaction_journal_meta' => 'Meta information', 'transaction_journal_more' => 'More information', 'basic_journal_information' => 'Basic transaction information',