Skip to content

Commit

Permalink
feat: settings for notifications (#61)
Browse files Browse the repository at this point in the history
* Add settings to enable/disable operation notification types

Several users have requested the ability to reduce the number of notifications sent via the calendar. This commit adds the ability to globally disable each type of notification: create/post, update/edit, cancel, activate, end.

* Allow configuring intervals for operation pings

This addresses one of the top comments in issues reported to the calendar: allow admins to control when and how often operations are pinged out.

Closes #22
  • Loading branch information
alkari-verende authored Jan 3, 2022
1 parent 3ddf302 commit ee71122
Show file tree
Hide file tree
Showing 11 changed files with 215 additions and 61 deletions.
24 changes: 14 additions & 10 deletions src/Commands/RemindOperation.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,28 @@ class RemindOperation extends Command
*/
protected $description = 'Check for operation to be reminded on Slack';

/**
* @var array
*/
private $marks = [15, 30, 60];

/**
* Process the command.
*/
public function handle()
{
if (setting('kassie.calendar.slack_integration', true) == 1) {
$ops = Operation::all()->take(-50);
$now = Carbon::now('UTC');
# Ensure we send reminders starting with furthest in the future. That way
# when more than one event is being reminded, the last reminder in chat
# is the next event to occur.
$configured_marks = setting('kassie.calendar.notify_operation_interval', true);
if ($configured_marks === null || !setting('kassie.calendar.slack_integration', true)) return;
$marks = explode(',', $configured_marks);
rsort($marks);

foreach ($marks as $mark)
{
$when = Carbon::now('UTC')->floorMinute()->addMinutes($mark);
$ops = Operation::where('is_cancelled', false)
->where('start_at', $when)
->get();
foreach($ops as $op)
{
if ($op->status == 'incoming' && in_array($now->diffInMinutes($op->start_at, false), $this->marks))
Notification::send($op, new OperationPinged());
Notification::send($op, new OperationPinged());
}
}
}
Expand Down
45 changes: 13 additions & 32 deletions src/Http/Controllers/SettingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Seat\Web\Http\Controllers\Controller;
use Seat\Kassie\Calendar\Models\Tag;
use Seat\Notifications\Models\Integration;
use Seat\Kassie\Calendar\Http\Validation\SettingsValidation;

/**
* Class SettingController.
Expand All @@ -31,39 +32,19 @@ public function index() {
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function updateSlack(Request $request)
public function updateSlack(SettingsValidation $request)
{
$validated_data = $request->validate([
'slack_integration_default_channel' => ['nullable', 'exists:integrations,id'],
'slack_emoji_importance_full' => ['nullable', 'string'],
'slack_emoji_importance_half' => ['nullable', 'string'],
'slack_emoji_importance_empty' => ['nullable', 'string'],
]);

setting([
'kassie.calendar.slack_integration',
$request->slack_integration == 1 ? 1 : 0,
], true);

setting([
'kassie.calendar.slack_integration_default_channel',
$validated_data['slack_integration_default_channel'],
], true);

setting([
'kassie.calendar.slack_emoji_importance_full',
$validated_data['slack_emoji_importance_full'],
], true);

setting([
'kassie.calendar.slack_emoji_importance_half',
$validated_data['slack_emoji_importance_half'],
], true);

setting([
'kassie.calendar.slack_emoji_importance_empty',
$validated_data['slack_emoji_importance_empty'],
], true);
setting(['kassie.calendar.slack_integration', (bool)$request->slack_integration], true);
setting(['kassie.calendar.slack_integration_default_channel', $request['slack_integration_default_channel']], true);
setting(['kassie.calendar.notify_create_operation', (bool)$request->notify_create_operation], true);
setting(['kassie.calendar.notify_update_operation', (bool)$request->notify_update_operation], true);
setting(['kassie.calendar.notify_cancel_operation', (bool)$request->notify_cancel_operation], true);
setting(['kassie.calendar.notify_activate_operation', (bool)$request->notify_activate_operation], true);
setting(['kassie.calendar.notify_end_operation', (bool)$request->notify_end_operation], true);
setting(['kassie.calendar.notify_operation_interval', $request['notify_operation_interval']], true);
setting(['kassie.calendar.slack_emoji_importance_full', $request['slack_emoji_importance_full']], true);
setting(['kassie.calendar.slack_emoji_importance_half', $request['slack_emoji_importance_half']], true);
setting(['kassie.calendar.slack_emoji_importance_empty', $request['slack_emoji_importance_empty']], true);

return redirect()->back();
}
Expand Down
66 changes: 66 additions & 0 deletions src/Http/Validation/SettingsValidation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

/*
* This file is part of SeAT
*
* Copyright (C) 2022 Alkari Verende
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

namespace Seat\Kassie\Calendar\Http\Validation;

use Illuminate\Foundation\Http\FormRequest;

/**
* Class Settings.
*
* @package Seat\Kassie\Calendar\Validation
*/
class SettingsValidation extends FormRequest
{
/**
* Authorize the request by default.
*
* @return bool
*/
public function authorize()
{

return true;
}

/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'slack_integration' => ['boolean'],
'slack_integration_default_channel' => ['nullable', 'exists:integrations,id'],
'slack_emoji_importance_full' => ['nullable', 'string'],
'slack_emoji_importance_half' => ['nullable', 'string'],
'slack_emoji_importance_empty' => ['nullable', 'string'],
'notify_operation_interval' => ['nullable', 'string'],
'notify_create_operation' => ['boolean'],
'notify_update_operation' => ['boolean'],
'notify_cancel_operation' => ['boolean'],
'notify_activate_operation' => ['boolean'],
'notify_end_operation' => ['boolean'],
];
}
}
16 changes: 10 additions & 6 deletions src/Observers/OperationObserver.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ class OperationObserver
*/
public function created(Operation $operation)
{
if (setting('kassie.calendar.slack_integration', true) == 1 && !is_null($operation->integration))
if (setting('kassie.calendar.slack_integration', true) &&
!is_null($operation->integration) &&
setting('kassie.calendar.notify_create_operation', true))
Notification::send($operation, new OperationPosted());
}

Expand All @@ -31,18 +33,20 @@ public function created(Operation $operation)
*/
public function updating(Operation $new_operation)
{
if (setting('kassie.calendar.slack_integration', true) == 1 && !is_null($new_operation->integration)) {
if (setting('kassie.calendar.slack_integration', true) && !is_null($new_operation->integration)) {
$old_operation = Operation::find($new_operation->id);
if ($old_operation->is_cancelled != $new_operation->is_cancelled) {
if ($new_operation->is_cancelled == true)
if ($new_operation->is_cancelled == true && setting('kassie.calendar.notify_cancel_operation', true))
Notification::send($new_operation, new OperationCancelled());
else
elseif (setting('kassie.calendar.notify_activate_operation', true))
Notification::send($new_operation, new OperationActivated());
}
elseif ($old_operation->end_at != $new_operation->end_at && $new_operation->is_cancelled == false) {
elseif ($old_operation->end_at != $new_operation->end_at &&
$new_operation->is_cancelled == false &&
setting('kassie.calendar.notify_end_operation', true)) {
Notification::send($new_operation, new OperationEnded());
}
else {
elseif (setting('kassie.calendar.notify_update_operation', true)) {
Notification::send($new_operation, new OperationUpdated());
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Seat\Services\Models\GlobalSetting;

class DefaultNotificationSettings extends Migration
{
const DEFAULT_SETTINGS = [
'kassie.calendar.notify_create_operation' => true,
'kassie.calendar.notify_update_operation' => true,
'kassie.calendar.notify_cancel_operation' => true,
'kassie.calendar.notify_activate_operation' => true,
'kassie.calendar.notify_end_operation' => true,
'kassie.calendar.notify_operation_interval' => '15,30,60',
];

/**
* Run the migrations.
*
* @return void
*/
public function up()
{
foreach (self::DEFAULT_SETTINGS as $name => $value) {
setting([$name, $value], true);
}
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
GlobalSetting::whereIn('name', array_keys(self::DEFAULT_SETTINGS))->delete();
}
}
28 changes: 19 additions & 9 deletions src/resources/lang/en/seat.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,25 @@
'notification_enable' => 'Notify to Slack',
'integration_channel' => 'Integration Channel',

'slack_integration' => 'Slack Integration',
'enabled' => 'Enabled',
'default_channel' => 'Default Channel',
'webhook' => 'Webhook',
'emoji_full' => 'Full Emoji',
'emoji_half' => 'Half Emoji',
'emoji_empty' => 'Empty Emoji',
'help_emoji' => 'Setup which emoji to use to display the "importance" of an operation when relaying to Slack.',
'save' => 'Save',
'notifications_to_send' => 'Notifications to send',

'help_notify_operation_interval' => 'Decide how many pings to send before each operation. Each value is the number of minutes prior to the operation to send the ping. Separate numbers with commas. Default value of :default_interval will send 3 notifications: 15 minutes, 30 minutes, and 60 minutes prior to the operation start time.',
'ping_intervals' => 'Ping intervals',

'slack_integration' => 'Slack Integration',
'enabled' => 'Enabled',
'default_channel' => 'Default Channel',
'create_operation' => 'Create Operation',
'cancel_operation' => 'Cancel Operation',
'end_operation' => 'End Operation',
'update_operation' => 'Update Operation',
'activate_operation' => 'Reactivate Cancelled Operation',
'webhook' => 'Webhook',
'emoji_full' => 'Full Emoji',
'emoji_half' => 'Half Emoji',
'emoji_empty' => 'Empty Emoji',
'help_emoji' => 'Setup which emoji to use to display the "importance" of an operation when relaying to Slack.',
'save' => 'Save',

'warning_no_character' => "You can't subscribe to an operation without any character registered in SeAT. Please add an API Key and retry.",

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
{{ csrf_field() }}
<input type="hidden" name="operation_id">

@if(setting('kassie.calendar.slack_integration', true) == true)
@if(setting('kassie.calendar.slack_integration', true) && setting('kassie.calendar.notify_activate_operation', true))
<div class="form-group row">
<label for="activate-operation-channel" class="col-sm-4 col-form-label">
{{ trans('calendar::seat.notification_enable') }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
{{ csrf_field() }}
<input type="hidden" name="operation_id">

@if(setting('kassie.calendar.slack_integration', true) == true)
@if(setting('kassie.calendar.slack_integration', true) && setting('kassie.calendar.notify_cancel_operation', true))
<div class="form-group row">
<label for="cancel-operation-channel" class="col-sm-4 col-form-label">
{{ trans('calendar::seat.notification_enable') }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@
</div>
</div>
{{-- Operation slack --}}
@if(setting('kassie.calendar.slack_integration', true) == true)
@if(setting('kassie.calendar.slack_integration', true))
<div class="form-group row">
<label for="create-operation-channel" class="col-sm-3 col-form-label">
<i class="fas fa-bell"></i>&nbsp;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@
</div>
</div>
{{-- Operation slack --}}
@if(setting('kassie.calendar.slack_integration', true) == true)
@if(setting('kassie.calendar.slack_integration', true))
<div class="form-group row">
<label for="update-operation-channel" class="col-sm-3 col-form-label">
<i class="fas fa-bell"></i>&nbsp;
Expand Down
49 changes: 49 additions & 0 deletions src/resources/views/setting/includes/slack.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,55 @@
</div>
</div>

<div class="form-group row">
<label class="col-sm-3 col-form-label">{{ trans('calendar::seat.notifications_to_send') }}</label>
<div class="col-sm-9">
<div class="form-check">
<input type="checkbox"
name="notify_create_operation"
class="form-check-input"
value="1"
@if(setting('kassie.calendar.notify_create_operation', true)) checked @endif />
{{ trans('calendar::seat.create_operation') }}<br>
<input type="checkbox"
name="notify_update_operation"
class="form-check-input"
value="1"
@if(setting('kassie.calendar.notify_update_operation', true)) checked @endif />
{{ trans('calendar::seat.update_operation') }}<br>
<input type="checkbox"
name="notify_cancel_operation"
class="form-check-input"
value="1"
@if(setting('kassie.calendar.notify_cancel_operation', true)) checked @endif />
{{ trans('calendar::seat.cancel_operation') }}<br>
<input type="checkbox"
name="notify_activate_operation"
class="form-check-input"
value="1"
@if(setting('kassie.calendar.notify_activate_operation', true)) checked @endif />
{{ trans('calendar::seat.activate_operation') }}<br>
<input type="checkbox"
name="notify_end_operation"
class="form-check-input"
value="1"
@if(setting('kassie.calendar.notify_end_operation', true)) checked @endif />
{{ trans('calendar::seat.end_operation') }}
</div>
</div>
</div>

<p class="callout callout-info text-justify">
{!! trans('calendar::seat.help_notify_operation_interval', ['default_interval' => '<code>15,30,60</code>']) !!}
</p>
<div class="form-group row">
<label for="notify_operation_interval" class="col-sm-3 col-form-label">{{ trans('calendar::seat.ping_intervals') }}</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="notify_operation_interval"
value="{{ setting('kassie.calendar.notify_operation_interval', true) }}">
</div>
</div>

<p class="callout callout-info text-justify">
{{ trans('calendar::seat.help_emoji') }}
</p>
Expand Down

0 comments on commit ee71122

Please sign in to comment.