Merge branch 'develop' into saving_custom_report_template

This commit is contained in:
Marcus Moore 2024-11-12 10:36:07 -08:00
commit 53de97db4e
No known key found for this signature in database
65 changed files with 28619 additions and 18700 deletions

View file

@ -77,7 +77,3 @@ jobs:
DB_PORT: ${{ job.services.mysql.ports[3306] }}
DB_USERNAME: root
run: php artisan test
- name: Test failure
if: ${{ failure() }}
run: docker exec "$PROJECT_NAME-php-fpm" cat storage/logs/laravel.log

View file

@ -75,7 +75,3 @@ jobs:
DB_USERNAME: snipeit
DB_PASSWORD: password
run: php artisan test
- name: Test failure
if: ${{ failure() }}
run: docker exec "$PROJECT_NAME-php-fpm" cat storage/logs/laravel.log

View file

@ -59,7 +59,3 @@ jobs:
env:
DB_CONNECTION: sqlite_testing
run: php artisan test
- name: Test failure
if: ${{ failure() }}
run: docker exec "$PROJECT_NAME-php-fpm" cat storage/logs/laravel.log

View file

@ -16,6 +16,7 @@ use Illuminate\Support\Facades\Crypt;
use Illuminate\Contracts\Encryption\DecryptException;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Intervention\Image\ImageManagerStatic as Image;
use Illuminate\Support\Facades\Session;
@ -708,6 +709,28 @@ class Helper
return $randomString;
}
/**
* A method to be used to handle deprecations notifications, currently handling MS Teams. more can be added when needed.
*
*
* @author [Godfrey Martinez]
* @since [v7.0.14]
* @return array
*/
public static function deprecationCheck() : array {
// The check and message that the user is still using the deprecated version
$deprecations = [
'ms_teams_deprecated' => array(
'check' => !Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows'),
'message' => 'The Microsoft Teams webhook URL being used will be deprecated Jan 31st, 2025. <a class="btn btn-primary" href="' . route('settings.slack.index') . '">Change webhook endpoint</a>'),
];
// if item of concern is being used and its being used with the deprecated values return the notification array.
if(Setting::getSettings()->webhook_selected === 'microsoft' && $deprecations['ms_teams_deprecated']['check']) {
return $deprecations;
}
return [];
}
/**
* This nasty little method gets the low inventory info for the

View file

@ -338,4 +338,5 @@ class AcceptanceController extends Controller
return redirect()->to('account/accept')->with('success', $return_msg);
}
}

View file

@ -37,10 +37,18 @@ class ActionlogController extends Controller
}
}
public function getStoredEula($filename) : Response | BinaryFileResponse
public function getStoredEula($filename) : Response | BinaryFileResponse | RedirectResponse
{
$this->authorize('view', \App\Models\Asset::class);
$file = config('app.private_uploads').'/eula-pdfs/'.$filename;
return response()->download($file);
if (Storage::exists($file)) {
return response()->download($file);
}
return redirect()->back()->with('error', trans('general.file_does_not_exist'));
}
}

View file

@ -44,21 +44,6 @@ class ReportsController extends Controller
});
}
if ($request->filled('action_type')) {
$actionlogs = $actionlogs->where('action_type', '=', $request->input('action_type'))->orderBy('created_at', 'desc');
}
if ($request->filled('created_by')) {
$actionlogs = $actionlogs->where('created_by', '=', $request->input('created_by'));
}
if ($request->filled('action_source')) {
$actionlogs = $actionlogs->where('action_source', '=', $request->input('action_source'))->orderBy('created_at', 'desc');
}
if ($request->filled('remote_ip')) {
$actionlogs = $actionlogs->where('remote_ip', '=', $request->input('remote_ip'))->orderBy('created_at', 'desc');
}
if ($request->filled('uploads')) {
$actionlogs = $actionlogs->whereNotNull('filename')->orderBy('created_at', 'desc');
@ -74,6 +59,8 @@ class ReportsController extends Controller
'note',
'remote_ip',
'user_agent',
'target_type',
'item_type',
'action_source',
'action_date',
];

View file

@ -245,10 +245,12 @@ class BulkAssetsController extends Controller
|| ($request->filled('status_id'))
|| ($request->filled('model_id'))
|| ($request->filled('next_audit_date'))
|| ($request->filled('asset_eol_date'))
|| ($request->filled('null_name'))
|| ($request->filled('null_purchase_date'))
|| ($request->filled('null_expected_checkin_date'))
|| ($request->filled('null_next_audit_date'))
|| ($request->filled('null_asset_eol_date'))
|| ($request->anyFilled($custom_field_columns))
) {
@ -271,7 +273,8 @@ class BulkAssetsController extends Controller
->conditionallyAddItem('requestable')
->conditionallyAddItem('supplier_id')
->conditionallyAddItem('warranty_months')
->conditionallyAddItem('next_audit_date');
->conditionallyAddItem('next_audit_date')
->conditionallyAddItem('asset_eol_date');
foreach ($custom_field_columns as $key => $custom_field_column) {
$this->conditionallyAddItem($custom_field_column);
}
@ -316,6 +319,17 @@ class BulkAssetsController extends Controller
$this->update_array['next_audit_date'] = null;
}
if ($request->input('null_asset_eol_date')=='1') {
$this->update_array['asset_eol_date'] = null;
// If they are nulling the EOL date to allow it to calculate, set eol explicit to 0
if ($request->input('calc_eol')=='1') {
$this->update_array['eol_explicit'] = 0;
}
}
if ($request->filled('purchase_cost')) {
$this->update_array['purchase_cost'] = $request->input('purchase_cost');
}

View file

@ -25,7 +25,7 @@ class StoreNotificationSettings extends FormRequest
{
return [
'alert_email' => 'email_array|nullable',
'admin_cc_email' => 'email|nullable',
'admin_cc_email' => 'email_array|nullable',
'alert_threshold' => 'numeric|nullable|gt:0',
'alert_interval' => 'numeric|nullable|gt:0',
'audit_warning_days' => 'numeric|nullable|gt:0',

View file

@ -3,13 +3,20 @@
namespace App\Listeners;
use App\Events\CheckoutableCheckedOut;
use App\Mail\CheckinAccessoryMail;
use App\Mail\CheckinLicenseMail;
use App\Mail\CheckoutAccessoryMail;
use App\Mail\CheckoutAssetMail;
use App\Mail\CheckinAssetMail;
use App\Mail\CheckoutConsumableMail;
use App\Mail\CheckoutLicenseMail;
use App\Models\Accessory;
use App\Models\Asset;
use App\Models\CheckoutAcceptance;
use App\Models\Component;
use App\Models\Consumable;
use App\Models\LicenseSeat;
use App\Models\Recipients\AdminRecipient;
use App\Models\Location;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\CheckinAccessoryNotification;
@ -20,9 +27,11 @@ use App\Notifications\CheckoutAssetNotification;
use App\Notifications\CheckoutConsumableNotification;
use App\Notifications\CheckoutLicenseSeatNotification;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use Exception;
use Illuminate\Support\Facades\Log;
use Osama\LaravelTeamsNotification\TeamsNotification;
class CheckoutableListener
{
@ -43,33 +52,56 @@ class CheckoutableListener
/**
* Make a checkout acceptance and attach it in the notification
*/
$settings = Setting::getSettings();
$acceptance = $this->getCheckoutAcceptance($event);
$notifiables = $this->getNotifiables($event);
$adminCcEmailsArray = [];
if($settings->admin_cc_email !== '') {
$adminCcEmail = $settings->admin_cc_email;
$adminCcEmailsArray = array_map('trim', explode(',', $adminCcEmail));
}
$ccEmails = array_filter($adminCcEmailsArray);
$mailable = $this->getCheckoutMailType($event, $acceptance);
$notifiable = $this->getNotifiables($event);
if (!$event->checkedOutTo->locale){
$mailable->locale($event->checkedOutTo->locale);
}
// Send email notifications
try {
foreach ($notifiables as $notifiable) {
if ($notifiable instanceof User && $notifiable->email != '') {
if (! $event->checkedOutTo->locale){
Notification::locale(Setting::getSettings()->locale)->send($notifiable, $this->getCheckoutNotification($event, $acceptance));
}
else {
Notification::send($notifiable, $this->getCheckoutNotification($event, $acceptance));
}
}
}
/**
* Send an email if any of the following conditions are met:
* 1. The asset requires acceptance
* 2. The item has a EULA
* 3. The item should send an email at check-in/check-out
*/
// Send Webhook notification
if ($this->shouldSendWebhookNotification()) {
// Slack doesn't include the URL in its messaging format, so this is needed to hit the endpoint
if (Setting::getSettings()->webhook_selected === 'slack' || Setting::getSettings()->webhook_selected === 'general') {
Notification::route('slack', Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckoutNotification($event, $acceptance));
} else {
Notification::route(Setting::getSettings()->webhook_selected, Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckoutNotification($event, $acceptance));
if ($event->checkoutable->requireAcceptance() || $event->checkoutable->getEula() ||
(method_exists($event->checkoutable, 'checkin_email') && $event->checkoutable->checkin_email())) {
if (!empty($notifiable)) {
Mail::to($notifiable)->cc($ccEmails)->send($mailable);
} elseif (!empty($ccEmails)) {
Mail::cc($ccEmails)->send($mailable);
}
Log::info('Sending email, Locale: ' . ($event->checkedOutTo->locale ?? 'default'));
}
} catch (ClientException $e) {
Log::debug("Exception caught during checkout email: " . $e->getMessage());
} catch (Exception $e) {
Log::debug("Exception caught during checkout email: " . $e->getMessage());
}
// Send Webhook notification
try{
if ($this->shouldSendWebhookNotification()) {
if (Setting::getSettings()->webhook_selected === 'microsoft') {
$message = $this->getCheckoutNotification($event)->toMicrosoftTeams();
$notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint);
$notification->success()->sendMessage($message[0], $message[1]); // Send the message to Microsoft Teams
} else {
Notification::route(Setting::getSettings()->webhook_selected, Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckoutNotification($event, $acceptance));
}
}
}
} catch (ClientException $e) {
Log::debug("Exception caught during checkout notification: " . $e->getMessage());
} catch (Exception $e) {
@ -103,34 +135,51 @@ class CheckoutableListener
}
}
}
$settings = Setting::getSettings();
$adminCcEmailsArray = [];
$notifiables = $this->getNotifiables($event);
if($settings->admin_cc_email !== '') {
$adminCcEmail = $settings->admin_cc_email;
$adminCcEmailsArray = array_map('trim', explode(',', $adminCcEmail));
}
$ccEmails = array_filter($adminCcEmailsArray);
$mailable = $this->getCheckinMailType($event);
$notifiable = $this->getNotifiables($event);
if (!$event->checkedOutTo->locale){
$mailable->locale($event->checkedOutTo->locale);
}
// Send email notifications
try {
foreach ($notifiables as $notifiable) {
if ($notifiable instanceof User && $notifiable->email != '') {
if (! $event->checkedOutTo->locale){
Notification::locale(Setting::getSettings()->locale)->send($notifiable, $this->getCheckoutNotification($event, $acceptance));
}
else {
Notification::send($notifiable, $this->getCheckinNotification($event));
/**
* Send an email if any of the following conditions are met:
* 1. The asset requires acceptance
* 2. The item has a EULA
* 3. The item should send an email at check-in/check-out
*/
if ($event->checkoutable->requireAcceptance() || $event->checkoutable->getEula() ||
(method_exists($event->checkoutable, 'checkin_email') && $event->checkoutable->checkin_email())) {
if (!empty($notifiable)) {
Mail::to($notifiable)->cc($ccEmails)->send($mailable);
} elseif (!empty($ccEmails)){
Mail::cc($ccEmails)->send($mailable);
}
Log::info('Sending email, Locale: ' . $event->checkedOutTo->locale);
}
}
// Send Webhook notification
} catch (ClientException $e) {
Log::debug("Exception caught during checkin email: " . $e->getMessage());
} catch (Exception $e) {
Log::debug("Exception caught during checkin email: " . $e->getMessage());
}
// Send Webhook notification
try {
if ($this->shouldSendWebhookNotification()) {
// Slack doesn't include the URL in its messaging format, so this is needed to hit the endpoint
if (Setting::getSettings()->webhook_selected === 'slack' || Setting::getSettings()->webhook_selected === 'general') {
Notification::route('slack', Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckinNotification($event));
} else {
Notification::route(Setting::getSettings()->webhook_selected, Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckinNotification($event));
}
}
} catch (ClientException $e) {
Log::warning("Exception caught during checkout notification: " . $e->getMessage());
Log::warning("Exception caught during checkin notification: " . $e->getMessage());
} catch (Exception $e) {
Log::warning("Exception caught during checkin notification: " . $e->getMessage());
}
@ -159,33 +208,6 @@ class CheckoutableListener
return $acceptance;
}
/**
* Gets the entities to be notified of the passed event
*
* @param Event $event
* @return Collection
*/
private function getNotifiables($event)
{
$notifiables = collect();
/**
* Notify who checked out the item as long as the model can route notifications
*/
if (method_exists($event->checkedOutTo, 'routeNotificationFor')) {
$notifiables->push($event->checkedOutTo);
}
/**
* Notify Admin users if the settings is activated
*/
if ((Setting::getSettings()) && (Setting::getSettings()->admin_cc_email != '')) {
$notifiables->push(new AdminRecipient());
}
return $notifiables;
}
/**
* Get the appropriate notification for the event
*
@ -234,7 +256,7 @@ class CheckoutableListener
break;
case Consumable::class:
$notificationClass = CheckoutConsumableNotification::class;
break;
break;
case LicenseSeat::class:
$notificationClass = CheckoutLicenseSeatNotification::class;
break;
@ -243,6 +265,43 @@ class CheckoutableListener
return new $notificationClass($event->checkoutable, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note);
}
private function getCheckoutMailType($event, $acceptance){
$lookup = [
Accessory::class => CheckoutAccessoryMail::class,
Asset::class => CheckoutAssetMail::class,
LicenseSeat::class => CheckoutLicenseMail::class,
Consumable::class => CheckoutConsumableMail::class,
];
$mailable= $lookup[get_class($event->checkoutable)];
return new $mailable($event->checkoutable, $event->checkedOutTo, $event->checkedOutBy, $event->note, $acceptance);
}
private function getCheckinMailType($event){
$lookup = [
Accessory::class => CheckinAccessoryMail::class,
Asset::class => CheckinAssetMail::class,
LicenseSeat::class => CheckinLicenseMail::class,
];
$mailable= $lookup[get_class($event->checkoutable)];
return new $mailable($event->checkoutable, $event->checkedOutTo, $event->checkedInBy, $event->note);
}
private function getNotifiables($event){
if($event->checkedOutTo instanceof Asset){
$event->checkedOutTo->load('assignedTo');
return $event->checkedOutTo->assignedto?->email ?? '';
}
else if($event->checkedOutTo instanceof Location) {
return $event->checkedOutTo->manager?->email ?? '';
}
else{
return $event->checkedOutTo->email;
}
}
/**
* Register the listeners for the subscriber.

View file

@ -4,10 +4,11 @@ namespace App\Livewire;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use Livewire\Component;
use App\Models\Setting;
use App\Helpers\Helper;
use Osama\LaravelTeamsNotification\TeamsNotification;
class SlackSettingsForm extends Component
{
public $webhook_endpoint;
@ -19,6 +20,7 @@ class SlackSettingsForm extends Component
public $webhook_placeholder;
public $webhook_icon;
public $webhook_selected;
public $teams_webhook_deprecated;
public array $webhook_text;
public Setting $setting;
@ -62,7 +64,7 @@ class SlackSettingsForm extends Component
"name" => trans('admin/settings/general.ms_teams'),
"icon" => "fa-brands fa-microsoft",
"placeholder" => "https://abcd.webhook.office.com/webhookb2/XXXXXXX",
"link" => "https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook?tabs=dotnet#create-incoming-webhooks-1",
"link" => "https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498",
"test" => "msTeamTestWebhook"
),
];
@ -79,15 +81,17 @@ class SlackSettingsForm extends Component
$this->webhook_channel = $this->setting->webhook_channel;
$this->webhook_botname = $this->setting->webhook_botname;
$this->webhook_options = $this->setting->webhook_selected;
if($this->webhook_selected == 'microsoft' || $this->webhook_selected == 'google'){
$this->teams_webhook_deprecated = !Str::contains($this->webhook_endpoint, 'workflows');
if($this->webhook_selected === 'microsoft' || $this->webhook_selected === 'google'){
$this->webhook_channel = '#NA';
}
if($this->setting->webhook_endpoint != null && $this->setting->webhook_channel != null){
$this->isDisabled= '';
}
if($this->webhook_selected === 'microsoft' && $this->teams_webhook_deprecated) {
session()->flash('warning', 'The selected Microsoft Teams webhook URL will be deprecated Jan 31st, 2025. Please use a workflow URL. Microsofts Documentation on creating a workflow can be found <a href="https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498" target="_blank"> here.</a>');
}
}
public function updated($field) {
@ -109,7 +113,6 @@ class SlackSettingsForm extends Component
if($this->webhook_selected == 'microsoft' || $this->webhook_selected == 'google'){
$this->webhook_channel = '#NA';
}
}
private function isButtonDisabled() {
@ -126,7 +129,9 @@ class SlackSettingsForm extends Component
public function render()
{
$this->isButtonDisabled();
return view('livewire.slack-settings-form');
}
public function testWebhook(){
@ -236,20 +241,32 @@ class SlackSettingsForm extends Component
}
public function msTeamTestWebhook(){
$payload =
[
"@type" => "MessageCard",
"@context" => "http://schema.org/extensions",
"summary" => trans('mail.snipe_webhook_summary'),
"title" => trans('mail.snipe_webhook_test'),
"text" => trans('general.webhook_test_msg', ['app' => $this->webhook_name]),
];
try {
try {
$response = Http::withHeaders([
'content-type' => 'applications/json',
])->post($this->webhook_endpoint,
$payload)->throw();
if($this->teams_webhook_deprecated){
//will use the deprecated webhook format
$payload =
[
"@type" => "MessageCard",
"@context" => "http://schema.org/extensions",
"summary" => trans('mail.snipe_webhook_summary'),
"title" => trans('mail.snipe_webhook_test'),
"text" => trans('general.webhook_test_msg', ['app' => $this->webhook_name]),
];
$response = Http::withHeaders([
'content-type' => 'applications/json',
])->post($this->webhook_endpoint,
$payload)->throw();
}
else {
$notification = new TeamsNotification($this->webhook_endpoint);
$message = trans('general.webhook_test_msg', ['app' => $this->webhook_name]);
$notification->success()->sendMessage($message);
$response = Http::withHeaders([
'content-type' => 'applications/json',
])->post($this->webhook_endpoint);
}
if(($response->getStatusCode() == 302)||($response->getStatusCode() == 301)){
return session()->flash('error' , trans('admin/settings/message.webhook.error_redirect', ['endpoint' => $this->webhook_endpoint]));

View file

@ -0,0 +1,70 @@
<?php
namespace App\Mail;
use App\Models\Accessory;
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 CheckinAccessoryMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct(Accessory $accessory, $checkedOutTo, User $checkedInby, $note)
{
$this->item = $accessory;
$this->target = $checkedOutTo;
$this->admin = $checkedInby;
$this->note = $note;
$this->settings = Setting::getSettings();
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
return new Envelope(
from: $from,
subject: trans('mail.Accessory_Checkin_Notification'),
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'mail.markdown.checkin-accessory',
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 [];
}
}

View file

@ -0,0 +1,93 @@
<?php
namespace App\Mail;
use App\Helpers\Helper;
use App\Models\Asset;
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\Notifications\Messages\MailMessage;
use Illuminate\Queue\SerializesModels;
class CheckinAssetMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct(Asset $asset, $checkedOutTo, User $checkedInBy, $note)
{
$this->target = $checkedOutTo;
$this->item = $asset;
$this->admin = $checkedInBy;
$this->note = $note;
$this->settings = Setting::getSettings();
$this->expected_checkin = '';
if ($this->item->expected_checkin) {
$this->expected_checkin = Helper::getFormattedDateObject($this->item->expected_checkin, 'date',
false);
}
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
return new Envelope(
from: $from,
subject: trans('mail.Asset_Checkin_Notification'),
);
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return Content
*/
public function content(): Content
{
$this->item->load('assetstatus');
$fields = [];
// Check if the item has custom fields associated with it
if (($this->item->model) && ($this->item->model->fieldset)) {
$fields = $this->item->model->fieldset->fields;
}
return new Content(
markdown: 'mail.markdown.checkin-asset',
with: [
'item' => $this->item,
'status' => $this->item->assetstatus?->name,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
'fields' => $fields,
'expected_checkin' => $this->expected_checkin,
],
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View file

@ -0,0 +1,71 @@
<?php
namespace App\Mail;
use App\Models\LicenseSeat;
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 CheckinLicenseMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct(LicenseSeat $licenseSeat, $checkedOutTo, User $checkedInBy, $note)
{
$this->target = $checkedOutTo;
$this->item = $licenseSeat;
$this->admin = $checkedInBy;
$this->note = $note;
$this->settings = Setting::getSettings();
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
return new Envelope(
from: $from,
subject: trans('mail.License_Checkin_Notification'),
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'mail.markdown.checkin-license',
with: [
'license_seat' => $this->item,
'license' => $this->item->license,
'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 [];
}
}

View file

@ -0,0 +1,82 @@
<?php
namespace App\Mail;
use App\Models\Accessory;
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;
use Illuminate\Support\Facades\Log;
class CheckoutAccessoryMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy,$note, $acceptance)
{
$this->item = $accessory;
$this->admin = $checkedOutBy;
$this->note = $note;
$this->checkout_qty = $accessory->checkout_qty;
$this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->settings = Setting::getSettings();
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
return new Envelope(
from: $from,
subject: (trans('mail.Accessory_Checkout_Notification')),
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
Log::debug($this->item->getImageUrl());
$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-accessory',
with: [
'item' => $this->item,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
'eula' => $eula,
'req_accept' => $req_accept,
'accept_url' => $accept_url,
'checkout_qty' => $this->checkout_qty,
],
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View file

@ -0,0 +1,110 @@
<?php
namespace App\Mail;
use App\Helpers\Helper;
use App\Models\Asset;
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\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Queue\SerializesModels;
class CheckoutAssetMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $note, $acceptance)
{
$this->item = $asset;
$this->admin = $checkedOutBy;
$this->note = $note;
$this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->settings = Setting::getSettings();
$this->last_checkout = '';
$this->expected_checkin = '';
if ($this->item->last_checkout) {
$this->last_checkout = Helper::getFormattedDateObject($this->item->last_checkout, 'date',
false);
}
if ($this->item->expected_checkin) {
$this->expected_checkin = Helper::getFormattedDateObject($this->item->expected_checkin, 'date',
false);
}
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$from = new Address(env('MAIL_FROM_ADDR', 'service@snipe-it.io'));
return new Envelope(
from: $from,
subject: trans('mail.Asset_Checkout_Notification'),
);
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return Content
*/
public function content(): Content
{
$this->item->load('assetstatus');
$eula = method_exists($this->item, 'getEula') ? $this->item->getEula() : '';
$req_accept = method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0;
$fields = [];
// Check if the item has custom fields associated with it
if (($this->item->model) && ($this->item->model->fieldset)) {
$fields = $this->item->model->fieldset->fields;
}
$accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance);
return new Content(
markdown: 'mail.markdown.checkout-asset',
with: [
'item' => $this->item,
'admin' => $this->admin,
'status' => $this->item->assetstatus?->name,
'note' => $this->note,
'target' => $this->target,
'fields' => $fields,
'eula' => $eula,
'req_accept' => $req_accept,
'accept_url' => $accept_url,
'last_checkout' => $this->last_checkout,
'expected_checkin' => $this->expected_checkin,
],
);
}
/**
* Get the attachments for the message.
*
* @return array<int, Attachment>
*/
public function attachments(): array
{
return [];
}
}

View file

@ -0,0 +1,84 @@
<?php
namespace App\Mail;
use App\Models\Consumable;
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;
use Illuminate\Support\Facades\Log;
class CheckoutConsumableMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct(Consumable $consumable, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
{
$this->item = $consumable;
$this->admin = $checkedOutBy;
$this->note = $note;
$this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->qty = $consumable->checkout_qty;
$this->settings = Setting::getSettings();
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
return new Envelope(
from: $from,
subject: trans('mail.Confirm_consumable_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-consumable',
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 [];
}
}

View file

@ -0,0 +1,80 @@
<?php
namespace App\Mail;
use App\Models\LicenseSeat;
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 CheckoutLicenseMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct(LicenseSeat $licenseSeat, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
{
$this->item = $licenseSeat;
$this->admin = $checkedOutBy;
$this->note = $note;
$this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->settings = Setting::getSettings();
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
return new Envelope(
from: $from,
subject: trans('mail.Confirm_license_delivery'),
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
$eula = method_exists($this->item, 'getEula') ? $this->item->getEula() : '';
$req_accept = method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0;
$accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance);
return new Content(
markdown: 'mail.markdown.checkout-license',
with: [
'license_seat' => $this->item,
'license' => $this->item->license,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
'eula' => $eula,
'req_accept' => $req_accept,
'accept_url' => $accept_url,
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View file

@ -55,6 +55,8 @@ class Actionlog extends SnipeModel
'created_by',
'remote_ip',
'user_agent',
'item_type',
'target_type',
'action_source'
];
@ -64,10 +66,10 @@ class Actionlog extends SnipeModel
* @var array
*/
protected $searchableRelations = [
'company' => ['name'],
'adminuser' => ['first_name','last_name','username', 'email'],
'user' => ['first_name','last_name','username', 'email'],
'assets' => ['asset_tag','name'],
'company' => ['name'],
'adminuser' => ['first_name','last_name','username', 'email'],
'user' => ['first_name','last_name','username', 'email'],
'assets' => ['asset_tag','name'],
];
/**

View file

@ -184,7 +184,7 @@ class License extends Depreciable
$logAction->item_type = self::class;
$logAction->item_id = $license->id;
$logAction->created_by = auth()->id() ?: 1; // We don't have an id while running the importer from CLI.
$logAction->note = "deleted ${change} seats";
$logAction->note = "deleted {$change} seats";
$logAction->target_id = null;
$logAction->logaction('delete seats');
@ -216,7 +216,7 @@ class License extends Depreciable
$logAction->item_type = self::class;
$logAction->item_id = $license->id;
$logAction->created_by = auth()->id() ?: 1; // Importer.
$logAction->note = "added ${change} seats";
$logAction->note = "added {$change} seats";
$logAction->target_id = null;
$logAction->logaction('add seats');
}
@ -743,4 +743,4 @@ class License extends Depreciable
{
return $query->leftJoin('users as admin_sort', 'licenses.created_by', '=', 'admin_sort.id')->select('licenses.*')->orderBy('admin_sort.first_name', $order)->orderBy('admin_sort.last_name', $order);
}
}
}

View file

@ -6,9 +6,15 @@ use App\Models\Setting;
class AdminRecipient extends Recipient
{
protected $email;
public function __construct()
{
$settings = Setting::getSettings();
$this->email = trim($settings->admin_cc_email);
}
public function getEmail(){
return $this->email;
}
}

View file

@ -6,9 +6,11 @@ use App\Models\Accessory;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Channels\SlackWebhookChannel;
use Illuminate\Notifications\Messages\MailMessage;
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;
@ -55,22 +57,9 @@ class CheckinAccessoryNotification extends Notification
}
if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) {
$notifyBy[] = 'slack';
$notifyBy[] = SlackWebhookChannel::class;
}
/**
* Only send notifications to users that have email addresses
*/
if ($this->target instanceof User && $this->target->email != '') {
Log::debug('The target is a user');
if ($this->item->checkin_email()) {
$notifyBy[] = 'mail';
}
}
Log::debug('checkin_email on this category is '.$this->item->checkin_email());
return $notifyBy;
}
@ -103,18 +92,29 @@ class CheckinAccessoryNotification extends Notification
$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('Accessory_Checkin_Notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle')
->fact(trans('mail.checked_into'), $item->location->name ? $item->location->name : '')
->fact(trans('mail.Accessory_Checkin_Notification')." by ", $admin->present()->fullName())
->fact(trans('admin/consumables/general.remaining'), $item->numRemaining())
->fact(trans('mail.notes'), $note ?: '');
}
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->addStartGroupToSection('activityTitle')
->title(trans('Accessory_Checkin_Notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle')
->fact(trans('mail.checked_into'), $item->location->name ? $item->location->name : '')
->fact(trans('mail.Accessory_Checkin_Notification')." by ", $admin->present()->fullName())
->fact(trans('admin/consumables/general.remaining'), $item->numRemaining())
->fact(trans('mail.notes'), $note ?: '');
$message = trans('mail.Accessory_Checkin_Notification');
$details = [
trans('mail.accessory_name') => htmlspecialchars_decode($item->present()->name),
trans('mail.checked_into') => $item->location->name ? $item->location->name : '',
trans('mail.Accessory_Checkin_Notification'). ' by' => $admin->present()->fullName(),
trans('admin/consumables/general.remaining')=> $item->numRemaining(),
trans('mail.notes') => $note ?: '',
];
return array($message, $details);
}
public function toGoogleChat()
{
@ -142,24 +142,4 @@ class CheckinAccessoryNotification extends Notification
);
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail()
{
Log::debug('to email called');
return (new MailMessage)->markdown('notifications.markdown.checkin-accessory',
[
'item' => $this->item,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
])
->subject(trans('mail.Accessory_Checkin_Notification'));
}
}

View file

@ -7,9 +7,11 @@ use App\Models\Asset;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Channels\SlackWebhookChannel;
use Illuminate\Notifications\Messages\MailMessage;
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;
@ -50,7 +52,6 @@ class CheckinAssetNotification extends Notification
*/
public function via()
{
$notifyBy = [];
if (Setting::getSettings()->webhook_selected == 'google' && Setting::getSettings()->webhook_endpoint) {
$notifyBy[] = GoogleChatChannel::class;
@ -62,15 +63,7 @@ class CheckinAssetNotification extends Notification
}
if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) {
Log::debug('use webhook');
$notifyBy[] = 'slack';
}
/**
* Only send checkin notifications to users if the category
* has the corresponding checkbox checked.
*/
if ($this->item->checkin_email() && $this->target instanceof User && $this->target->email != '') {
$notifyBy[] = 'mail';
$notifyBy[] = SlackWebhookChannel::class;
}
return $notifyBy;
@ -106,16 +99,30 @@ class CheckinAssetNotification extends Notification
$item = $this->item;
$note = $this->note;
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->title(trans('mail.Asset_Checkin_Notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityText')
->fact(trans('mail.checked_into'), $item->location->name ? $item->location->name : '')
->fact(trans('mail.Asset_Checkin_Notification')." by ", $admin->present()->fullName())
->fact(trans('admin/hardware/form.status'), $item->assetstatus->name)
->fact(trans('mail.notes'), $note ?: '');
if(!Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows')) {
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->title(trans('mail.Asset_Checkin_Notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityText')
->fact(trans('mail.checked_into'), $item->location->name ? $item->location->name : '')
->fact(trans('mail.Asset_Checkin_Notification') . " by ", $admin->present()->fullName())
->fact(trans('admin/hardware/form.status'), $item->assetstatus->name)
->fact(trans('mail.notes'), $note ?: '');
}
$message = trans('mail.Asset_Checkin_Notification');
$details = [
trans('mail.asset') => htmlspecialchars_decode($item->present()->name),
trans('mail.checked_into') => $item->location->name ? $item->location->name : '',
trans('mail.Asset_Checkin_Notification')." by " => $admin->present()->fullName(),
trans('admin/hardware/form.status') => $item->assetstatus->name,
trans('mail.notes') => $note ?: '',
];
return array($message, $details);
}
public function toGoogleChat()
{
@ -142,35 +149,5 @@ class CheckinAssetNotification extends Notification
)
)
);
}
/**
* Get the mail representation of the notification.
*
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail()
{
$fields = [];
// Check if the item has custom fields associated with it
if (($this->item->model) && ($this->item->model->fieldset)) {
$fields = $this->item->model->fieldset->fields;
}
$message = (new MailMessage)->markdown('notifications.markdown.checkin-asset',
[
'item' => $this->item,
'status' => $this->item->assetstatus?->name,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
'fields' => $fields,
'expected_checkin' => $this->expected_checkin,
])
->subject(trans('mail.Asset_Checkin_Notification'));
return $message;
}
}

View file

@ -6,9 +6,11 @@ use App\Models\LicenseSeat;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Channels\SlackWebhookChannel;
use Illuminate\Notifications\Messages\MailMessage;
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;
@ -58,15 +60,7 @@ class CheckinLicenseSeatNotification extends Notification
}
if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) {
$notifyBy[] = 'slack';
}
/**
* Only send checkin notifications to users if the category
* has the corresponding checkbox checked.
*/
if ($this->item->checkin_email() && $this->target instanceof User && $this->target->email != '') {
$notifyBy[] = 'mail';
$notifyBy[] = SlackWebhookChannel::class;
}
return $notifyBy;
@ -109,18 +103,30 @@ class CheckinLicenseSeatNotification extends Notification
$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.License_Checkin_Notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'header')
->fact(trans('mail.License_Checkin_Notification')." by ", $admin->present()->fullName() ?: 'CLI tool')
->fact(trans('mail.checkedin_from'), $target->present()->fullName())
->fact(trans('admin/consumables/general.remaining'), $item->availCount()->count())
->fact(trans('mail.notes'), $note ?: '');
}
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->addStartGroupToSection('activityTitle')
->title(trans('mail.License_Checkin_Notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'header')
->fact(trans('mail.License_Checkin_Notification')." by ", $admin->present()->fullName() ?: 'CLI tool')
->fact(trans('mail.checkedin_from'), $target->present()->fullName())
->fact(trans('admin/consumables/general.remaining'), $item->availCount()->count())
->fact(trans('mail.notes'), $note ?: '');
$message = trans('mail.License_Checkin_Notification');
$details = [
trans('mail.checkedin_from')=> $target->present()->fullName(),
trans('mail.license_for') => htmlspecialchars_decode($item->present()->name),
trans('mail.License_Checkin_Notification')." by " => $admin->present()->fullName() ?: 'CLI tool',
trans('admin/consumables/general.remaining') => $item->availCount()->count(),
trans('mail.notes') => $note ?: '',
];
return array($message, $details);
}
public function toGoogleChat()
{
@ -149,23 +155,4 @@ class CheckinLicenseSeatNotification extends Notification
);
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail()
{
return (new MailMessage)->markdown('notifications.markdown.checkin-license',
[
'item' => $this->item,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
])
->subject(trans('mail.License_Checkin_Notification'));
}
}

View file

@ -9,6 +9,7 @@ use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
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;
@ -120,6 +121,7 @@ class CheckoutAccessoryNotification extends Notification
$item = $this->item;
$note = $this->note;
if(!Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows')) {
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
@ -133,7 +135,19 @@ class CheckoutAccessoryNotification extends Notification
->fact(trans('mail.Accessory_Checkout_Notification') . " by ", $admin->present()->fullName())
->fact(trans('admin/consumables/general.remaining'), $item->numRemaining())
->fact(trans('mail.notes'), $note ?: '');
}
$message = trans('mail.Accessory_Checkout_Notification');
$details = [
trans('mail.assigned_to') => $target->present()->name,
trans('mail.accessory_name') => htmlspecialchars_decode($item->present()->name),
trans('general.qty') => $this->checkout_qty,
trans('mail.checkedout_from') => $item->location->name ? $item->location->name : '',
trans('mail.Accessory_Checkout_Notification'). ' by' => $admin->present()->fullName(),
trans('admin/consumables/general.remaining')=> $item->numRemaining(),
trans('mail.notes') => $note ?: '',
];
return array($message, $details);
}
public function toGoogleChat()
{

View file

@ -8,9 +8,10 @@ use App\Models\Setting;
use App\Models\User;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
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\Enums\Icon;
use NotificationChannels\GoogleChat\Enums\ImageStyle;
@ -21,6 +22,9 @@ use NotificationChannels\GoogleChat\Widgets\KeyValue;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsChannel;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsMessage;
use Illuminate\Support\Facades\Log;
use Osama\LaravelTeamsNotification\Logging\TeamsLoggingChannel;
use Osama\LaravelTeamsNotification\TeamsNotification;
class CheckoutAssetNotification extends Notification
{
use Queueable;
@ -32,14 +36,11 @@ class CheckoutAssetNotification extends Notification
*/
public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
{
$this->settings = Setting::getSettings();
$this->item = $asset;
$this->admin = $checkedOutBy;
$this->note = $note;
$this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->settings = Setting::getSettings();
$this->last_checkout = '';
$this->expected_checkin = '';
@ -53,7 +54,6 @@ class CheckoutAssetNotification extends Notification
false);
}
}
/**
* Get the notification's delivery channels.
*
@ -62,61 +62,34 @@ class CheckoutAssetNotification extends Notification
public function via()
{
$notifyBy = [];
if (Setting::getSettings()->webhook_selected == 'google' && Setting::getSettings()->webhook_endpoint) {
if (Setting::getSettings()->webhook_selected === 'google' && Setting::getSettings()->webhook_endpoint) {
$notifyBy[] = GoogleChatChannel::class;
}
if (Setting::getSettings()->webhook_selected == 'microsoft' && Setting::getSettings()->webhook_endpoint) {
if (Setting::getSettings()->webhook_selected === 'microsoft' && Setting::getSettings()->webhook_endpoint) {
$notifyBy[] = MicrosoftTeamsChannel::class;
$notifyBy[] = TeamsNotification::class;
}
if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) {
if (Setting::getSettings()->webhook_selected === 'slack' || Setting::getSettings()->webhook_selected === 'general' ) {
Log::debug('use webhook');
$notifyBy[] = 'slack';
}
/**
* Only send notifications to users that have email addresses
*/
if ($this->target instanceof User && $this->target->email != '') {
/**
* Send an email if the asset requires acceptance,
* so the user can accept or decline the asset
*/
if ($this->item->requireAcceptance()) {
$notifyBy[1] = 'mail';
}
/**
* Send an email if the item has a EULA, since the user should always receive it
*/
if ($this->item->getEula()) {
$notifyBy[1] = 'mail';
}
/**
* Send an email if an email should be sent at checkin/checkout
*/
if ($this->item->checkin_email()) {
$notifyBy[1] = 'mail';
}
$notifyBy[] = SlackWebhookChannel::class;
}
return $notifyBy;
}
public function toSlack()
public function toSlack() :SlackMessage
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
$botname = ($this->settings->webhook_botname) ? $this->settings->webhook_botname : 'Snipe-Bot';
$botname = ($this->settings->webhook_botname) ?: 'Snipe-Bot';
$channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : '';
$fields = [
@ -124,7 +97,7 @@ class CheckoutAssetNotification extends Notification
'By' => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
];
if (($this->expected_checkin) && ($this->expected_checkin != '')) {
if (($this->expected_checkin) && ($this->expected_checkin !== '')) {
$fields['Expected Checkin'] = $this->expected_checkin;
}
@ -138,24 +111,33 @@ class CheckoutAssetNotification extends Notification
->content($note);
});
}
public function toMicrosoftTeams()
public function toMicrosoftTeams() : array
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->title(trans('mail.Asset_Checkout_Notification'))
->addStartGroupToSection('activityText')
->fact(trans('mail.assigned_to'), $target->present()->name)
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityText')
->fact(trans('mail.Asset_Checkout_Notification') . " by ", $admin->present()->fullName())
->fact(trans('mail.notes'), $note ?: '');
if(!Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows')) {
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->title(trans('mail.Asset_Checkout_Notification'))
->addStartGroupToSection('activityText')
->fact(trans('mail.assigned_to'), $target->present()->name)
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityText')
->fact(trans('mail.Asset_Checkout_Notification') . " by ", $admin->present()->fullName())
->fact(trans('mail.notes'), $note ?: '');
}
$message = trans('mail.Asset_Checkout_Notification');
$details = [
trans('mail.assigned_to') => $target->present()->name,
trans('mail.asset') => htmlspecialchars_decode($item->present()->name),
trans('mail.Asset_Checkout_Notification'). ' by' => $admin->present()->fullName(),
trans('mail.notes') => $note ?: '',
];
return array($message, $details);
}
public function toGoogleChat()
{
@ -184,42 +166,4 @@ public function toGoogleChat()
);
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail()
{ $this->item->load('assetstatus');
$eula = method_exists($this->item, 'getEula') ? $this->item->getEula() : '';
$req_accept = method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0;
$fields = [];
// Check if the item has custom fields associated with it
if (($this->item->model) && ($this->item->model->fieldset)) {
$fields = $this->item->model->fieldset->fields;
}
$accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance);
$message = (new MailMessage)->markdown('notifications.markdown.checkout-asset',
[
'item' => $this->item,
'admin' => $this->admin,
'status' => $this->item->assetstatus?->name,
'note' => $this->note,
'target' => $this->target,
'fields' => $fields,
'eula' => $eula,
'req_accept' => $req_accept,
'accept_url' => $accept_url,
'last_checkout' => $this->last_checkout,
'expected_checkin' => $this->expected_checkin,
])
->subject(trans('mail.Confirm_asset_delivery'));
return $message;
}
}

View file

@ -6,9 +6,11 @@ use App\Models\Consumable;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Channels\SlackWebhookChannel;
use Illuminate\Notifications\Messages\MailMessage;
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;
@ -62,35 +64,7 @@ class CheckoutConsumableNotification extends Notification
}
if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) {
$notifyBy[] = 'slack';
}
/**
* Only send notifications to users that have email addresses
*/
if ($this->target instanceof User && $this->target->email != '') {
/**
* Send an email if the asset requires acceptance,
* so the user can accept or decline the asset
*/
if ($this->item->requireAcceptance()) {
$notifyBy[1] = 'mail';
}
/**
* Send an email if the item has a EULA, since the user should always receive it
*/
if ($this->item->getEula()) {
$notifyBy[1] = 'mail';
}
/**
* Send an email if an email should be sent at checkin/checkout
*/
if ((method_exists($this->item, 'checkin_email')) && ($this->item->checkin_email())) {
$notifyBy[1] = 'mail';
}
$notifyBy[] = SlackWebhookChannel::class;
}
return $notifyBy;
@ -127,17 +101,30 @@ class CheckoutConsumableNotification extends Notification
$item = $this->item;
$note = $this->note;
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->addStartGroupToSection('activityTitle')
->title(trans('mail.Consumable_checkout_notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle')
->fact(trans('mail.Consumable_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 ?: '');
if(!Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows')) {
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->addStartGroupToSection('activityTitle')
->title(trans('mail.Consumable_checkout_notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle')
->fact(trans('mail.Consumable_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.Consumable_checkout_notification');
$details = [
trans('mail.assigned_to') => $target->present()->fullName(),
trans('mail.item') => htmlspecialchars_decode($item->present()->name),
trans('mail.Consumable_checkout_notification').' by' => $admin->present()->fullName(),
trans('admin/consumables/general.remaining') => $item->numRemaining(),
trans('mail.notes') => $note ?: '',
];
return array($message, $details);
}
public function toGoogleChat()
{
@ -166,30 +153,4 @@ class CheckoutConsumableNotification extends Notification
);
}
/**
* Get the mail representation of the notification.
*
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail()
{
$eula = $this->item->getEula();
$req_accept = $this->item->requireAcceptance();
$accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance);
return (new MailMessage)->markdown('notifications.markdown.checkout-consumable',
[
'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,
])
->subject(trans('mail.Confirm_consumable_delivery'));
}
}

View file

@ -6,9 +6,11 @@ use App\Models\LicenseSeat;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Channels\SlackWebhookChannel;
use Illuminate\Notifications\Messages\MailMessage;
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;
@ -60,35 +62,7 @@ class CheckoutLicenseSeatNotification extends Notification
}
if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) {
$notifyBy[] = 'slack';
}
/**
* Only send notifications to users that have email addresses
*/
if ($this->target instanceof User && $this->target->email != '') {
/**
* Send an email if the asset requires acceptance,
* so the user can accept or decline the asset
*/
if ($this->item->requireAcceptance()) {
$notifyBy[1] = 'mail';
}
/**
* Send an email if the item has a EULA, since the user should always receive it
*/
if ($this->item->getEula()) {
$notifyBy[1] = 'mail';
}
/**
* Send an email if an email should be sent at checkin/checkout
*/
if ($this->item->checkin_email()) {
$notifyBy[1] = 'mail';
}
$notifyBy[] = SlackWebhookChannel::class;
}
return $notifyBy;
@ -125,17 +99,29 @@ class CheckoutLicenseSeatNotification extends Notification
$item = $this->item;
$note = $this->note;
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->addStartGroupToSection('activityTitle')
->title(trans('mail.License_Checkout_Notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle')
->fact(trans('mail.License_Checkout_Notification')." by ", $admin->present()->fullName())
->fact(trans('mail.assigned_to'), $target->present()->fullName())
->fact(trans('admin/consumables/general.remaining'), $item->availCount()->count())
->fact(trans('mail.notes'), $note ?: '');
if(!Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows')) {
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->addStartGroupToSection('activityTitle')
->title(trans('mail.License_Checkout_Notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle')
->fact(trans('mail.License_Checkout_Notification')." by ", $admin->present()->fullName())
->fact(trans('mail.assigned_to'), $target->present()->fullName())
->fact(trans('admin/consumables/general.remaining'), $item->availCount()->count())
->fact(trans('mail.notes'), $note ?: '');
}
$message = trans('mail.License_Checkout_Notification');
$details = [
trans('mail.assigned_to') => $target->present()->fullName(),
trans('mail.license_for') => htmlspecialchars_decode($item->present()->name),
trans('mail.License_Checkout_Notification').' by' => $admin->present()->fullName(),
trans('admin/consumables/general.remaining') => $item->availCount()->count(),
trans('mail.notes') => $note ?: '',
];
return array($message, $details);
}
public function toGoogleChat()
{
@ -164,29 +150,4 @@ class CheckoutLicenseSeatNotification extends Notification
);
}
/**
* Get the mail representation of the notification.
*
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail()
{
$eula = method_exists($this->item, 'getEula') ? $this->item->getEula() : '';
$req_accept = method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0;
$accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance);
return (new MailMessage)->markdown('notifications.markdown.checkout-license',
[
'item' => $this->item,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
'eula' => $eula,
'req_accept' => $req_accept,
'accept_url' => $accept_url,
])
->subject(trans('mail.Confirm_license_delivery'));
}
}

View file

@ -38,7 +38,7 @@
"intervention/image": "^2.5",
"javiereguiluz/easyslugger": "^1.0",
"laravel-notification-channels/google-chat": "^3.0",
"laravel-notification-channels/microsoft-teams": "^1.1",
"laravel-notification-channels/microsoft-teams": "^1.2",
"laravel/framework": "^10.0",
"laravel/helpers": "^1.4",
"laravel/passport": "^11.0",
@ -55,6 +55,7 @@
"nunomaduro/collision": "^7.0",
"okvpn/clock-lts": "^1.0",
"onelogin/php-saml": "^3.4",
"osa-eg/laravel-teams-notification": "^2.1",
"paragonie/constant_time_encoding": "^2.3",
"paragonie/sodium_compat": "^1.19",
"phpdocumentor/reflection-docblock": "^5.1",

103
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "5341bc5be02b3c33e28e46e06dd99f29",
"content-hash": "0750e3a427347b2a56a05a8b9b533d48",
"packages": [
{
"name": "alek13/slack",
@ -5572,6 +5572,71 @@
],
"time": "2024-05-30T15:14:26+00:00"
},
{
"name": "osa-eg/laravel-teams-notification",
"version": "v2.1.2",
"source": {
"type": "git",
"url": "https://github.com/osa-eg/laravel-teams-notification.git",
"reference": "76173689930aca92b5174a3b102e705279192c0f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/osa-eg/laravel-teams-notification/zipball/76173689930aca92b5174a3b102e705279192c0f",
"reference": "76173689930aca92b5174a3b102e705279192c0f",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": ">=6.5",
"illuminate/support": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0",
"monolog/monolog": ">=1.0",
"php": ">=7.0"
},
"require-dev": {
"phpunit/phpunit": "^8.5"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Osama\\LaravelTeamsNotification\\LaravelTeamsNotificationServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Osama\\LaravelTeamsNotification\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Osama Saad",
"email": "osaad96eg@gmail.com"
}
],
"description": "A Laravel package to send notifications to Microsoft Teams",
"keywords": [
"Teams",
"adaptive-card",
"laravel",
"logging",
"microsoft-teams-workflow",
"notification",
"teams-connector",
"teams-webhock",
"teams-workflow",
"teams_logging"
],
"support": {
"issues": "https://github.com/osa-eg/laravel-teams-notification/issues",
"source": "https://github.com/osa-eg/laravel-teams-notification/tree/v2.1.2"
},
"time": "2024-09-23T05:24:48+00:00"
},
{
"name": "paragonie/constant_time_encoding",
"version": "v2.7.0",
@ -9083,16 +9148,16 @@
},
{
"name": "symfony/http-foundation",
"version": "v6.4.12",
"version": "v6.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "133ac043875f59c26c55e79cf074562127cce4d2"
"reference": "ba020a321a95519303a3f09ec2824d34d601c388"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/133ac043875f59c26c55e79cf074562127cce4d2",
"reference": "133ac043875f59c26c55e79cf074562127cce4d2",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/ba020a321a95519303a3f09ec2824d34d601c388",
"reference": "ba020a321a95519303a3f09ec2824d34d601c388",
"shasum": ""
},
"require": {
@ -9140,7 +9205,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v6.4.12"
"source": "https://github.com/symfony/http-foundation/tree/v6.4.14"
},
"funding": [
{
@ -9156,7 +9221,7 @@
"type": "tidelift"
}
],
"time": "2024-09-20T08:18:25+00:00"
"time": "2024-11-05T16:39:55+00:00"
},
{
"name": "symfony/http-kernel",
@ -10075,16 +10140,16 @@
},
{
"name": "symfony/process",
"version": "v6.4.12",
"version": "v6.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "3f94e5f13ff58df371a7ead461b6e8068900fbb3"
"reference": "25214adbb0996d18112548de20c281be9f27279f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/3f94e5f13ff58df371a7ead461b6e8068900fbb3",
"reference": "3f94e5f13ff58df371a7ead461b6e8068900fbb3",
"url": "https://api.github.com/repos/symfony/process/zipball/25214adbb0996d18112548de20c281be9f27279f",
"reference": "25214adbb0996d18112548de20c281be9f27279f",
"shasum": ""
},
"require": {
@ -10116,7 +10181,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v6.4.12"
"source": "https://github.com/symfony/process/tree/v6.4.14"
},
"funding": [
{
@ -10132,7 +10197,7 @@
"type": "tidelift"
}
],
"time": "2024-09-17T12:47:12+00:00"
"time": "2024-11-06T09:25:01+00:00"
},
{
"name": "symfony/psr-http-message-bridge",
@ -15876,16 +15941,16 @@
},
{
"name": "symfony/http-client",
"version": "v6.4.12",
"version": "v6.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
"reference": "fbebfcce21084d3e91ea987ae5bdd8c71ff0fd56"
"reference": "05d88cbd816ad6e0202edd9a9963cb9d615b8826"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client/zipball/fbebfcce21084d3e91ea987ae5bdd8c71ff0fd56",
"reference": "fbebfcce21084d3e91ea987ae5bdd8c71ff0fd56",
"url": "https://api.github.com/repos/symfony/http-client/zipball/05d88cbd816ad6e0202edd9a9963cb9d615b8826",
"reference": "05d88cbd816ad6e0202edd9a9963cb9d615b8826",
"shasum": ""
},
"require": {
@ -15949,7 +16014,7 @@
"http"
],
"support": {
"source": "https://github.com/symfony/http-client/tree/v6.4.12"
"source": "https://github.com/symfony/http-client/tree/v6.4.14"
},
"funding": [
{
@ -15965,7 +16030,7 @@
"type": "tidelift"
}
],
"time": "2024-09-20T08:21:33+00:00"
"time": "2024-11-05T16:39:55+00:00"
},
{
"name": "symfony/http-client-contracts",

View file

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('locations', function (Blueprint $table) {
$table->index('manager_id');
});
Schema::table('users', function (Blueprint $table) {
$table->index('manager_id');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('locations', function (Blueprint $table) {
$table->dropIndex(['manager_id']);
});
Schema::table('users', function (Blueprint $table) {
$table->dropIndex(['manager_id']);
});
}
};

View file

@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('locations', function (Blueprint $table) {
$table->dropIndex(['manager_id']);
$table->index(['manager_id','deleted_at']);
});
Schema::table('users', function (Blueprint $table) {
$table->dropIndex(['manager_id']);
$table->index(['manager_id','deleted_at']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('locations', function (Blueprint $table) {
$table->dropIndex(['manager_id','deleted_at']);
$table->index(['manager_id']);
});
Schema::table('users', function (Blueprint $table) {
$table->dropIndex(['manager_id','deleted_at']);
$table->index(['manager_id']);
});
}
};

119
package-lock.json generated
View file

@ -15,7 +15,7 @@
"bootstrap-colorpicker": "^2.5.3",
"bootstrap-datepicker": "^1.10.0",
"bootstrap-less": "^3.3.8",
"bootstrap-table": "1.23.2",
"bootstrap-table": "1.23.5",
"canvas-confetti": "^1.9.3",
"chart.js": "^2.9.4",
"clipboard": "^2.0.11",
@ -26,7 +26,7 @@
"jquery-ui": "^1.14.0",
"jquery-validation": "^1.21.0",
"jquery.iframe-transport": "^1.0.0",
"jspdf-autotable": "^3.8.3",
"jspdf-autotable": "^3.8.4",
"less": "^4.2.0",
"less-loader": "^6.0",
"list.js": "^1.5.0",
@ -37,7 +37,7 @@
"signature_pad": "^4.2.0",
"tableexport.jquery.plugin": "1.30.0",
"tether": "^1.4.0",
"webpack": "^5.94.0"
"webpack": "^5.95.0"
},
"devDependencies": {
"all-contributors-cli": "^6.26.1",
@ -2105,10 +2105,28 @@
"@types/node": "*"
}
},
"node_modules/@types/eslint": {
"version": "9.6.1",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
"dependencies": {
"@types/estree": "*",
"@types/json-schema": "*"
}
},
"node_modules/@types/eslint-scope": {
"version": "3.7.7",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
"integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
"dependencies": {
"@types/eslint": "*",
"@types/estree": "*"
}
},
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="
},
"node_modules/@types/express": {
"version": "4.17.21",
@ -2467,9 +2485,9 @@
}
},
"node_modules/acorn": {
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
"bin": {
"acorn": "bin/acorn"
},
@ -2484,14 +2502,6 @@
"acorn": "^8"
}
},
"node_modules/acorn-import-attributes": {
"version": "1.9.5",
"resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
"integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
"peerDependencies": {
"acorn": "^8"
}
},
"node_modules/acorn-node": {
"version": "1.8.2",
"license": "Apache-2.0",
@ -3678,9 +3688,9 @@
"license": "MIT"
},
"node_modules/bootstrap-table": {
"version": "1.23.2",
"resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.23.2.tgz",
"integrity": "sha512-1IFiWFZzbKlleXgYEHdwHkX6rxlQMEx2N1tA8rJK/j08pI+NjIGnxFeXUL26yQLQ0U135eis/BX3OV1+anY25g==",
"version": "1.23.5",
"resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.23.5.tgz",
"integrity": "sha512-9WByoSpJvA73gi2YYIlX6IWR74oZtBmSixul/Th8FTBtBd/kZRpbKESGTjhA3BA3AYTnfyY8Iy1KeRWPlV2GWQ==",
"peerDependencies": {
"jquery": "3"
}
@ -3940,7 +3950,9 @@
}
},
"node_modules/browserslist": {
"version": "4.23.0",
"version": "4.24.2",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz",
"integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==",
"funding": [
{
"type": "opencollective",
@ -3955,12 +3967,11 @@
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"caniuse-lite": "^1.0.30001587",
"electron-to-chromium": "^1.4.668",
"node-releases": "^2.0.14",
"update-browserslist-db": "^1.0.13"
"caniuse-lite": "^1.0.30001669",
"electron-to-chromium": "^1.5.41",
"node-releases": "^2.0.18",
"update-browserslist-db": "^1.1.1"
},
"bin": {
"browserslist": "cli.js"
@ -4067,7 +4078,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001616",
"version": "1.0.30001677",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz",
"integrity": "sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==",
"funding": [
{
"type": "opencollective",
@ -4081,8 +4094,7 @@
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "CC-BY-4.0"
]
},
"node_modules/canvas-confetti": {
"version": "1.9.3",
@ -5254,8 +5266,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
"version": "1.4.756",
"license": "ISC"
"version": "1.5.52",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.52.tgz",
"integrity": "sha512-xtoijJTZ+qeucLBDNztDOuQBE1ksqjvNjvqFoST3nGC7fSpqJ+X6BdTBaY5BHG+IhWWmpc6b/KfpeuEDupEPOQ=="
},
"node_modules/elliptic": {
"version": "6.5.5",
@ -5388,8 +5401,9 @@
"license": "MIT"
},
"node_modules/escalade": {
"version": "3.1.2",
"license": "MIT",
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"engines": {
"node": ">=6"
}
@ -7167,9 +7181,9 @@
}
},
"node_modules/jspdf-autotable": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-3.8.3.tgz",
"integrity": "sha512-PQFdljBt+ijm6ZWXYxhZ54A/awV63UKcipYoA2+YGsz0BXXiXTIL/FIg+V30j7wPdSdzClfbB3qKX9UeuFylPQ==",
"version": "3.8.4",
"resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-3.8.4.tgz",
"integrity": "sha512-rSffGoBsJYX83iTRv8Ft7FhqfgEL2nLpGAIiqruEQQ3e4r0qdLFbPUB7N9HAle0I3XgpisvyW751VHCqKUVOgQ==",
"peerDependencies": {
"jspdf": "^2.5.1"
}
@ -8097,8 +8111,9 @@
}
},
"node_modules/node-releases": {
"version": "2.0.14",
"license": "MIT"
"version": "2.0.18",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
"integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g=="
},
"node_modules/normalize-path": {
"version": "3.0.0",
@ -8549,8 +8564,9 @@
"optional": true
},
"node_modules/picocolors": {
"version": "1.0.0",
"license": "ISC"
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"node_modules/picomatch": {
"version": "2.3.1",
@ -10703,7 +10719,9 @@
}
},
"node_modules/update-browserslist-db": {
"version": "1.0.15",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
"integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
"funding": [
{
"type": "opencollective",
@ -10718,10 +10736,9 @@
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"escalade": "^3.1.2",
"picocolors": "^1.0.0"
"escalade": "^3.2.0",
"picocolors": "^1.1.0"
},
"bin": {
"update-browserslist-db": "cli.js"
@ -10866,17 +10883,17 @@
"license": "BSD-2-Clause"
},
"node_modules/webpack": {
"version": "5.94.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
"integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
"version": "5.96.1",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz",
"integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==",
"dependencies": {
"@types/estree": "^1.0.5",
"@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.6",
"@webassemblyjs/ast": "^1.12.1",
"@webassemblyjs/wasm-edit": "^1.12.1",
"@webassemblyjs/wasm-parser": "^1.12.1",
"acorn": "^8.7.1",
"acorn-import-attributes": "^1.9.5",
"browserslist": "^4.21.10",
"acorn": "^8.14.0",
"browserslist": "^4.24.0",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.17.1",
"es-module-lexer": "^1.2.1",

View file

@ -35,7 +35,7 @@
"bootstrap-colorpicker": "^2.5.3",
"bootstrap-datepicker": "^1.10.0",
"bootstrap-less": "^3.3.8",
"bootstrap-table": "1.23.2",
"bootstrap-table": "1.23.5",
"canvas-confetti": "^1.9.3",
"chart.js": "^2.9.4",
"clipboard": "^2.0.11",
@ -46,7 +46,7 @@
"jquery-ui": "^1.14.0",
"jquery-validation": "^1.21.0",
"jquery.iframe-transport": "^1.0.0",
"jspdf-autotable": "^3.8.3",
"jspdf-autotable": "^3.8.4",
"less": "^4.2.0",
"less-loader": "^6.0",
"list.js": "^1.5.0",
@ -57,6 +57,6 @@
"signature_pad": "^4.2.0",
"tableexport.jquery.plugin": "1.30.0",
"tether": "^1.4.0",
"webpack": "^5.94.0"
"webpack": "^5.95.0"
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -19,7 +19,7 @@
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=f677207c6cf9678eb539abecb408c374",
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=0640e45bad692dcf62873c6e85904899",
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=76482123f6c70e866d6b971ba91de7bb",
"/css/dist/all.css": "/css/dist/all.css?id=9f69886d7a8e4c383cd09a48573922b7",
"/css/dist/all.css": "/css/dist/all.css?id=1f52593f5a0982ed4f09c5c87eddf295",
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/js/select2/i18n/af.js": "/js/select2/i18n/af.js?id=4f6fcd73488ce79fae1b7a90aceaecde",
@ -90,8 +90,8 @@
"/css/webfonts/fa-solid-900.woff2": "/css/webfonts/fa-solid-900.woff2?id=541cafc702f56f57de95f3d1f792f428",
"/css/webfonts/fa-v4compatibility.ttf": "/css/webfonts/fa-v4compatibility.ttf?id=51ade19e1b10d7a0031b18568a2b01d5",
"/css/webfonts/fa-v4compatibility.woff2": "/css/webfonts/fa-v4compatibility.woff2?id=1cc408d68a27c3757b4460bbc542433e",
"/js/dist/bootstrap-table-locale-all.min.js": "/js/dist/bootstrap-table-locale-all.min.js?id=467938d6a524df8e62c4fb8ae5e7f3f1",
"/js/dist/bootstrap-table-en-US.min.js": "/js/dist/bootstrap-table-en-US.min.js?id=d4ef3db8dc9f809258218c187de5ee2a",
"/js/dist/bootstrap-table-locale-all.min.js": "/js/dist/bootstrap-table-locale-all.min.js?id=c5445e15be5ce91a9ffef05e08ad6898",
"/js/dist/bootstrap-table-en-US.min.js": "/js/dist/bootstrap-table-en-US.min.js?id=0f6e85ae692d03a3b11cab445ff263ab",
"/css/dist/skins/_all-skins.min.css": "/css/dist/skins/_all-skins.min.css?id=f4397c717b99fce41a633ca6edd5d1f4",
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=f0b08873a06bb54daeee176a9459f4a9",
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=76482123f6c70e866d6b971ba91de7bb",
@ -108,8 +108,8 @@
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=44bf834f2110504a793dadec132a5898",
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=393aaa7b368b0670fc42434c8cca7dc7",
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=393d720a0f9aba560094fbc8d3b0c0f0",
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=c384582a6ba08903af353be861ffe74e",
"/js/build/vendor.js": "/js/build/vendor.js?id=5269eb5a6beb74f03387c78938cf17b2",
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=6660df122e24940d42d03c06775fec7b",
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=b4c3069f1a292527a96c058b77b28d69",
"/js/dist/all.js": "/js/dist/all.js?id=e0a4b1a80b09333a460973137f39eab4"
}

File diff suppressed because one or more lines are too long

View file

@ -6,6 +6,4 @@ return array(
'id' => 'ID',
'require_acceptance' => 'Acceptance',
'title' => 'Accessory Name',
);

View file

@ -59,5 +59,6 @@ return [
'asset_deployable' => 'That status is deployable. This asset can be checked out.',
'processing_spinner' => 'Processing... (This might take a bit of time on large files)',
'optional_infos' => 'Optional Information',
'order_details' => 'Order Related Information'
'order_details' => 'Order Related Information',
'calc_eol' => 'If nulling the EOL date, use automatic EOL calculation based on the purchase date and EOL rate.',
];

View file

@ -23,6 +23,7 @@ return [
'Item_Requested' => 'Item Requested',
'License_Checkin_Notification' => 'License checked in',
'License_Checkout_Notification' => 'License checked out',
'license_for' => 'License for',
'Low_Inventory_Report' => 'Low Inventory Report',
'a_user_canceled' => 'A user has canceled an item request on the website',
'a_user_requested' => 'A user has requested an item on the website',

View file

@ -91,6 +91,33 @@
</div>
</div>
<!-- EOL Date -->
<div class="form-group {{ $errors->has('asset_eol_date') ? ' has-error' : '' }}">
<label for="eol_date" class="col-md-3 control-label">{{ trans('admin/hardware/form.eol_date') }}</label>
<div class="col-md-4">
<div class="input-group date" data-provide="datepicker" data-date-format="yyyy-mm-dd" data-autoclose="true">
<input type="text" class="form-control" placeholder="{{ trans('general.select_date') }}" name="asset_eol_date" id="asset_eol_date" value="{{ old('asset_eol_date') }}">
<span class="input-group-addon"><x-icon type="calendar" /></span>
</div>
{!! $errors->first('asset_eol_date', '<span class="alert-msg"><i class="fas fa-times"></i> :message</span>') !!}
</div>
<div class="col-md-5">
<label class="form-control">
{{ Form::checkbox('null_asset_eol_date', '1', false) }}
{{ trans_choice('general.set_to_null', count($assets),['selection_count' => count($assets)]) }}
</label>
</div>
</div>
<div class="form-group">
<div class="col-md-9 col-md-offset-3">
<label class="form-control">
{{ Form::checkbox('calc_eol', '1', false) }}
{{ trans('admin/hardware/form.calc_eol') }}
</label>
</div>
</div>
<!-- Status -->
<div class="form-group {{ $errors->has('status_id') ? ' has-error' : '' }}">

View file

@ -268,17 +268,24 @@ dir="{{ Helper::determineLanguageDirection() }}">
@can('admin')
@if ($snipeSettings->show_alerts_in_menu=='1')
<!-- Tasks: style can be found in dropdown.less -->
<?php $alert_items = Helper::checkLowInventory(); ?>
<?php $alert_items = Helper::checkLowInventory(); $deprecations = Helper::deprecationCheck()?>
<li class="dropdown tasks-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<x-icon type="alerts" />
<span class="sr-only">{{ trans('general.alerts') }}</span>
@if (count($alert_items))
<span class="label label-danger">{{ count($alert_items) }}</span>
@if (count($alert_items) || count($deprecations))
<span class="label label-danger">{{ count($alert_items) + count($deprecations) }}</span>
@endif
</a>
<ul class="dropdown-menu">
@if($deprecations)
@foreach ($deprecations as $key => $deprecation)
@if ($deprecation['check'])
<li class="header alert-warning">{!! $deprecation['message'] !!}</li>
@endif
@endforeach
@endif
<li class="header">{{ trans_choice('general.quantity_minimum', count($alert_items)) }}</li>
<li>
<!-- inner menu: contains the actual data -->

View file

@ -15,7 +15,14 @@
<div><!-- livewire div - do not remove -->
<form class="form-horizontal" role="form" wire:submit="submit">
{{csrf_field()}}
@if (session()->has('warning'))
<div class="alert alert-warning">
{!! session('warning') !!}
@php
session()->forget('warning'); // Clear the session flash immediately
@endphp
</div>
@endif
<div class="row">
<div class="col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2">

View file

@ -6,15 +6,15 @@
@component('mail::table')
| | |
| ------------- | ------------- |
| **{{ trans('mail.asset_name') }}** | {{ $item->name }} |
@if (isset($item->manufacturer))
| **{{ trans('general.manufacturer') }}** | {{ $item->manufacturer->name }} |
| **{{ trans('mail.asset_name') }}** | {{ $license->name }} |
@if (isset($license->manufacturer))
| **{{ trans('general.manufacturer') }}** | {{ $license->manufacturer->name }} |
@endif
@if ($target->can('update', $item))
| **Key** | {{ $item->serial }} |
@if (($target instanceof \App\Models\User && $target->can('view', $license)) ||($target instanceof \App\Models\Asset && $license_seat->user->can('view', $license)))
| **Key** | {{ $license->serial }} |
@endif
@if (isset($item->category))
| **{{ trans('general.category') }}** | {{ $item->category->name }} |
| **{{ trans('general.category') }}** | {{ $license->category->name }} |
@endif
@if ($admin)
| **{{ trans('general.administrator') }}** | {{ $admin->present()->fullName() }} |

View file

@ -73,4 +73,4 @@
{{ $snipeSettings->site_name }}
@endcomponent
@endcomponent

View file

@ -17,6 +17,9 @@
@if (isset($item->manufacturer))
| **{{ trans('general.manufacturer') }}** | {{ $item->manufacturer->name }} |
@endif
@if (isset($qty))
| **{{ trans('general.qty') }}** | {{ $qty }} |
@endif
@if (isset($item->model_no))
| **{{ trans('general.model_no') }}** | {{ $item->model_no }} |
@endif

View file

@ -9,15 +9,15 @@
@if (isset($checkout_date))
| **{{ trans('mail.checkout_date') }}** | {{ $checkout_date }} |
@endif
| **{{ trans('general.license') }}** | {{ $item->name }} |
@if (isset($item->manufacturer))
| **{{ trans('general.manufacturer') }}** | {{ $item->manufacturer->name }} |
| **{{ trans('general.license') }}** | {{ $license->name}} |
@if (isset($license->manufacturer))
| **{{ trans('general.manufacturer') }}** | {{ $license->manufacturer->name }} |
@endif
@if (isset($item->category))
| **{{ trans('general.category') }}** | {{ $item->category->name }} |
@if (isset($license->category))
| **{{ trans('general.category') }}** | {{ $license->category->name }} |
@endif
@if ($target->can('view', $item))
| **Key** | {{ $item->serial }} |
@if (($target instanceof \App\Models\User && $target->can('view', $license)) || ($target instanceof \App\Models\Asset && $license_seat->user->can('view', $license)))
| **Key** | {{ $license->serial }} |
@endif
@if ($note)
| **{{ trans('mail.additional_notes') }}** | {{ $note }} |

View file

@ -64,7 +64,7 @@
<th class="col-sm-2" data-field="file" data-visible="false" data-formatter="fileUploadNameFormatter">
{{ trans('general.file_name') }}
</th>
<th class="col-sm-1" data-field="type" data-formatter="itemTypeFormatter">
<th class="col-sm-1" data-field="item_type" data-searchable="true" data-formatter="itemTypeFormatter">
{{ trans('general.type') }}
</th>
<th class="col-sm-3" data-field="item.serial" data-visible="false">

View file

@ -896,7 +896,8 @@
}'>
<thead>
<tr>
<th class="col-md-5">{{ trans('general.name') }}</th>
<th class="col-md-1">{{ trans('general.id') }}</th>
<th class="col-md-4">{{ trans('general.name') }}</th>
<th class-="col-md-5" data-fieldname="note">{{ trans('general.notes') }}</th>
<th class="col-md-1" data-footer-formatter="sumFormatter" data-fieldname="purchase_cost">{{ trans('general.purchase_cost') }}</th>
<th class="col-md-1 hidden-print">{{ trans('general.action') }}</th>
@ -905,7 +906,8 @@
<tbody>
@foreach ($user->accessories as $accessory)
<tr>
<td>{!!$accessory->present()->nameUrl()!!}</td>
<td>{{ $accessory->pivot->id }}</td>
<td>{!!$accessory->present()->nameUrl()!!}</td>
<td>{!! $accessory->pivot->note !!}</td>
<td>
{!! Helper::formatCurrencyOutput($accessory->purchase_cost) !!}
@ -1210,4 +1212,4 @@ $('#dataConfirmModal').on('show.bs.modal', function (event) {
</script>
@stop
@stop

View file

@ -54,7 +54,6 @@ Route::group(['middleware' => 'auth'], function () {
/*
* Locations
*/
Route::group(['prefix' => 'locations', 'middleware' => ['auth']], function () {
Route::post(

View file

@ -3,10 +3,12 @@
namespace Tests\Feature\Checkins\Ui;
use App\Events\CheckoutableCheckedIn;
use App\Mail\CheckinAccessoryMail;
use App\Models\Accessory;
use App\Models\User;
use App\Notifications\CheckinAccessoryNotification;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;
@ -40,7 +42,7 @@ class AccessoryCheckinTest extends TestCase
public function testEmailSentToUserIfSettingEnabled()
{
Notification::fake();
Mail::fake();
$user = User::factory()->create();
$accessory = Accessory::factory()->checkedOutToUser($user)->create();
@ -53,23 +55,24 @@ class AccessoryCheckinTest extends TestCase
User::factory()->checkinAccessories()->create(),
'',
));
Mail::assertSent(CheckinAccessoryMail::class, function (CheckinAccessoryMail $mail) use ( $accessory, $user) {
return $mail->hasTo($user->email);
Notification::assertSentTo(
[$user],
function (CheckinAccessoryNotification $notification, $channels) {
return in_array('mail', $channels);
},
);
});
}
public function testEmailNotSentToUserIfSettingDisabled()
{
Notification::fake();
Mail::fake();
$user = User::factory()->create();
$accessory = Accessory::factory()->checkedOutToUser($user)->create();
$accessory->category->update(['checkin_email' => false]);
$accessory->category->update([
'checkin_email' => false,
'require_acceptance' => false,
'eula_text' => null
]);
event(new CheckoutableCheckedIn(
$accessory,
@ -78,11 +81,8 @@ class AccessoryCheckinTest extends TestCase
'',
));
Notification::assertNotSentTo(
[$user],
function (CheckinAccessoryNotification $notification, $channels) {
return in_array('mail', $channels);
},
);
Mail::assertNotSent(CheckinAccessoryMail::class, function ($mail) use ($user) {
return $mail->hasTo($user->email);
});
}
}

View file

@ -2,10 +2,12 @@
namespace Tests\Feature\Checkouts\Api;
use App\Mail\CheckoutAccessoryMail;
use App\Models\Accessory;
use App\Models\Actionlog;
use App\Models\User;
use App\Notifications\CheckoutAccessoryNotification;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use Tests\Concerns\TestsPermissionsRequirement;
use Tests\TestCase;
@ -146,7 +148,7 @@ class AccessoryCheckoutTest extends TestCase implements TestsPermissionsRequirem
public function testUserSentNotificationUponCheckout()
{
Notification::fake();
Mail::fake();
$accessory = Accessory::factory()->requiringAcceptance()->create();
$user = User::factory()->create();
@ -157,7 +159,9 @@ class AccessoryCheckoutTest extends TestCase implements TestsPermissionsRequirem
'checkout_to_type' => 'user',
]);
Notification::assertSentTo($user, CheckoutAccessoryNotification::class);
Mail::assertSent(CheckoutAccessoryMail::class, function ($mail) use ($user) {
return $mail->hasTo($user->email);
});
}
public function testActionLogCreatedUponCheckout()

View file

@ -2,10 +2,12 @@
namespace Tests\Feature\Checkouts\Api;
use App\Mail\CheckoutConsumableMail;
use App\Models\Actionlog;
use App\Models\Consumable;
use App\Models\User;
use App\Notifications\CheckoutConsumableNotification;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;
@ -51,7 +53,7 @@ class ConsumableCheckoutTest extends TestCase
public function testUserSentNotificationUponCheckout()
{
Notification::fake();
Mail::fake();
$consumable = Consumable::factory()->requiringAcceptance()->create();
@ -62,7 +64,9 @@ class ConsumableCheckoutTest extends TestCase
'assigned_to' => $user->id,
]);
Notification::assertSentTo($user, CheckoutConsumableNotification::class);
Mail::assertSent(CheckoutConsumableMail::class, function ($mail) use ($consumable, $user) {
return $mail->hasTo($user->email);
});
}
public function testActionLogCreatedUponCheckout()

View file

@ -2,12 +2,14 @@
namespace Tests\Feature\Checkouts\Ui;
use App\Mail\CheckoutAccessoryMail;
use App\Models\Accessory;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\Location;
use App\Models\User;
use App\Notifications\CheckoutAccessoryNotification;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;
@ -156,7 +158,7 @@ class AccessoryCheckoutTest extends TestCase
public function testUserSentNotificationUponCheckout()
{
Notification::fake();
Mail::fake();
$accessory = Accessory::factory()->requiringAcceptance()->create();
$user = User::factory()->create();
@ -168,7 +170,9 @@ class AccessoryCheckoutTest extends TestCase
'checkout_to_type' => 'user',
]);
Notification::assertSentTo($user, CheckoutAccessoryNotification::class);
Mail::assertSent(CheckoutAccessoryMail::class, function ($mail) use ($user) {
return $mail->hasTo($user->email);
});
}
public function testActionLogCreatedUponCheckout()

View file

@ -2,12 +2,14 @@
namespace Tests\Feature\Checkouts\Ui;
use App\Mail\CheckoutConsumableMail;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\Component;
use App\Models\Consumable;
use App\Models\User;
use App\Notifications\CheckoutConsumableNotification;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;
@ -53,7 +55,7 @@ class ConsumableCheckoutTest extends TestCase
public function testUserSentNotificationUponCheckout()
{
Notification::fake();
Mail::fake();
$consumable = Consumable::factory()->create();
$user = User::factory()->create();
@ -63,7 +65,9 @@ class ConsumableCheckoutTest extends TestCase
'assigned_to' => $user->id,
]);
Notification::assertSentTo($user, CheckoutConsumableNotification::class);
Mail::assertSent(CheckoutConsumableMail::class, function ($mail) use ($user) {
return $mail->hasTo($user->email);
});
}
public function testActionLogCreatedUponCheckout()

View file

@ -2,6 +2,7 @@
namespace Tests\Feature\Importing\Api;
use App\Mail\CheckoutAssetMail;
use App\Models\Actionlog as ActionLog;
use App\Models\Asset;
use App\Models\CustomField;
@ -11,6 +12,7 @@ use App\Notifications\CheckoutAssetNotification;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Str;
use Illuminate\Testing\TestResponse;
@ -54,7 +56,6 @@ class ImportAssetsTest extends ImportDataTestCase implements TestsPermissionsReq
#[Test]
public function importAsset(): void
{
Notification::fake();
$importFileBuilder = ImportFileBuilder::new();
$row = $importFileBuilder->firstRow();
@ -138,7 +139,6 @@ class ImportAssetsTest extends ImportDataTestCase implements TestsPermissionsReq
//Notes is never read.
// $this->assertEquals($row['notes'], $newAsset->notes);
Notification::assertSentTo($assignee, CheckoutAssetNotification::class);
}
#[Test]

View file

@ -2,6 +2,8 @@
namespace Tests\Feature\Notifications\Email;
use App\Mail\CheckinAssetMail;
use Illuminate\Support\Facades\Mail;
use PHPUnit\Framework\Attributes\Group;
use App\Events\CheckoutableCheckedIn;
use App\Models\Asset;
@ -17,11 +19,13 @@ class EmailNotificationsUponCheckinTest extends TestCase
{
parent::setUp();
Notification::fake();
Mail::fake();
}
public function testCheckInEmailSentToUserIfSettingEnabled()
{
Mail::fake();
$user = User::factory()->create();
$asset = Asset::factory()->assignedToUser($user)->create();
@ -29,27 +33,29 @@ class EmailNotificationsUponCheckinTest extends TestCase
$this->fireCheckInEvent($asset, $user);
Notification::assertSentTo(
$user,
function (CheckinAssetNotification $notification, $channels) {
return in_array('mail', $channels);
},
);
Mail::assertSent(CheckinAssetMail::class, function($mail) use ($user) {
return $mail->hasTo($user->email);
});
}
public function testCheckInEmailNotSentToUserIfSettingDisabled()
{
Mail::fake();
$user = User::factory()->create();
$asset = Asset::factory()->assignedToUser($user)->create();
$asset->model->category->update(['checkin_email' => false]);
$asset->model->category->update([
'checkin_email' => false,
'eula_text' => null,
'require_acceptance' => false,
]);
$this->fireCheckInEvent($asset, $user);
Notification::assertNotSentTo(
$user,
function (CheckinAssetNotification $notification, $channels) {
return in_array('mail', $channels);
Mail::assertNotSent(CheckinAssetMail::class, function($mail) use ($user) {
return $mail->hasTo($user->email);
}
);
}

View file

@ -1,12 +1,14 @@
<?php
namespace Tests\Unit;
use App\Mail\CheckoutAssetMail;
use App\Models\User;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Category;
use Carbon\Carbon;
use App\Notifications\CheckoutAssetNotification;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;
@ -29,13 +31,15 @@ class NotificationTest extends TestCase
'purchase_date' => Carbon::createFromDate(2017, 1, 1)->hour(0)->minute(0)->second(0)->format('Y-m-d')
]);
Notification::fake();
Mail::fake();
$asset->checkOut($user, $admin->id);
Notification::assertSentTo($user, CheckoutAssetNotification::class);
Mail::assertSent(CheckoutAssetMail::class, function ($mail) use ($user) {
return $mail->hasTo($user->email);
});
}
public function testDefaultEulaIsSentWhenSetInCategory()
{
Notification::fake();
Mail::fake();
$this->settings->setEula('My Custom EULA Text');
@ -51,10 +55,10 @@ class NotificationTest extends TestCase
$asset->checkOut($user, User::factory()->superuser()->create()->id);
Notification::assertSentTo($user, CheckoutAssetNotification::class, function ($notification) {
$content = $notification->toMail()->render();
return str_contains($content, 'My Custom EULA Text') && !str_contains($content, 'EULA Text that should not be used');
Mail::assertSent(CheckoutAssetMail::class, function ($mail) use ($user) {
return $mail->hasTo($user->email) &&
str_contains($mail->render(), 'My Custom EULA Text') &&
!str_contains($mail->render(), 'EULA Text that should not be used');
});
}
}