Admin and Discord Notification Improvements (#1407)

* Backend changes

Sorting for : Airlines, Subfleets, Type Ratings and exported Flights

Blade : Added WYSIWYG editor to flight remarks/notes field

Notifications :

Mails > Added enabled/disabled settings for mails. Discord > Dashed out PirepPreFiled, re-enabled PirepStatusChanged with reduced messages

* Update PirepStatusChanged.php

* Update NotificationEventsHandler.php

* in_array fix

* Fix Discord Notifications

* Discord Notifications

Removed the pirep url from message as it is mostly private at phpvms side.

Also removed the Flight Ident from fields 'cause it is being used in the title.

Added the user avatar as thumbnail, and pirep filed message uses the airline logo as the main image.

Even though the outgoing pirep status messages are reduced, it is still possible to enable/disable them from admin settings.

Pirep Filed is always being sent (if the webhook is defined)

* StyleFix
This commit is contained in:
B.Fatih KOZ
2022-02-15 00:24:22 +03:00
committed by GitHub
parent 60cec870f6
commit 08f82f8a2e
12 changed files with 171 additions and 68 deletions

View File

@@ -410,6 +410,41 @@
options: ''
type: text
description: The Discord Webhook URL for private notifications
- key: notifications.discord_pirep_status
name: Discord Pirep Messages (Public)
group: notifications
value: true
options: ''
type: boolean
description: Pirep status messages (Only key events are being sent)
- key: notifications.mail_pirep_admin
name: Pirep Filed (Admin)
group: notifications
value: true
options: ''
type: boolean
description: Pirep filed mails sent to admins
- key: notifications.mail_pirep_user_ack
name: Pirep Accepted (Pilot)
group: notifications
value: true
options: ''
type: boolean
description: Pirep Accepted mails sent to pilots
- key: notifications.mail_pirep_user_rej
name: Pirep Rejected (Pilot)
group: notifications
value: true
options: ''
type: boolean
description: Pirep Rejected mails sent to pilots
- key: notifications.mail_news
name: News Mails
group: notifications
value: true
options: ''
type: boolean
description: News mails sent to all members
- key: 'cron.random_id'
name: 'Cron Randomized ID'
group: 'cron'

View File

@@ -38,7 +38,7 @@ class AirlinesController extends Controller
public function index(Request $request)
{
$this->airlineRepo->pushCriteria(new RequestCriteria($request));
$airlines = $this->airlineRepo->all();
$airlines = $this->airlineRepo->orderby('name', 'asc')->get();
return view('admin.airlines.index', [
'airlines' => $airlines,

View File

@@ -308,7 +308,7 @@ class FlightController extends Controller
$where['airline_id'] = $airline_id;
$file_name = 'flights-'.$airline_id.'.csv';
}
$flights = $this->flightRepo->where($where)->get();
$flights = $this->flightRepo->where($where)->orderBy('airline_id')->orderBy('flight_number')->orderBy('route_code')->orderBy('route_leg')->get();
$path = $exporter->exportFlights($flights);
return response()->download($path, $file_name, ['content-type' => 'text/csv'])->deleteFileAfterSend(true);

View File

@@ -83,7 +83,7 @@ class SubfleetController extends Controller
public function index(Request $request)
{
$this->subfleetRepo->with(['airline'])->pushCriteria(new RequestCriteria($request));
$subfleets = $this->subfleetRepo->all();
$subfleets = $this->subfleetRepo->orderby('name', 'asc')->get();
return view('admin.subfleets.index', [
'subfleets' => $subfleets,

View File

@@ -32,7 +32,7 @@ class TypeRatingController extends Controller
public function index(Request $request)
{
$this->typeratingRepo->pushCriteria(new RequestCriteria($request));
$typeratings = $this->typeratingRepo->all();
$typeratings = $this->typeratingRepo->orderby('type', 'asc')->get();
return view('admin.typeratings.index', [
'typeratings' => $typeratings,
@@ -50,7 +50,6 @@ class TypeRatingController extends Controller
$model = $this->typeratingRepo->create($input);
Flash::success('Type Rating saved successfully.');
// Cache::forget(config('cache.keys.RANKS_PILOT_LIST.key'));
return redirect(route('admin.typeratings.edit', [$model->id]));
}

View File

@@ -11,14 +11,16 @@ use Carbon\Carbon;
*/
class DiscordMessage
{
const COLOR_SUCCESS = '#0B6623';
const COLOR_WARNING = '#FD6A02';
const COLOR_ERROR = '#ED2939';
const COLOR_SUCCESS = '0B6623';
const COLOR_WARNING = 'FD6A02';
const COLOR_ERROR = 'ED2939';
public $webhook_url;
protected $title;
protected $url;
protected $thumbnail = [];
protected $image = [];
protected $description;
protected $timestamp;
protected $footer;
@@ -26,30 +28,43 @@ class DiscordMessage
protected $author = [];
protected $fields = [];
/**
* Supply the webhook URL that this should be going to
*/
// Supply the webhook URL that this should be going to
public function webhook(string $url): self
{
$this->webhook_url = $url;
return $this;
}
/**
* Title of the notification
*/
// Title of the embed
public function title(string $title): self
{
$this->title = $title;
return $this;
}
// URL of the Title
public function url(string $url): self
{
$this->url = $url;
return $this;
}
// Thumbnail image (placed right side of embed)
// ['url' => '']
public function thumbnail(array $thumbnail): self
{
$this->thumbnail = $thumbnail;
return $this;
}
// Main image (placed bottom of embed)
// ['url' => '']
public function image(array $image): self
{
$this->image = $image;
return $this;
}
/**
* @param array|string $descriptionLines
*/
@@ -63,22 +78,15 @@ class DiscordMessage
return $this;
}
/**
* Set the author information:
* [
* 'name' => '',
* 'url' => '',
* 'icon_url' => '',
*/
// Author details
// ['name' => '', 'url' => '', 'icon_url' => '']
public function author(array $author): self
{
$this->author = $author;
return $this;
}
/**
* Set the fields
*/
// Fields
public function fields(array $fields): self
{
$this->fields = [];
@@ -99,32 +107,45 @@ class DiscordMessage
return $this;
}
// Fixed Success Color
public function success(): self
{
$this->color = static::COLOR_SUCCESS;
$this->color = hexdec('0B6623'); // static::COLOR_SUCCESS;
return $this;
}
// Fixed Warning
public function warning(): self
{
$this->color = static::COLOR_WARNING;
$this->color = hexdec('FD6A02'); // static::COLOR_WARNING;
return $this;
}
// Fixed Error
public function error(): self
{
$this->color = static::COLOR_ERROR;
$this->color = hexdec('ED2939'); // static::COLOR_ERROR;
return $this;
}
// Custom Color
public function color(string $embed_color): self
{
$this->color = hexdec($embed_color);
return $this;
}
public function toArray(): array
{
$embeds = [
'type' => 'rich',
'color' => $this->color,
'title' => $this->title,
'url' => $this->url,
'type' => 'rich',
'thumbnail' => $this->thumbnail,
'description' => $this->description,
'author' => $this->author,
'image' => $this->image,
'timestamp' => Carbon::now('UTC'),
];

View File

@@ -31,7 +31,7 @@ class NewsAdded extends Notification implements ShouldQueue
public function toDiscordChannel($news): ?DiscordMessage
{
$dm = new DiscordMessage();
return $dm->url(setting('notifications.discord_public_webhook_url'))
return $dm->webhook(setting('notifications.discord_public_webhook_url'))
->success()
->title('News: '.$news->subject)
->author([

View File

@@ -43,11 +43,10 @@ class PirepFiled extends Notification implements ShouldQueue
{
$title = 'Flight '.$pirep->ident.' Filed';
$fields = [
'Flight' => $pirep->ident,
'Departure Airport' => $pirep->dpt_airport_id,
'Arrival Airport' => $pirep->arr_airport_id,
'Equipment' => $pirep->aircraft->ident,
'Flight Time' => Time::minutesToTimeString($pirep->flight_time),
'Dep.Airport' => $pirep->dpt_airport_id,
'Arr.Airport' => $pirep->arr_airport_id,
'Equipment' => $pirep->aircraft->ident,
'Flight Time' => Time::minutesToTimeString($pirep->flight_time),
];
if ($pirep->distance) {
@@ -63,16 +62,19 @@ class PirepFiled extends Notification implements ShouldQueue
}
}
// User avatar, somehow $pirep->user->resolveAvatarUrl() is not being accepted by Discord as thumbnail
$user_avatar = !empty($pirep->user->avatar) ? $pirep->user->avatar->url : $pirep->user->gravatar(256);
$dm = new DiscordMessage();
return $dm->url(setting('notifications.discord_public_webhook_url'))
return $dm->webhook(setting('notifications.discord_public_webhook_url'))
->success()
->title($title)
->description($pirep->user->discord_id ? 'Flight by <@'.$pirep->user->discord_id.'>' : '')
->url(route('frontend.pireps.show', [$pirep->id]))
->thumbnail(['url' => $user_avatar])
->image(['url' => $pirep->airline->logo])
->author([
'name' => $pirep->user->ident.' - '.$pirep->user->name_private,
'url' => route('frontend.profile.show', [$pirep->user_id]),
'icon_url' => $pirep->user->resolveAvatarUrl(),
'name' => $pirep->user->ident.' - '.$pirep->user->name_private,
'url' => route('frontend.profile.show', [$pirep->user_id]),
])
->fields($fields);
}

View File

@@ -9,11 +9,9 @@ use App\Notifications\Channels\Discord\DiscordMessage;
use App\Notifications\Channels\Discord\DiscordWebhook;
use App\Support\Units\Distance;
use App\Support\Units\Time;
use function config;
use Illuminate\Contracts\Queue\ShouldQueue;
use PhpUnitsOfMeasure\Exception\NonNumericValue;
use PhpUnitsOfMeasure\Exception\NonStringUnitName;
use function route;
/**
* Send the PIREP accepted message to a particular user, can also be sent to Discord
@@ -75,14 +73,16 @@ class PirepStatusChanged extends Notification implements ShouldQueue
*/
public function toDiscordChannel($pirep): ?DiscordMessage
{
$title = 'Flight '.$pirep->ident.' '.self::$verbs[$pirep->status];
if (empty(setting('notifications.discord_public_webhook_url'))) {
return null;
}
$title = 'Flight '.$pirep->ident.' '.self::$verbs[$pirep->status];
$fields = [
'Flight' => $pirep->ident,
'Departure Airport' => $pirep->dpt_airport_id,
'Arrival Airport' => $pirep->arr_airport_id,
'Equipment' => $pirep->aircraft->ident,
'Flight Time' => Time::minutesToTimeString($pirep->flight_time),
'Dep.Airport' => $pirep->dpt_airport_id,
'Arr.Airport' => $pirep->arr_airport_id,
'Equipment' => $pirep->aircraft->ident,
'Flight Time' => Time::minutesToTimeString($pirep->flight_time),
];
// Show the distance, but include the planned distance if it's been set
@@ -109,16 +109,29 @@ class PirepStatusChanged extends Notification implements ShouldQueue
}
}
// User avatar, somehow $pirep->user->resolveAvatarUrl() is not being accepted by Discord as thumbnail
$user_avatar = !empty($pirep->user->avatar) ? $pirep->user->avatar->url : $pirep->user->gravatar(256);
// Proper coloring for the messages
// Pirep Filed > success, normals > warning, non-normals > error
$danger_types = [
PirepStatus::GRND_RTRN,
PirepStatus::DIVERTED,
PirepStatus::CANCELLED,
PirepStatus::PAUSED,
PirepStatus::EMERG_DESCENT,
];
$color = in_array($pirep->status, $danger_types, true) ? 'ED2939' : 'FD6A02';
$dm = new DiscordMessage();
return $dm->url(setting('notifications.discord_public_webhook_url'))
->success()
return $dm->webhook(setting('notifications.discord_public_webhook_url'))
->color($color)
->title($title)
->description($pirep->user->discord_id ? 'Flight by <@'.$pirep->user->discord_id.'>' : '')
->url(route('frontend.pireps.show', [$pirep->id]))
->thumbnail(['url' => $user_avatar])
->author([
'name' => $pirep->user->ident.' - '.$pirep->user->name_private,
'url' => route('frontend.profile.show', [$pirep->user_id]),
'icon_url' => $pirep->user->resolveAvatarUrl(),
'name' => $pirep->user->ident.' - '.$pirep->user->name_private,
'url' => route('frontend.profile.show', [$pirep->user_id]),
])
->fields($fields);
}

View File

@@ -49,7 +49,7 @@ class UserRegistered extends Notification implements ShouldQueue
public function toDiscordChannel($notifiable): ?DiscordMessage
{
$dm = new DiscordMessage();
return $dm->url(setting('notifications.discord_private_webhook_url'))
return $dm->webhook(setting('notifications.discord_private_webhook_url'))
->success()
->title('New User Registered: '.$this->user->ident)
->author([

View File

@@ -11,6 +11,7 @@ use App\Events\PirepRejected;
use App\Events\PirepStatusChange;
use App\Events\UserRegistered;
use App\Events\UserStateChanged;
use App\Models\Enums\PirepStatus;
use App\Models\Enums\UserState;
use App\Models\User;
use App\Notifications\Messages\UserRejected;
@@ -161,7 +162,7 @@ class NotificationEventsHandler extends Listener
}
/**
* Prefile notification
* Prefile notification. Disabled intentionally, No need to send it to Discord
*/
public function onPirepPrefile(PirepPrefiled $event): void
{
@@ -170,16 +171,33 @@ class NotificationEventsHandler extends Listener
/*
* Broadcast notifications
*/
Notification::send([$event->pirep], new Messages\Broadcast\PirepPrefiled($event->pirep));
// Notification::send([$event->pirep], new Messages\Broadcast\PirepPrefiled($event->pirep));
}
/**
* Status Change notification. Disabled for now, too noisy
* Status Change notification.
* Reduced the messages (Boarding, Pushback, TakeOff, Landing and non-normals only)
* If needed array can be tied to a setting at admin side for further customization
*/
public function onPirepStatusChange(PirepStatusChange $event): void
{
// Log::info('NotificationEvents::onPirepStatusChange: '.$event->pirep->id.' prefiled');
// Notification::send([$event->pirep], new Messages\Discord\PirepStatusChanged($event->pirep));
Log::info('NotificationEvents::onPirepStatusChange: '.$event->pirep->id.' status changed');
$message_types = [
PirepStatus::BOARDING,
PirepStatus::PUSHBACK_TOW,
PirepStatus::GRND_RTRN,
PirepStatus::TAKEOFF,
PirepStatus::LANDED,
PirepStatus::DIVERTED,
PirepStatus::CANCELLED,
PirepStatus::PAUSED,
PirepStatus::EMERG_DESCENT,
];
if (setting('notifications.discord_pirep_status', true) && in_array($event->pirep->status, $message_types, true)) {
Notification::send([$event->pirep], new Messages\Broadcast\PirepStatusChanged($event->pirep));
}
}
/**
@@ -190,7 +208,9 @@ class NotificationEventsHandler extends Listener
public function onPirepFile(PirepFiled $event): void
{
Log::info('NotificationEvents::onPirepFile: '.$event->pirep->id.' filed');
$this->notifyAdmins(new Messages\PirepFiled($event->pirep));
if (setting('notifications.mail_pirep_admin', true)) {
$this->notifyAdmins(new Messages\PirepFiled($event->pirep));
}
/*
* Broadcast notifications
@@ -205,8 +225,10 @@ class NotificationEventsHandler extends Listener
*/
public function onPirepAccepted(PirepAccepted $event): void
{
Log::info('NotificationEvents::onPirepAccepted: '.$event->pirep->id.' accepted');
$this->notifyUser($event->pirep->user, new Messages\PirepAccepted($event->pirep));
if (setting('notifications.mail_pirep_user_ack', true)) {
Log::info('NotificationEvents::onPirepAccepted: '.$event->pirep->id.' accepted');
$this->notifyUser($event->pirep->user, new Messages\PirepAccepted($event->pirep));
}
}
/**
@@ -216,8 +238,10 @@ class NotificationEventsHandler extends Listener
*/
public function onPirepRejected(PirepRejected $event): void
{
Log::info('NotificationEvents::onPirepRejected: '.$event->pirep->id.' rejected');
$this->notifyUser($event->pirep->user, new Messages\PirepRejected($event->pirep));
if (setting('notifications.mail_pirep_user_rej', true)) {
Log::info('NotificationEvents::onPirepRejected: '.$event->pirep->id.' rejected');
$this->notifyUser($event->pirep->user, new Messages\PirepRejected($event->pirep));
}
}
/**
@@ -228,7 +252,9 @@ class NotificationEventsHandler extends Listener
public function onNewsAdded(NewsAdded $event): void
{
Log::info('NotificationEvents::onNewsAdded');
$this->notifyAllUsers(new Messages\NewsAdded($event->news));
if (setting('notifications.mail_news', true)) {
$this->notifyAllUsers(new Messages\NewsAdded($event->news));
}
/*
* Broadcast notifications

View File

@@ -174,7 +174,6 @@
</div>
</div>
<div class="row">
<div class="col-12">
<div class="form-container">
@@ -260,8 +259,9 @@
<div class="row">
<div class="form-group col-sm-12">
{{ Form::textarea('notes', null, [
'class' => 'form-control input-text',
'style' => 'padding: 10px',
'id' => 'editor',
'class' => 'editor',
'style' => 'padding: 5px',
]) }}
<p class="text-danger">{{ $errors->first('notes') }}</p>
</div>
@@ -288,3 +288,10 @@
</div>
</div>
</div>
@section('scripts')
@parent
<script src="{{ public_asset('assets/vendor/ckeditor4/ckeditor.js') }}"></script>
<script>
$(document).ready(function () { CKEDITOR.replace('editor'); });
</script>
@endsection