Expand UI for notifications.

This commit is contained in:
James Cole
2024-12-07 08:05:51 +01:00
parent 1220564f30
commit 26948a058a
13 changed files with 2834 additions and 2622 deletions

View File

@@ -67,22 +67,24 @@ class HomeController extends Controller
// admin notification settings: // admin notification settings:
$notifications = []; $notifications = [];
foreach (config('firefly.admin_notifications') as $item) { foreach (config('notifications.notifications.owner') as $key => $info) {
$notifications[$item] = app('fireflyconfig')->get(sprintf('notification_%s', $item), true)->data; if($info['enabled']) {
$notifications[$key] = app('fireflyconfig')->get(sprintf('notification_%s', $key), true)->data;
}
} }
$slackUrl = app('fireflyconfig')->get('slack_webhook_url', '')->data; //
return view('admin.index', compact('title', 'mainTitleIcon', 'email', 'notifications', 'slackUrl')); return view('admin.index', compact('title', 'mainTitleIcon', 'email', 'notifications'));
} }
public function notifications(Request $request): RedirectResponse public function notifications(Request $request): RedirectResponse
{ {
foreach (config('firefly.admin_notifications') as $item) { foreach (config('notifications.notifications.owner') as $key => $info) {
$value = false; $value = false;
if ($request->has(sprintf('notification_%s', $item))) { if ($request->has(sprintf('notification_%s', $key))) {
$value = true; $value = true;
} }
app('fireflyconfig')->set(sprintf('notification_%s', $item), $value); app('fireflyconfig')->set(sprintf('notification_%s', $key), $value);
} }
$url = (string)$request->get('slackUrl'); $url = (string)$request->get('slackUrl');
if ('' === $url) { if ('' === $url) {

View File

@@ -0,0 +1,44 @@
<?php
/*
* NotificationController.php
* Copyright (c) 2024 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 https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Admin;
use FireflyIII\Http\Controllers\Controller;
use Illuminate\Support\Facades\Log;
class NotificationController extends Controller
{
public function index()
{
Log::channel('audit')->info('User visits notifications index.');
$title = (string) trans('firefly.administration');
$mainTitleIcon = 'fa-hand-spock-o';
$subTitle = (string) trans('firefly.title_owner_notifications');
$subTitleIcon = 'envelope-o';
$slackUrl = app('fireflyconfig')->get('slack_webhook_url', '')->data;
$discordUrl = app('fireflyconfig')->get('discord_webhook_url', '')->data;
$channels = config('notifications.channels');
return view('admin.notifications.index', compact('title', 'subTitle', 'mainTitleIcon', 'subTitleIcon', 'channels', 'slackUrl','discordUrl'));
}
}

View File

@@ -111,6 +111,7 @@ class PreferencesController extends Controller
// notification preferences (single value for each): // notification preferences (single value for each):
$notifications = []; $notifications = [];
die('fix the reference to the available notifications.');
foreach (config('firefly.available_notifications') as $notification) { foreach (config('firefly.available_notifications') as $notification) {
$notifications[$notification] = app('preferences')->get(sprintf('notification_%s', $notification), true)->data; $notifications[$notification] = app('preferences')->get(sprintf('notification_%s', $notification), true)->data;
} }
@@ -165,6 +166,7 @@ class PreferencesController extends Controller
// extract notifications: // extract notifications:
$all = $request->all(); $all = $request->all();
die('fix the reference to the available notifications.');
foreach (config('firefly.available_notifications') as $option) { foreach (config('firefly.available_notifications') as $option) {
$key = sprintf('notification_%s', $option); $key = sprintf('notification_%s', $option);
if (array_key_exists($key, $all)) { if (array_key_exists($key, $all)) {

View File

@@ -147,9 +147,7 @@ return [
'update_endpoint' => 'https://version.firefly-iii.org/index.json', 'update_endpoint' => 'https://version.firefly-iii.org/index.json',
'update_minimum_age' => 7, 'update_minimum_age' => 7,
// notifications
'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 // enabled languages
'languages' => [ 'languages' => [

54
config/notifications.php Normal file
View File

@@ -0,0 +1,54 @@
<?php
/*
* notifications.php
* Copyright (c) 2024 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 https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
return [
'channels' => [
'email' => ['enabled' => true, 'ui_configurable' => 0,],
'slack' => ['enabled' => true, 'ui_configurable' => 1,],
'discord' => ['enabled' => true, 'ui_configurable' => 1,],
'nfty' => ['enabled' => false, 'ui_configurable' => 0,],
'pushover' => ['enabled' => false, 'ui_configurable' => 0,],
'gotify' => ['enabled' => false, 'ui_configurable' => 0,],
'pushbullet' => ['enabled' => false, 'ui_configurable' => 0,],
],
'notifications' => [
'user' => [
'some_notification' => [
'enabled' => true,
'email' => '',
'slack' => '',
],
],
'owner' => [
//'invitation_created' => ['enabled' => true],
// 'some_notification' => ['enabled' => true],
'admin_new_reg' => ['enabled' => true],
'user_new_reg' => ['enabled' => true],
'new_version' => ['enabled' => true],
'invite_created' => ['enabled' => true],
'invite_redeemed' => ['enabled' => true],
],
],
// // notifications
// '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'],
];

View File

@@ -86,4 +86,7 @@ return [
'mfa_enableMFA' => 'Enable multi-factor authentication', 'mfa_enableMFA' => 'Enable multi-factor authentication',
'mfa_backup_codes' => 'Backup codes', 'mfa_backup_codes' => 'Backup codes',
'mfa_disableMFA' => 'Disable multi-factor authentication', 'mfa_disableMFA' => 'Disable multi-factor authentication',
// notifications
'notification_index' => 'Owner notifications',
]; ];

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,7 @@
</li> </li>
<li><a href="{{ route('admin.links.index') }}">{{ 'journal_link_configuration'|_ }}</a></li> <li><a href="{{ route('admin.links.index') }}">{{ 'journal_link_configuration'|_ }}</a></li>
<li><a href="{{ route('admin.update-check') }}">{{ 'update_check_title'|_ }}</a></li> <li><a href="{{ route('admin.update-check') }}">{{ 'update_check_title'|_ }}</a></li>
<li><a href="{{ route('admin.notifications') }}">{{ 'settings_notifications'|_ }}</a></li>
</ul> </ul>
</div> </div>
</div> </div>
@@ -34,20 +35,20 @@
<input type="hidden" name="_token" value="{{ csrf_token() }}"> <input type="hidden" name="_token" value="{{ csrf_token() }}">
<div class="box box-default"> <div class="box box-default">
<div class="box-header with-border"> <div class="box-header with-border">
<h3 class="box-title">{{ 'admin_notifications'|_ }}</h3> <h3 class="box-title">{{ 'owner_notifications'|_ }}</h3>
</div> </div>
<div class="box-body"> <div class="box-body">
<p> <p>
{{ 'admin_notifications_expl'|_ }} {{ 'owner_notifications_expl'|_ }}
</p> </p>
{% for notification, value in notifications %} {% for notification, value in notifications %}
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input value="1" {% if true == value %}checked{% endif %} type="checkbox" name="notification_{{ notification }}"> {{ trans('firefly.admin_notification_check_'~notification) }} <input value="1" {% if true == value %}checked{% endif %} type="checkbox" name="notification_{{ notification }}"> {{ trans('firefly.owner_notification_check_'~notification) }}
</label> </label>
</div> </div>
{% endfor %} {% endfor %}
{{ ExpandedForm.text('slackUrl', slackUrl, {'label' : 'slack_url_label'|_}) }} {# {{ ExpandedForm.text('slackUrl', slackUrl, {'label' : 'slack_url_label'|_}) }} #}
</div> </div>
<div class="box-footer"> <div class="box-footer">
<button type="submit" class="btn btn-success"> <button type="submit" class="btn btn-success">

View File

@@ -0,0 +1,68 @@
{% extends './layout/default' %}
{% block breadcrumbs %}
{{ Breadcrumbs.render }}
{% endblock %}
{% block content %}
<div class="row" xmlns="http://www.w3.org/1999/html">
<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12">
<form action="{{ route('admin.notification.post') }}" method="post">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<div class="box box-default">
<div class="box-header with-border">
<h3 class="box-title">{{ 'notification_settings'|_ }}</h3>
</div>
<div class="box-body">
{{ ExpandedForm.text('slackUrl', slackUrl, {'label' : 'slack_url_label'|_}) }}
{{ ExpandedForm.text('discordUrl', discordUrl, {'label' : 'discord_url_label'|_}) }}
</div>
<div class="box-footer">
<button type="submit" class="btn btn-success">
<span class="fa fa-check-circle"></span> {{ ('save_notification_settings')|_ }}
</button>
</div>
</div>
</form>
</div>
<form action="{{ route('admin.notification.test') }}" method="post">
<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<div class="box box-default">
<div class="box-header with-border">
<h3 class="box-title">{{ 'available_channels_title'|_ }}</h3>
</div>
<div class="box-body">
<p>
{{ 'available_channels_expl'|_ }}
</p>
<ul>
{% for name,info in channels %}
<li>
{% if info.enabled %}
☑️ {{ trans('firefly.notification_channel_name_'~name) }}
{% if 0 == info.ui_configurable %}({{ 'configure_channel_in_env'|_ }}) {% endif %}
{% endif %}
{% if not info.enabled %}
⚠️ {{ trans('firefly.notification_channel_name_'~name) }} ({{ 'channel_not_available'|_ }})
{% endif %}
</li>
{% endfor %}
</ul>
</div>
<div class="box-footer">
<div class="btn-group">
{% for name,info in channels %}
{% if info.enabled %}
<button type="submit" name="test_submit" value="{{ name }}" class="btn btn-default">
{{ trans('firefly.test_notification_channel_name_'~name) }}
</button>
{% endif %}
{% endfor %}
</div>
</div>
</div>
</div>
</form>
</div>
{% endblock %}

View File

@@ -2,7 +2,7 @@
<label for="{{ options.id }}" class="col-sm-4 control-label">{{ label }}</label> <label for="{{ options.id }}" class="col-sm-4 control-label">{{ label }}</label>
<div class="col-sm-8"> <div class="col-sm-8">
{{ Html.select(name~"[]", list, selected).id(options.id).class('form-control').attribute('multiple').attribute('autocomplete','off').attribute('spellcheck','false').attribute('placeholder', options.placeholder) }} {{ Html.multiselect(name~"[]", list, selected).id(options.id).class('form-control').attribute('autocomplete','off').attribute('spellcheck','false').attribute('placeholder', options.placeholder) }}
{% include 'form.help' %} {% include 'form.help' %}
{% include 'form.feedback' %} {% include 'form.feedback' %}

View File

@@ -56,7 +56,13 @@
{% endif %} {% endif %}
{# SINGLE INFO MESSAGE #} {# SINGLE INFO MESSAGE #}
{% if session('info') is not iterable %} {% if session('info') is not iterable %}
{% if session_has('info_url') %}
<a href="{{ session('info_url') }}">
{% endif %}
<strong>{{ 'flash_info'|_ }}:</strong> {{ session('info')|raw }} <strong>{{ 'flash_info'|_ }}:</strong> {{ session('info')|raw }}
{% if session_has('info_url') %}
</a>
{% endif %}
{% endif %} {% endif %}
</div> </div>

View File

@@ -179,6 +179,15 @@ Breadcrumbs::for(
} }
); );
Breadcrumbs::for(
'admin.notification.index',
static function (Generator $breadcrumbs): void {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('firefly.administration'), route('admin.index'));
$breadcrumbs->push(trans('breadcrumbs.notification_index'), route('admin.notification.index'));
}
);
Breadcrumbs::for( Breadcrumbs::for(
'admin.users', 'admin.users',
static function (Generator $breadcrumbs): void { static function (Generator $breadcrumbs): void {

View File

@@ -1398,6 +1398,11 @@ Route::group(
// FF configuration: // FF configuration:
Route::get('configuration', ['uses' => 'ConfigurationController@index', 'as' => 'configuration.index']); Route::get('configuration', ['uses' => 'ConfigurationController@index', 'as' => 'configuration.index']);
Route::post('configuration', ['uses' => 'ConfigurationController@postIndex', 'as' => 'configuration.index.post']); Route::post('configuration', ['uses' => 'ConfigurationController@postIndex', 'as' => 'configuration.index.post']);
// routes for notifications settings.
Route::get('notifications', ['uses' => 'NotificationController@index', 'as' => 'notification.index']);
Route::post('notifications', ['uses' => 'NotificationController@postIndex', 'as' => 'notification.post']);
Route::post('notifications/test', ['uses' => 'NotificationController@testNotification', 'as' => 'notification.test']);
} }
); );