mirror of
https://github.com/snipe/snipe-it.git
synced 2026-02-05 11:15:33 +00:00
Merge pull request #17391 from Godmartinz/add-components-notifications
FIXED: #13844 Adds Webhook and Mail Notifications for Components
This commit is contained in:
@ -4,10 +4,12 @@ namespace App\Listeners;
|
||||
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Mail\CheckinAccessoryMail;
|
||||
use App\Mail\CheckinComponentMail;
|
||||
use App\Mail\CheckinLicenseMail;
|
||||
use App\Mail\CheckoutAccessoryMail;
|
||||
use App\Mail\CheckoutAssetMail;
|
||||
use App\Mail\CheckinAssetMail;
|
||||
use App\Mail\CheckoutComponentMail;
|
||||
use App\Mail\CheckoutConsumableMail;
|
||||
use App\Mail\CheckoutLicenseMail;
|
||||
use App\Models\Accessory;
|
||||
@ -22,9 +24,11 @@ use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\CheckinAccessoryNotification;
|
||||
use App\Notifications\CheckinAssetNotification;
|
||||
use App\Notifications\CheckinComponentNotification;
|
||||
use App\Notifications\CheckinLicenseSeatNotification;
|
||||
use App\Notifications\CheckoutAccessoryNotification;
|
||||
use App\Notifications\CheckoutAssetNotification;
|
||||
use App\Notifications\CheckoutComponentNotification;
|
||||
use App\Notifications\CheckoutConsumableNotification;
|
||||
use App\Notifications\CheckoutLicenseSeatNotification;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
@ -39,7 +43,7 @@ use Osama\LaravelTeamsNotification\TeamsNotification;
|
||||
class CheckoutableListener
|
||||
{
|
||||
private array $skipNotificationsFor = [
|
||||
Component::class,
|
||||
// Component::class,
|
||||
];
|
||||
|
||||
/**
|
||||
@ -145,7 +149,6 @@ class CheckoutableListener
|
||||
$shouldSendEmailToUser = $this->checkoutableCategoryShouldSendEmail($event->checkoutable);
|
||||
$shouldSendEmailToAlertAddress = $this->shouldSendEmailToAlertAddress();
|
||||
$shouldSendWebhookNotification = $this->shouldSendWebhookNotification();
|
||||
|
||||
if (!$shouldSendEmailToUser && !$shouldSendEmailToAlertAddress && !$shouldSendWebhookNotification) {
|
||||
return;
|
||||
}
|
||||
@ -269,6 +272,9 @@ class CheckoutableListener
|
||||
case LicenseSeat::class:
|
||||
$notificationClass = CheckinLicenseSeatNotification::class;
|
||||
break;
|
||||
case Component::class:
|
||||
$notificationClass = CheckinComponentNotification::class;
|
||||
break;
|
||||
}
|
||||
|
||||
Log::debug('Notification class: '.$notificationClass);
|
||||
@ -299,6 +305,9 @@ class CheckoutableListener
|
||||
case LicenseSeat::class:
|
||||
$notificationClass = CheckoutLicenseSeatNotification::class;
|
||||
break;
|
||||
case Component::class:
|
||||
$notificationClass = CheckoutComponentNotification::class;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -310,6 +319,7 @@ class CheckoutableListener
|
||||
Asset::class => CheckoutAssetMail::class,
|
||||
LicenseSeat::class => CheckoutLicenseMail::class,
|
||||
Consumable::class => CheckoutConsumableMail::class,
|
||||
Component::class => CheckoutComponentMail::class,
|
||||
];
|
||||
$mailable= $lookup[get_class($event->checkoutable)];
|
||||
|
||||
@ -322,8 +332,8 @@ class CheckoutableListener
|
||||
Accessory::class => CheckinAccessoryMail::class,
|
||||
Asset::class => CheckinAssetMail::class,
|
||||
LicenseSeat::class => CheckinLicenseMail::class,
|
||||
Component::class => CheckinComponentMail::class,
|
||||
];
|
||||
|
||||
$mailable= $lookup[get_class($event->checkoutable)];
|
||||
|
||||
return new $mailable($event->checkoutable, $event->checkedOutTo, $event->checkedInBy, $event->note);
|
||||
@ -469,7 +479,8 @@ class CheckoutableListener
|
||||
return match (true) {
|
||||
$checkoutable instanceof Asset => $checkoutable->model->category,
|
||||
$checkoutable instanceof Accessory,
|
||||
$checkoutable instanceof Consumable => $checkoutable->category,
|
||||
$checkoutable instanceof Consumable,
|
||||
$checkoutable instanceof Component => $checkoutable->category,
|
||||
$checkoutable instanceof LicenseSeat => $checkoutable->license->category,
|
||||
};
|
||||
}
|
||||
|
||||
71
app/Mail/CheckinComponentMail.php
Normal file
71
app/Mail/CheckinComponentMail.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Component;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Address;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class CheckinComponentMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct(Component $component, $checkedOutTo, User $checkedInby, $note)
|
||||
{
|
||||
$this->item = $component;
|
||||
$this->target = $checkedOutTo;
|
||||
$this->admin = $checkedInby;
|
||||
$this->note = $note;
|
||||
$this->settings = Setting::getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(config('mail.from.address'), config('mail.from.name'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
subject: trans('mail.Confirm_component_checkin'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'mail.markdown.checkin-component',
|
||||
with: [
|
||||
'item' => $this->item,
|
||||
'admin' => $this->admin,
|
||||
'note' => $this->note,
|
||||
'target' => $this->target,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachments for the message.
|
||||
*
|
||||
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
82
app/Mail/CheckoutComponentMail.php
Normal file
82
app/Mail/CheckoutComponentMail.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\Component;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Address;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class CheckoutComponentMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct(Component $component, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
|
||||
{
|
||||
$this->item = $component;
|
||||
$this->admin = $checkedOutBy;
|
||||
$this->note = $note;
|
||||
$this->target = $checkedOutTo;
|
||||
$this->acceptance = $acceptance;
|
||||
$this->qty = $component->assets->first()?->pivot?->assigned_qty;
|
||||
|
||||
$this->settings = Setting::getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(config('mail.from.address'), config('mail.from.name'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
subject: trans('mail.Confirm_component_delivery'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
|
||||
$eula = $this->item->getEula();
|
||||
$req_accept = $this->item->requireAcceptance();
|
||||
|
||||
$accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance);
|
||||
|
||||
return new Content(
|
||||
markdown: 'mail.markdown.checkout-component',
|
||||
with: [
|
||||
'item' => $this->item,
|
||||
'admin' => $this->admin,
|
||||
'note' => $this->note,
|
||||
'target' => $this->target,
|
||||
'eula' => $eula,
|
||||
'req_accept' => $req_accept,
|
||||
'accept_url' => $accept_url,
|
||||
'qty' => $this->qty,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachments for the message.
|
||||
*
|
||||
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@ -2,12 +2,14 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
|
||||
/**
|
||||
@ -203,6 +205,36 @@ class Component extends SnipeModel
|
||||
{
|
||||
return $this->belongsTo(\App\Models\Manufacturer::class, 'manufacturer_id');
|
||||
}
|
||||
/**
|
||||
* Determine whether this asset requires acceptance by the assigned user
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return bool
|
||||
*/
|
||||
public function requireAcceptance()
|
||||
{
|
||||
return $this->category->require_acceptance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for a category-specific EULA, and if that doesn't exist,
|
||||
* checks for a settings level EULA
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return string | false
|
||||
*/
|
||||
public function getEula()
|
||||
{
|
||||
if ($this->category->eula_text) {
|
||||
return Helper::parseEscapedMarkedown($this->category->eula_text);
|
||||
} elseif ((Setting::getSettings()->default_eula_text) && ($this->category->use_default_eula == '1')) {
|
||||
return Helper::parseEscapedMarkedown(Setting::getSettings()->default_eula_text);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the component -> action logs relationship
|
||||
@ -248,6 +280,19 @@ class Component extends SnipeModel
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether to send a checkin/checkout email based on
|
||||
* asset model category
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return bool
|
||||
*/
|
||||
public function checkin_email()
|
||||
{
|
||||
return $this->category?->checkin_email;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check how many items within a component are remaining
|
||||
|
||||
165
app/Notifications/CheckinComponentNotification.php
Normal file
165
app/Notifications/CheckinComponentNotification.php
Normal file
@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Models\Component;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Notifications\Channels\SlackWebhookChannel;
|
||||
use Illuminate\Notifications\Messages\SlackMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Support\Str;
|
||||
use NotificationChannels\GoogleChat\Card;
|
||||
use NotificationChannels\GoogleChat\GoogleChatChannel;
|
||||
use NotificationChannels\GoogleChat\GoogleChatMessage;
|
||||
use NotificationChannels\GoogleChat\Section;
|
||||
use NotificationChannels\GoogleChat\Widgets\KeyValue;
|
||||
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsChannel;
|
||||
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsMessage;
|
||||
|
||||
class CheckinComponentNotification extends Notification
|
||||
{
|
||||
use Queueable;
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
private $params;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
* @param $params
|
||||
*/
|
||||
public function __construct(Component $component, $checkedOutTo, User $checkedInBy, $note)
|
||||
{
|
||||
$this->target = $checkedOutTo;
|
||||
$this->item = $component;
|
||||
$this->admin = $checkedInBy;
|
||||
$this->note = $note;
|
||||
$this->settings = Setting::getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function via()
|
||||
{
|
||||
$notifyBy = [];
|
||||
|
||||
if (Setting::getSettings()->webhook_selected == 'google' && Setting::getSettings()->webhook_endpoint) {
|
||||
|
||||
$notifyBy[] = GoogleChatChannel::class;
|
||||
}
|
||||
if (Setting::getSettings()->webhook_selected == 'microsoft' && Setting::getSettings()->webhook_endpoint) {
|
||||
|
||||
$notifyBy[] = MicrosoftTeamsChannel::class;
|
||||
}
|
||||
|
||||
if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) {
|
||||
$notifyBy[] = SlackWebhookChannel::class;
|
||||
}
|
||||
|
||||
return $notifyBy;
|
||||
}
|
||||
|
||||
public function toSlack()
|
||||
{
|
||||
$target = $this->target;
|
||||
$admin = $this->admin;
|
||||
$item = $this->item;
|
||||
$note = $this->note;
|
||||
$botname = ($this->settings->webhook_botname) ? $this->settings->webhook_botname : 'Snipe-Bot';
|
||||
$channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : '';
|
||||
|
||||
if ($admin) {
|
||||
$fields = [
|
||||
trans('general.from') => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
|
||||
trans('general.by') => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
|
||||
];
|
||||
|
||||
if ($item->location) {
|
||||
$fields[trans('general.location')] = $item->location->name;
|
||||
}
|
||||
|
||||
if ($item->company) {
|
||||
$fields[trans('general.company')] = $item->company->name;
|
||||
}
|
||||
|
||||
} else {
|
||||
$fields = [
|
||||
'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
|
||||
'By' => 'CLI tool',
|
||||
];
|
||||
}
|
||||
|
||||
return (new SlackMessage)
|
||||
->content(':arrow_down: :package: '.trans('mail.Component_checkin_notification'))
|
||||
->from($botname)
|
||||
->to($channel)
|
||||
->attachment(function ($attachment) use ($item, $note, $admin, $fields) {
|
||||
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
|
||||
->fields($fields)
|
||||
->content($note);
|
||||
});
|
||||
}
|
||||
public function toMicrosoftTeams()
|
||||
{
|
||||
$target = $this->target;
|
||||
$admin = $this->admin;
|
||||
$item = $this->item;
|
||||
$note = $this->note;
|
||||
if(!Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows')) {
|
||||
return MicrosoftTeamsMessage::create()
|
||||
->to($this->settings->webhook_endpoint)
|
||||
->type('success')
|
||||
->addStartGroupToSection('activityTitle')
|
||||
->title(trans('mail.Component_checkin_notification'))
|
||||
->addStartGroupToSection('activityText')
|
||||
->fact(htmlspecialchars_decode($item->present()->name), '', 'header')
|
||||
->fact(trans('mail.Component_checkin_notification')." by ", $admin->present()->fullName() ?: 'CLI tool')
|
||||
->fact(trans('mail.checkedin_from'), $target->present()->fullName())
|
||||
->fact(trans('admin/consumables/general.remaining'), $item->numRemaining())
|
||||
->fact(trans('mail.notes'), $note ?: '');
|
||||
}
|
||||
|
||||
$message = trans('mail.Component_checkin_notification');
|
||||
$details = [
|
||||
trans('mail.checkedin_from')=> $target->present()->fullName(),
|
||||
trans('mail.Component_checkin_notification')." by " => $admin->present()->fullName() ?: 'CLI tool',
|
||||
trans('admin/consumables/general.remaining') => $item->numRemaining(),
|
||||
trans('mail.notes') => $note ?: '',
|
||||
];
|
||||
|
||||
return array($message, $details);
|
||||
}
|
||||
public function toGoogleChat()
|
||||
{
|
||||
$target = $this->target;
|
||||
$item = $this->item;
|
||||
$note = $this->note;
|
||||
|
||||
return GoogleChatMessage::create()
|
||||
->to($this->settings->webhook_endpoint)
|
||||
->card(
|
||||
Card::create()
|
||||
->header(
|
||||
'<strong>'.trans('mail.Component_checkin_notification').'</strong>' ?: '',
|
||||
htmlspecialchars_decode($item->present()->name) ?: '',
|
||||
)
|
||||
->section(
|
||||
Section::create(
|
||||
KeyValue::create(
|
||||
trans('mail.checkedin_from') ?: '',
|
||||
$target->present()->fullName() ?: '',
|
||||
trans('admin/consumables/general.remaining').': '.$item->numRemaining(),
|
||||
)
|
||||
->onClick(route('components.show', $item->id))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
164
app/Notifications/CheckoutComponentNotification.php
Normal file
164
app/Notifications/CheckoutComponentNotification.php
Normal file
@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Models\Component;
|
||||
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Notifications\Channels\SlackWebhookChannel;
|
||||
use Illuminate\Notifications\Messages\SlackMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Support\Str;
|
||||
use NotificationChannels\GoogleChat\Card;
|
||||
use NotificationChannels\GoogleChat\GoogleChatChannel;
|
||||
use NotificationChannels\GoogleChat\GoogleChatMessage;
|
||||
use NotificationChannels\GoogleChat\Section;
|
||||
use NotificationChannels\GoogleChat\Widgets\KeyValue;
|
||||
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsChannel;
|
||||
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsMessage;
|
||||
|
||||
|
||||
class CheckoutComponentNotification extends Notification
|
||||
{
|
||||
use Queueable;
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
private $params;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
* @param $params
|
||||
*/
|
||||
public function __construct(Component $component, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
|
||||
{
|
||||
$this->item = $component;
|
||||
$this->admin = $checkedOutBy;
|
||||
$this->note = $note;
|
||||
$this->target = $checkedOutTo;
|
||||
$this->acceptance = $acceptance;
|
||||
$this->qty = $component->checkout_qty;
|
||||
|
||||
$this->settings = Setting::getSettings();
|
||||
}
|
||||
|
||||
/**`
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function via()
|
||||
{
|
||||
$notifyBy = [];
|
||||
if (Setting::getSettings()->webhook_selected == 'google' && Setting::getSettings()->webhook_endpoint) {
|
||||
|
||||
$notifyBy[] = GoogleChatChannel::class;
|
||||
}
|
||||
|
||||
if (Setting::getSettings()->webhook_selected == 'microsoft' && Setting::getSettings()->webhook_endpoint) {
|
||||
|
||||
$notifyBy[] = MicrosoftTeamsChannel::class;
|
||||
}
|
||||
|
||||
if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) {
|
||||
$notifyBy[] = SlackWebhookChannel::class;
|
||||
}
|
||||
|
||||
return $notifyBy;
|
||||
}
|
||||
|
||||
public function toSlack()
|
||||
{
|
||||
$target = $this->target;
|
||||
$admin = $this->admin;
|
||||
$item = $this->item;
|
||||
$note = $this->note;
|
||||
$botname = ($this->settings->webhook_botname) ? $this->settings->webhook_botname : 'Snipe-Bot';
|
||||
$channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : '';
|
||||
|
||||
$fields = [
|
||||
trans('general.to') => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
|
||||
trans('general.by') => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
|
||||
];
|
||||
|
||||
if ($item->location) {
|
||||
$fields[trans('general.location')] = $item->location->name;
|
||||
}
|
||||
|
||||
if ($item->company) {
|
||||
$fields[trans('general.company')] = $item->company->name;
|
||||
}
|
||||
|
||||
return (new SlackMessage)
|
||||
->content(':arrow_up: :package: '.trans('mail.Component_checkout_notification'))
|
||||
->from($botname)
|
||||
->to($channel)
|
||||
->attachment(function ($attachment) use ($item, $note, $admin, $fields) {
|
||||
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
|
||||
->fields($fields)
|
||||
->content($note);
|
||||
});
|
||||
}
|
||||
public function toMicrosoftTeams()
|
||||
{
|
||||
$target = $this->target;
|
||||
$admin = $this->admin;
|
||||
$item = $this->item;
|
||||
$note = $this->note;
|
||||
|
||||
if(!Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows')) {
|
||||
return MicrosoftTeamsMessage::create()
|
||||
->to($this->settings->webhook_endpoint)
|
||||
->type('success')
|
||||
->addStartGroupToSection('activityTitle')
|
||||
->title(trans('mail.Component_checkout_notification'))
|
||||
->addStartGroupToSection('activityText')
|
||||
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle')
|
||||
->fact(trans('mail.Component_checkout_notification')." by ", $admin->present()->fullName())
|
||||
->fact(trans('mail.assigned_to'), $target->present()->fullName())
|
||||
->fact(trans('admin/consumables/general.remaining'), $item->numRemaining())
|
||||
->fact(trans('mail.notes'), $note ?: '');
|
||||
}
|
||||
|
||||
$message = trans('mail.Component_checkout_notification');
|
||||
$details = [
|
||||
trans('mail.assigned_to') => $target->present()->fullName(),
|
||||
trans('mail.item') => htmlspecialchars_decode($item->present()->name),
|
||||
trans('mail.Component_checkout_notification').' by' => $admin->present()->fullName(),
|
||||
trans('admin/consumables/general.remaining') => $item->numRemaining(),
|
||||
trans('mail.notes') => $note ?: '',
|
||||
];
|
||||
|
||||
return array($message, $details);
|
||||
}
|
||||
public function toGoogleChat()
|
||||
{
|
||||
$target = $this->target;
|
||||
$item = $this->item;
|
||||
$note = $this->note;
|
||||
|
||||
return GoogleChatMessage::create()
|
||||
->to($this->settings->webhook_endpoint)
|
||||
->card(
|
||||
Card::create()
|
||||
->header(
|
||||
'<strong>'.trans('mail.Component_checkout_notification').'</strong>' ?: '',
|
||||
htmlspecialchars_decode($item->present()->name) ?: '',
|
||||
)
|
||||
->section(
|
||||
Section::create(
|
||||
KeyValue::create(
|
||||
trans('mail.assigned_to') ?: '',
|
||||
$target->present()->fullName() ?: '',
|
||||
trans('admin/consumables/general.remaining').': '.$item->numRemaining(),
|
||||
)
|
||||
->onClick(route('api.assets.show', $target->id))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
@ -8,11 +8,15 @@ return [
|
||||
'Asset_Checkout_Notification' => 'Asset checked out',
|
||||
'Confirm_Accessory_Checkin' => 'Accessory checkin confirmation',
|
||||
'Confirm_Asset_Checkin' => 'Asset checkin confirmation',
|
||||
'Confirm_component_checkin' => 'Component checkin confirmation',
|
||||
'Confirm_accessory_delivery' => 'Accessory delivery confirmation',
|
||||
'Confirm_asset_delivery' => 'Asset delivery confirmation',
|
||||
'Confirm_consumable_delivery' => 'Consumable delivery confirmation',
|
||||
'Confirm_component_delivery' => 'Component delivery confirmation',
|
||||
'Confirm_license_delivery' => 'License delivery confirmation',
|
||||
'Consumable_checkout_notification' => 'Consumable checked out',
|
||||
'Component_checkout_notification' => 'Component checked out',
|
||||
'Component_checkin_notification' => 'Component checked in',
|
||||
'Days' => 'Days',
|
||||
'Expected_Checkin_Date' => 'An asset checked out to you is due to be checked back in on :date',
|
||||
'Expected_Checkin_Notification' => 'Reminder: :name checkin deadline approaching',
|
||||
|
||||
@ -41,7 +41,31 @@
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@if ($component->requireAcceptance() || $component->getEula() || ($snipeSettings->webhook_endpoint!=''))
|
||||
<div class="form-group notification-callout">
|
||||
<div class="col-md-8 col-md-offset-3">
|
||||
<div class="callout callout-info">
|
||||
|
||||
@if ($component->category->require_acceptance=='1')
|
||||
<i class="far fa-envelope"></i>
|
||||
{{ trans('admin/categories/general.required_acceptance') }}
|
||||
<br>
|
||||
@endif
|
||||
|
||||
@if ($component->getEula())
|
||||
<i class="far fa-envelope"></i>
|
||||
{{ trans('admin/categories/general.required_eula') }}
|
||||
<br>
|
||||
@endif
|
||||
|
||||
@if ($snipeSettings->webhook_endpoint!='')
|
||||
<i class="fab fa-slack"></i>
|
||||
{{ trans('general.webhook_msg_note') }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Note -->
|
||||
<div class="form-group{{ $errors->has('note') ? ' error' : '' }}">
|
||||
|
||||
25
resources/views/mail/markdown/checkin-component.blade.php
Normal file
25
resources/views/mail/markdown/checkin-component.blade.php
Normal file
@ -0,0 +1,25 @@
|
||||
@component('mail::message')
|
||||
# {{ trans('mail.hello') }} {{ $target->assignedto->present()->fullName() }},
|
||||
|
||||
{{ trans('mail.the_following_item') }}
|
||||
|
||||
@component('mail::table')
|
||||
| | |
|
||||
| ------------- | ------------- |
|
||||
| **{{ trans('general.component') }}** | {{ $item->name }} |
|
||||
@if (isset($item->manufacturer))
|
||||
| **{{ trans('general.manufacturer') }}** | {{ $item->manufacturer->name }} |
|
||||
@endif
|
||||
@if ($admin)
|
||||
| **{{ trans('general.administrator') }}** | {{ $admin->present()->fullName() }} |
|
||||
@endif
|
||||
@if ($note)
|
||||
| **{{ trans('mail.additional_notes') }}** | {{ $note }} |
|
||||
@endif
|
||||
@endcomponent
|
||||
|
||||
{{ trans('mail.best_regards') }}
|
||||
|
||||
{{ $snipeSettings->site_name }}
|
||||
|
||||
@endcomponent
|
||||
50
resources/views/mail/markdown/checkout-component.blade.php
Normal file
50
resources/views/mail/markdown/checkout-component.blade.php
Normal file
@ -0,0 +1,50 @@
|
||||
@component('mail::message')
|
||||
# {{ trans('mail.hello') }} {{ $target->assignedto->present()->fullName() }},
|
||||
|
||||
{{ trans('mail.new_item_checked') }}
|
||||
|
||||
@component('mail::table')
|
||||
| | |
|
||||
| ------------- | ------------- |
|
||||
@if (isset($checkout_date))
|
||||
| **{{ trans('mail.checkout_date') }}** | {{ $checkout_date }} |
|
||||
@endif
|
||||
| **{{ trans('general.component') }}** | {{ $item->name }} |
|
||||
@if (isset($qty))
|
||||
| **{{ trans('general.qty') }}** | {{ $qty }} |
|
||||
@endif
|
||||
@if (isset($item->manufacturer))
|
||||
| **{{ trans('general.manufacturer') }}** | {{ $item->manufacturer->name }} |
|
||||
@endif
|
||||
@if ($note)
|
||||
| **{{ trans('mail.additional_notes') }}** | {{ $note }} |
|
||||
@endif
|
||||
@if ($admin)
|
||||
| **{{ trans('general.administrator') }}** | {{ $admin->present()->fullName() }} |
|
||||
@endif
|
||||
@endcomponent
|
||||
|
||||
@if (($req_accept == 1) && ($eula!=''))
|
||||
{{ trans('mail.read_the_terms_and_click') }}
|
||||
@elseif (($req_accept == 1) && ($eula==''))
|
||||
{{ trans('mail.click_on_the_link_asset') }}
|
||||
@elseif (($req_accept == 0) && ($eula!=''))
|
||||
{{ trans('mail.read_the_terms') }}
|
||||
@endif
|
||||
|
||||
@if ($eula)
|
||||
@component('mail::panel')
|
||||
{!! $eula !!}
|
||||
@endcomponent
|
||||
@endif
|
||||
|
||||
@if ($req_accept == 1)
|
||||
**[✔ {{ trans('mail.i_have_read') }}]({{ $accept_url }})**
|
||||
@endif
|
||||
|
||||
|
||||
{{ trans('mail.best_regards') }}
|
||||
|
||||
{{ $snipeSettings->site_name }}
|
||||
|
||||
@endcomponent
|
||||
@ -52,7 +52,12 @@ Route::group(['middleware' => 'auth'], function () {
|
||||
[LabelsController::class, 'show']
|
||||
)->where('labelName', '.*')->name('labels.show');
|
||||
|
||||
Route::get('/test-email', function () {
|
||||
$mailable = new \App\Mail\CheckoutComponentMail(
|
||||
|
||||
);
|
||||
return $mailable->render(); // dumps HTML
|
||||
});
|
||||
/*
|
||||
* Manufacturers
|
||||
*/
|
||||
|
||||
@ -4,6 +4,7 @@ namespace Tests\Feature\Notifications\Webhooks;
|
||||
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Category;
|
||||
use App\Notifications\CheckinComponentNotification;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use App\Events\CheckoutableCheckedIn;
|
||||
@ -96,6 +97,31 @@ class SlackNotificationsUponCheckinTest extends TestCase
|
||||
|
||||
$this->assertNoSlackNotificationSent(CheckinAssetNotification::class);
|
||||
}
|
||||
#[DataProvider('assetCheckInTargets')]
|
||||
public function testComponentCheckinSendsSlackNotificationWhenSettingEnabled($checkoutTarget)
|
||||
{
|
||||
$this->settings->enableSlackWebhook();
|
||||
|
||||
$this->fireCheckInEvent(
|
||||
Component::factory()->create(),
|
||||
$checkoutTarget(),
|
||||
);
|
||||
|
||||
$this->assertSlackNotificationSent(CheckinComponentNotification::class);
|
||||
}
|
||||
|
||||
#[DataProvider('assetCheckInTargets')]
|
||||
public function testComponentCheckinDoesNotSendSlackNotificationWhenSettingDisabled($checkoutTarget)
|
||||
{
|
||||
$this->settings->disableSlackWebhook();
|
||||
|
||||
$this->fireCheckInEvent(
|
||||
Component::factory()->create(),
|
||||
$checkoutTarget(),
|
||||
);
|
||||
|
||||
$this->assertNoSlackNotificationSent(CheckinComponentNotification::class);
|
||||
}
|
||||
|
||||
public function testSlackNotificationIsStillSentWhenCategoryEmailIsNotSetToSendEmails()
|
||||
{
|
||||
@ -118,18 +144,6 @@ class SlackNotificationsUponCheckinTest extends TestCase
|
||||
$this->assertSlackNotificationSent(CheckinAssetNotification::class);
|
||||
}
|
||||
|
||||
public function testComponentCheckinDoesNotSendSlackNotification()
|
||||
{
|
||||
$this->settings->enableSlackWebhook();
|
||||
|
||||
$this->fireCheckInEvent(
|
||||
Component::factory()->create(),
|
||||
Asset::factory()->laptopMbp()->create(),
|
||||
);
|
||||
|
||||
Notification::assertNothingSent();
|
||||
}
|
||||
|
||||
#[DataProvider('licenseCheckInTargets')]
|
||||
public function testLicenseCheckinSendsSlackNotificationWhenSettingEnabled($checkoutTarget)
|
||||
{
|
||||
|
||||
@ -4,6 +4,8 @@ namespace Tests\Feature\Notifications\Webhooks;
|
||||
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Category;
|
||||
use App\Notifications\CheckoutComponentNotification;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
@ -30,12 +32,13 @@ class SlackNotificationsUponCheckoutTest extends TestCase
|
||||
parent::setUp();
|
||||
|
||||
Notification::fake();
|
||||
Mail::fake();
|
||||
}
|
||||
|
||||
public static function assetCheckoutTargets(): array
|
||||
{
|
||||
return [
|
||||
'Asset checked out to user' => [fn() => User::factory()->create()],
|
||||
'Asset checked out to user' => [fn() => User::factory()->create(['email' => null])],
|
||||
'Asset checked out to asset' => [fn() => Asset::factory()->laptopMbp()->create()],
|
||||
'Asset checked out to location' => [fn() => Location::factory()->create()],
|
||||
];
|
||||
@ -44,7 +47,7 @@ class SlackNotificationsUponCheckoutTest extends TestCase
|
||||
public static function licenseCheckoutTargets(): array
|
||||
{
|
||||
return [
|
||||
'License checked out to user' => [fn() => User::factory()->create()],
|
||||
'License checked out to user' => [fn() => User::factory()->create(['email' => null])],
|
||||
'License checked out to asset' => [fn() => Asset::factory()->laptopMbp()->create()],
|
||||
];
|
||||
}
|
||||
@ -98,7 +101,41 @@ class SlackNotificationsUponCheckoutTest extends TestCase
|
||||
|
||||
$this->assertNoSlackNotificationSent(CheckoutAssetNotification::class);
|
||||
}
|
||||
#[DataProvider('assetCheckoutTargets')]
|
||||
public function testComponentCheckoutSendsSlackNotificationWhenSettingEnabled($checkoutTarget)
|
||||
{
|
||||
$this->settings->enableSlackWebhook();
|
||||
$component = Component::factory()->create([
|
||||
'category_id' => Category::factory()->create([
|
||||
'require_acceptance' => false,
|
||||
'eula_text' => null,
|
||||
]),
|
||||
]);
|
||||
$this->fireCheckOutEvent(
|
||||
$component,
|
||||
$checkoutTarget(),
|
||||
);
|
||||
|
||||
$this->assertSlackNotificationSent(CheckoutComponentNotification::class);
|
||||
}
|
||||
|
||||
#[DataProvider('assetCheckoutTargets')]
|
||||
public function testComponentCheckoutDoesNotSendSlackNotificationWhenSettingDisabled($checkoutTarget)
|
||||
{
|
||||
$this->settings->disableSlackWebhook();
|
||||
$component = Component::factory()->create([
|
||||
'category_id' => Category::factory()->create([
|
||||
'require_acceptance' => false,
|
||||
'eula_text' => null,
|
||||
]),
|
||||
]);
|
||||
$this->fireCheckOutEvent(
|
||||
$component,
|
||||
$checkoutTarget(),
|
||||
);
|
||||
|
||||
$this->assertNoSlackNotificationSent(CheckoutComponentNotification::class);
|
||||
}
|
||||
public function testSlackNotificationIsStillSentWhenCategoryEmailIsNotSetToSendEmails()
|
||||
{
|
||||
$this->settings->enableSlackWebhook();
|
||||
@ -120,18 +157,6 @@ class SlackNotificationsUponCheckoutTest extends TestCase
|
||||
$this->assertSlackNotificationSent(CheckoutAssetNotification::class);
|
||||
}
|
||||
|
||||
public function testComponentCheckoutDoesNotSendSlackNotification()
|
||||
{
|
||||
$this->settings->enableSlackWebhook();
|
||||
|
||||
$this->fireCheckOutEvent(
|
||||
Component::factory()->create(),
|
||||
Asset::factory()->laptopMbp()->create(),
|
||||
);
|
||||
|
||||
Notification::assertNothingSent();
|
||||
}
|
||||
|
||||
public function testConsumableCheckoutSendsSlackNotificationWhenSettingEnabled()
|
||||
{
|
||||
$this->settings->enableSlackWebhook();
|
||||
|
||||
Reference in New Issue
Block a user