From 92a2a5ccbc8c5380a64ef48ffef6ca520bac90d6 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 21 Jul 2018 13:54:30 +0200 Subject: [PATCH 01/42] Adds listeners for checking/checkout events --- .../SendingCheckInNotificationsListener.php | 18 ++++++++++++++++++ .../SendingCheckOutNotificationsListener.php | 17 +++++++++++++++++ app/Providers/EventServiceProvider.php | 11 +++++++++++ 3 files changed, 46 insertions(+) create mode 100644 app/Listeners/SendingCheckInNotificationsListener.php create mode 100644 app/Listeners/SendingCheckOutNotificationsListener.php diff --git a/app/Listeners/SendingCheckInNotificationsListener.php b/app/Listeners/SendingCheckInNotificationsListener.php new file mode 100644 index 000000000..cb5a32d67 --- /dev/null +++ b/app/Listeners/SendingCheckInNotificationsListener.php @@ -0,0 +1,18 @@ + Date: Sat, 21 Jul 2018 14:00:17 +0200 Subject: [PATCH 02/42] Adds checkin events --- app/Events/AccessoryCheckedIn.php | 33 +++++++++++++++++ app/Events/AssetCheckedIn.php | 33 +++++++++++++++++ app/Events/ComponentCheckedIn.php | 35 +++++++++++++++++++ app/Events/LicenseCheckedIn.php | 33 +++++++++++++++++ .../Assets/AssetCheckinController.php | 3 ++ 5 files changed, 137 insertions(+) create mode 100644 app/Events/AccessoryCheckedIn.php create mode 100644 app/Events/AssetCheckedIn.php create mode 100644 app/Events/ComponentCheckedIn.php create mode 100644 app/Events/LicenseCheckedIn.php diff --git a/app/Events/AccessoryCheckedIn.php b/app/Events/AccessoryCheckedIn.php new file mode 100644 index 000000000..812e072b2 --- /dev/null +++ b/app/Events/AccessoryCheckedIn.php @@ -0,0 +1,33 @@ +accessory = $accessory; + $this->checkedOutTo = $checkedOutTo; + $this->checkedInBy = $checkedInBy; + $this->note = $note; + } +} diff --git a/app/Events/AssetCheckedIn.php b/app/Events/AssetCheckedIn.php new file mode 100644 index 000000000..455b2b5d4 --- /dev/null +++ b/app/Events/AssetCheckedIn.php @@ -0,0 +1,33 @@ +asset = $asset; + $this->checkedOutTo = $checkedOutTo; + $this->checkedInBy = $checkedInBy; + $this->note = $note; + } +} diff --git a/app/Events/ComponentCheckedIn.php b/app/Events/ComponentCheckedIn.php new file mode 100644 index 000000000..283ea6e35 --- /dev/null +++ b/app/Events/ComponentCheckedIn.php @@ -0,0 +1,35 @@ +component = $component; + $this->checkedOutTo = $checkedOutTo; + $this->checkedInBy = $checkedInBy; + $this->quantity = $quantity; + $this->note = $note; + } +} diff --git a/app/Events/LicenseCheckedIn.php b/app/Events/LicenseCheckedIn.php new file mode 100644 index 000000000..36562296b --- /dev/null +++ b/app/Events/LicenseCheckedIn.php @@ -0,0 +1,33 @@ +license = $license; + $this->checkedOutTo = $checkedOutTo; + $this->checkedInBy = $checkedInBy; + $this->note = $note; + } +} diff --git a/app/Http/Controllers/Assets/AssetCheckinController.php b/app/Http/Controllers/Assets/AssetCheckinController.php index 7eed360a6..26f77e7fc 100644 --- a/app/Http/Controllers/Assets/AssetCheckinController.php +++ b/app/Http/Controllers/Assets/AssetCheckinController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Assets; +use App\Events\AssetCheckedIn; use App\Helpers\Helper; use App\Http\Controllers\Controller; use App\Http\Requests\AssetCheckinRequest; @@ -85,6 +86,8 @@ class AssetCheckinController extends Controller $asset->logCheckin($target, e(request('note'))); + event(new AssetCheckedIn($asset, $target, Auth::user(), $request->input('note'))); + if ($backto=='user') { return redirect()->route("users.show", $user->id)->with('success', trans('admin/hardware/message.checkin.success')); } From ea64abc6072161be72ecefd9ca3042ae89c96c1f Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 21 Jul 2018 14:01:30 +0200 Subject: [PATCH 03/42] Adds checkout events --- app/Events/AccessoryCheckedOut.php | 31 +++++++++++++++++++++++++++++ app/Events/AssetCheckedOut.php | 31 +++++++++++++++++++++++++++++ app/Events/ComponentCheckedOut.php | 31 +++++++++++++++++++++++++++++ app/Events/ConsumableCheckedOut.php | 31 +++++++++++++++++++++++++++++ app/Events/LicenseCheckedOut.php | 31 +++++++++++++++++++++++++++++ app/Models/Asset.php | 6 +++++- 6 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 app/Events/AccessoryCheckedOut.php create mode 100644 app/Events/AssetCheckedOut.php create mode 100644 app/Events/ComponentCheckedOut.php create mode 100644 app/Events/ConsumableCheckedOut.php create mode 100644 app/Events/LicenseCheckedOut.php diff --git a/app/Events/AccessoryCheckedOut.php b/app/Events/AccessoryCheckedOut.php new file mode 100644 index 000000000..397500238 --- /dev/null +++ b/app/Events/AccessoryCheckedOut.php @@ -0,0 +1,31 @@ +accessory = $accessory; + $this->checkedOutTo = $checkedOutTo; + $this->logEntry = $logEntry; + } +} diff --git a/app/Events/AssetCheckedOut.php b/app/Events/AssetCheckedOut.php new file mode 100644 index 000000000..6d2fdea3a --- /dev/null +++ b/app/Events/AssetCheckedOut.php @@ -0,0 +1,31 @@ +asset = $asset; + $this->checkedOutTo = $checkedOutTo; + $this->logEntry = $logEntry; + } +} diff --git a/app/Events/ComponentCheckedOut.php b/app/Events/ComponentCheckedOut.php new file mode 100644 index 000000000..81826a4d2 --- /dev/null +++ b/app/Events/ComponentCheckedOut.php @@ -0,0 +1,31 @@ +component = $component; + $this->checkedOutTo = $checkedOutTo; + $this->logEntry = $logEntry; + } +} diff --git a/app/Events/ConsumableCheckedOut.php b/app/Events/ConsumableCheckedOut.php new file mode 100644 index 000000000..df97561c3 --- /dev/null +++ b/app/Events/ConsumableCheckedOut.php @@ -0,0 +1,31 @@ +consumable = $consumable; + $this->checkedOutTo = $checkedOutTo; + $this->logEntry = $logEntry; + } +} diff --git a/app/Events/LicenseCheckedOut.php b/app/Events/LicenseCheckedOut.php new file mode 100644 index 000000000..5d1b8335e --- /dev/null +++ b/app/Events/LicenseCheckedOut.php @@ -0,0 +1,31 @@ +license = $license; + $this->checkedOutTo = $checkedOutTo; + $this->logEntry = $logEntry; + } +} diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 4805602f3..288cb0406 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -1,6 +1,7 @@ save()) { - $this->logCheckout($note, $target); + $loggedAction = $this->logCheckout($note, $target); + + event(new AssetCheckedOut($this, $target, $loggedAction)); + $this->increment('checkout_counter', 1); return true; } From 4a71542f233b655ec3283559a3a3206554aba85f Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 21 Jul 2018 14:02:56 +0200 Subject: [PATCH 04/42] Cleanup checkin notification constructors --- .../CheckinAccessoryNotification.php | 25 ++++++------------- .../CheckinAssetNotification.php | 23 ++++++++--------- .../CheckinLicenseNotification.php | 19 ++++++-------- 3 files changed, 24 insertions(+), 43 deletions(-) diff --git a/app/Notifications/CheckinAccessoryNotification.php b/app/Notifications/CheckinAccessoryNotification.php index 052279892..ee3bcac6a 100644 --- a/app/Notifications/CheckinAccessoryNotification.php +++ b/app/Notifications/CheckinAccessoryNotification.php @@ -2,6 +2,7 @@ namespace App\Notifications; +use App\Models\Accessory; use App\Models\Setting; use App\Models\SnipeModel; use App\Models\User; @@ -15,31 +16,19 @@ use Illuminate\Support\Facades\Mail; class CheckinAccessoryNotification extends Notification { use Queueable; - /** - * @var - */ - private $params; /** * Create a new notification instance. * * @param $params */ - public function __construct($params) + public function __construct(Accessory $accessory, $checkedOutTo, User $checkedInby, $note) { - $this->target = $params['target']; - $this->item = $params['item']; - $this->admin = $params['admin']; - $this->note = ''; - $this->target_type = $params['target']; - $this->settings = $params['settings']; - - if (array_key_exists('note', $params)) { - $this->note = $params['note']; - } - - - + $this->item = $accessory; + $this->target = $checkedOutTo; + $this->admin = $checkedInby; + $this->note = $note; + $this->settings = Setting::getSettings(); } /** diff --git a/app/Notifications/CheckinAssetNotification.php b/app/Notifications/CheckinAssetNotification.php index 78f3f0066..4053f22ff 100644 --- a/app/Notifications/CheckinAssetNotification.php +++ b/app/Notifications/CheckinAssetNotification.php @@ -2,13 +2,14 @@ namespace App\Notifications; +use App\Models\Asset; use App\Models\Setting; -use Illuminate\Bus\Queueable; use App\Models\User; -use Illuminate\Notifications\Notification; +use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\SlackMessage; +use Illuminate\Notifications\Notification; class CheckinAssetNotification extends Notification { @@ -20,19 +21,15 @@ class CheckinAssetNotification extends Notification * * @param $params */ - public function __construct($params) + public function __construct(Asset $asset, $checkedOutTo, User $checkedInBy, $note) { - $this->target = $params['target']; - $this->item = $params['item']; - $this->admin = $params['admin']; - $this->note = ''; - $this->expected_checkin = ''; - $this->target_type = $params['target_type']; - $this->settings = $params['settings']; + $this->target = $checkedOutTo; + $this->item = $asset; + $this->admin = $checkedInBy; + $this->note = $note; - if (array_key_exists('note', $params)) { - $this->note = $params['note']; - } + $this->settings = Setting::getSettings(); + $this->expected_checkin = ''; if ($this->item->expected_checkin) { $this->expected_checkin = \App\Helpers\Helper::getFormattedDateObject($this->item->expected_checkin, 'date', diff --git a/app/Notifications/CheckinLicenseNotification.php b/app/Notifications/CheckinLicenseNotification.php index f8a52ccf3..3ff06d2ca 100644 --- a/app/Notifications/CheckinLicenseNotification.php +++ b/app/Notifications/CheckinLicenseNotification.php @@ -2,6 +2,7 @@ namespace App\Notifications; +use App\Models\License; use App\Models\Setting; use App\Models\SnipeModel; use App\Models\User; @@ -25,19 +26,13 @@ class CheckinLicenseNotification extends Notification * * @param $params */ - public function __construct($params) + public function __construct(License $license, $checkedOutTo, User $checkedInBy, $note) { - $this->target = $params['target']; - $this->item = $params['item']; - $this->admin = $params['admin']; - $this->note = ''; - $this->settings = $params['settings']; - $this->target_type = $params['target_type']; - - if (array_key_exists('note', $params)) { - $this->note = $params['note']; - } - + $this->target = $checkedOutTo; + $this->item = $license; + $this->admin = $checkedInBy; + $this->note = $note; + $this->settings = Setting::getSettings(); } /** From ef76908fce31bd0e06fb8479da0d3b23f439e41c Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 21 Jul 2018 14:03:26 +0200 Subject: [PATCH 05/42] Listen for checkin events and send the appropriate notifications --- .../SendingCheckInNotificationsListener.php | 103 +++++++++++++++++- 1 file changed, 101 insertions(+), 2 deletions(-) diff --git a/app/Listeners/SendingCheckInNotificationsListener.php b/app/Listeners/SendingCheckInNotificationsListener.php index cb5a32d67..8a84c42fa 100644 --- a/app/Listeners/SendingCheckInNotificationsListener.php +++ b/app/Listeners/SendingCheckInNotificationsListener.php @@ -2,9 +2,95 @@ namespace App\Listeners; +use App\Models\Recipients\AdminRecipient; +use App\Models\Setting; +use App\Models\User; +use App\Notifications\CheckinAccessoryNotification; +use App\Notifications\CheckinAssetNotification; +use App\Notifications\CheckinLicenseNotification; +use App\Notifications\CheckoutAccessoryNotification; +use App\Notifications\CheckoutAssetNotification; +use App\Notifications\CheckoutConsumableNotification; +use App\Notifications\CheckoutLicenseNotification; +use Illuminate\Support\Facades\Notification; + class SendingCheckInNotificationsListener { - + /** + * Notify the user about the checked in accessory + */ + public function onAccessoryCheckedIn($event) { + /** + * When the item wasn't checked out to a user, we can't send notifications + */ + if(! $event->checkedOutTo instanceof User) { + return; + } + + Notification::send( + $this->getNotifiables($event), + new CheckinAccessoryNotification($event->accessory, $event->checkedOutTo, $event->checkedInBy, $event->note) + ); + } + + /** + * Notify the user about the checked in asset + */ + public function onAssetCheckedIn($event) { + /** + * When the item wasn't checked out to a user, we can't send notifications + */ + if(! $event->checkedOutTo instanceof User) { + return; + } + + Notification::send( + $this->getNotifiables($event), + new CheckinAssetNotification($event->asset, $event->checkedOutTo, $event->checkedInBy, $event->note) + ); + } + + /** + * Notify the user about the checked in license + */ + public function onLicenseCheckedIn($event) { + /** + * When the item wasn't checked out to a user, we can't send notifications + */ + if(! $event->checkedOutTo instanceof User) { + return; + } + + Notification::send( + $this->getNotifiables($event), + new CheckinLicenseNotification($event->license, $event->checkedOutTo, $event->checkedInBy, $event->note) + ); + } + + /** + * Gets the entities to be notified of the passed event + * + * @param Event $event + * @return Collection + */ + private function getNotifiables($event) { + $notifiables = collect(); + + /** + * Notify the user who checked out the item + */ + $notifiables->push($event->checkedOutTo); + + /** + * Notify Admin users if the settings is activated + */ + if (Setting::getSettings()->admin_cc_email != '') { + $notifiables->push(new AdminRecipient()); + } + + return $notifiables; + } + /** * Register the listeners for the subscriber. * @@ -12,7 +98,20 @@ class SendingCheckInNotificationsListener */ public function subscribe($events) { - + $events->listen( + 'App\Events\AccessoryCheckedIn', + 'App\Listeners\SendingCheckInNotificationsListener@onAccessoryCheckedIn' + ); + + $events->listen( + 'App\Events\AssetCheckedIn', + 'App\Listeners\SendingCheckInNotificationsListener@onAssetCheckedIn' + ); + + $events->listen( + 'App\Events\LicenseCheckedIn', + 'App\Listeners\SendingCheckInNotificationsListener@onLicenseCheckedIn' + ); } } \ No newline at end of file From 112a532618d28cd172f80e16e221d1f7c7b7821f Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 21 Jul 2018 14:03:44 +0200 Subject: [PATCH 06/42] Listen for checkout events and send appropriate notifications --- .../SendingCheckOutNotificationsListener.php | 90 ++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/app/Listeners/SendingCheckOutNotificationsListener.php b/app/Listeners/SendingCheckOutNotificationsListener.php index f9d61eef0..195962c49 100644 --- a/app/Listeners/SendingCheckOutNotificationsListener.php +++ b/app/Listeners/SendingCheckOutNotificationsListener.php @@ -2,8 +2,78 @@ namespace App\Listeners; +use App\Models\Recipients\AdminRecipient; +use App\Models\Setting; +use App\Models\User; +use App\Notifications\CheckoutAccessoryNotification; +use App\Notifications\CheckoutAssetNotification; +use App\Notifications\CheckoutConsumableNotification; +use App\Notifications\CheckoutLicenseNotification; + class SendingCheckOutNotificationsListener { + /** + * Handle user login events. + */ + public function onConsumableCheckedOut($event) { + /** + * Notify the user about the checked out consumable + */ + $this->sendNotification(CheckoutConsumableNotification::class, $event->logEntry); + } + + public function onAccessoryCheckedOut($event) { + /** + * Notify the user about the checked out accessory + */ + $this->sendNotification(CheckoutAccessoryNotification::class, $event->logEntry); + } + + public function onLicenseCheckedOut($event) { + /** + * Notify the user about the checked out license + */ + $this->sendNotification(CheckoutLicenseNotification::class, $event->logEntry); + } + + public function onAssetCheckedOut($event) { + /** + * Notify the user about the checked out asset + */ + $this->sendNotification(CheckoutAssetNotification::class, $event->logEntry); + } + + private function sendNotification($notificationClass, $logEntry) { + /** + * When the item isn't checked out to a user, we can't send notifications + */ + if(! $logEntry->target instanceof User) { + return; + } + + $params = [ + 'log_id' => $logEntry->id, + 'item' => $logEntry->item, + 'target_type' => $logEntry->target_type, + 'admin' => $logEntry->user, + + 'target' => $logEntry->target, + 'note' => $logEntry->note, + 'settings' => Setting::getSettings(), + ]; + + $logEntry->target->notify(new $notificationClass($params)); + + /** + * Notify Admin users if the settings is activated + */ + if (Setting::getSettings()->admin_cc_email != '') { + $recipient = new AdminRecipient(); + + $recipient->notify(new $notificationClass($params)); + } + } + /** * Register the listeners for the subscriber. * @@ -11,7 +81,25 @@ class SendingCheckOutNotificationsListener */ public function subscribe($events) { - + $events->listen( + 'App\Events\ConsumableCheckedOut', + 'App\Listeners\SendingCheckOutNotificationsListener@onConsumableCheckedOut' + ); + + $events->listen( + 'App\Events\AccessoryCheckedOut', + 'App\Listeners\SendingCheckOutNotificationsListener@onAccessoryCheckedOut' + ); + + $events->listen( + 'App\Events\LicenseCheckedOut', + 'App\Listeners\SendingCheckOutNotificationsListener@onLicenseCheckedOut' + ); + + $events->listen( + 'App\Events\AssetCheckedOut', + 'App\Listeners\SendingCheckOutNotificationsListener@onAssetCheckedOut' + ); } } \ No newline at end of file From 722f032895010e608299a9c4b59d75707c5da06d Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 21 Jul 2018 14:06:51 +0200 Subject: [PATCH 07/42] Remove notification sending from loggable trait --- app/Models/Loggable.php | 55 ----------------------------------------- 1 file changed, 55 deletions(-) diff --git a/app/Models/Loggable.php b/app/Models/Loggable.php index 61b1d6deb..f3914d9db 100644 --- a/app/Models/Loggable.php +++ b/app/Models/Loggable.php @@ -6,14 +6,7 @@ use App\Models\Actionlog; use App\Models\Asset; use App\Models\CheckoutRequest; use App\Models\User; -use App\Notifications\CheckinAssetNotification; use App\Notifications\AuditNotification; -use App\Notifications\CheckoutAssetNotification; -use App\Notifications\CheckoutAccessoryNotification; -use App\Notifications\CheckinAccessoryNotification; -use App\Notifications\CheckoutConsumableNotification; -use App\Notifications\CheckoutLicenseNotification; -use App\Notifications\CheckinLicenseNotification; use Illuminate\Support\Facades\Auth; @@ -38,7 +31,6 @@ trait Loggable */ public function logCheckout($note, $target /* What are we checking out to? */) { - $settings = Setting::getSettings(); $log = new Actionlog; $log = $this->determineLogItemType($log); $log->user_id = Auth::user()->id; @@ -63,29 +55,6 @@ trait Loggable $log->note = $note; $log->logaction('checkout'); - $params = [ - 'item' => $log->item, - 'target_type' => $log->target_type, - 'target' => $target, - 'admin' => $log->user, - 'note' => $note, - 'log_id' => $log->id, - 'settings' => $settings, - ]; - - $checkoutClass = null; - - if (method_exists($target, 'notify')) { - $target->notify(new static::$checkoutClass($params)); - } - - // Send to the admin, if settings dictate - $recipient = new \App\Models\Recipients\AdminRecipient(); - - if (($settings->admin_cc_email!='') && (static::$checkoutClass!='')) { - $recipient->notify(new static::$checkoutClass($params)); - } - return $log; } @@ -112,7 +81,6 @@ trait Loggable */ public function logCheckin($target, $note) { - $settings = Setting::getSettings(); $log = new Actionlog; $log->target_type = get_class($target); $log->target_id = $target->id; @@ -140,29 +108,6 @@ trait Loggable $log->user_id = Auth::user()->id; $log->logaction('checkin from'); - $params = [ - 'target' => $target, - 'item' => $log->item, - 'admin' => $log->user, - 'note' => $note, - 'target_type' => $log->target_type, - 'settings' => $settings, - ]; - - - $checkinClass = null; - - if (method_exists($target, 'notify')) { - $target->notify(new static::$checkinClass($params)); - } - - // Send to the admin, if settings dictate - $recipient = new \App\Models\Recipients\AdminRecipient(); - - if (($settings->admin_cc_email!='') && (static::$checkinClass!='')) { - $recipient->notify(new static::$checkinClass($params)); - } - return $log; } From 775e46288e6df0db7e092dbc4697582f096e65e8 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 21 Jul 2018 14:07:06 +0200 Subject: [PATCH 08/42] Cleanup of model attributes --- app/Models/Accessory.php | 7 ------- app/Models/Asset.php | 5 ----- app/Models/Component.php | 7 ------- app/Models/Consumable.php | 6 ------ app/Models/License.php | 6 ------ app/Models/LicenseSeat.php | 6 ------ 6 files changed, 37 deletions(-) diff --git a/app/Models/Accessory.php b/app/Models/Accessory.php index 8fa27515e..cdba5f320 100755 --- a/app/Models/Accessory.php +++ b/app/Models/Accessory.php @@ -47,13 +47,6 @@ class Accessory extends SnipeModel 'supplier' => ['name'], 'location' => ['name'] ]; - - /** - * Set static properties to determine which checkout/checkin handlers we should use - */ - public static $checkoutClass = CheckoutAccessoryNotification::class; - public static $checkinClass = CheckinAccessoryNotification::class; - /** * Accessory validation rules diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 288cb0406..051596ad2 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -34,11 +34,6 @@ class Asset extends Depreciable const USER = 'user'; const ACCEPTANCE_PENDING = 'pending'; - /** - * Set static properties to determine which checkout/checkin handlers we should use - */ - public static $checkoutClass = CheckoutAssetNotification::class; - public static $checkinClass = CheckinAssetNotification::class; /** diff --git a/app/Models/Component.php b/app/Models/Component.php index 8ccc3c307..e880234ba 100644 --- a/app/Models/Component.php +++ b/app/Models/Component.php @@ -20,13 +20,6 @@ class Component extends SnipeModel protected $dates = ['deleted_at', 'purchase_date']; protected $table = 'components'; - - /** - * Set static properties to determine which checkout/checkin handlers we should use - */ - public static $checkoutClass = null; - public static $checkinClass = null; - /** * Category validation rules diff --git a/app/Models/Consumable.php b/app/Models/Consumable.php index b1d3fe222..1b3a0da15 100644 --- a/app/Models/Consumable.php +++ b/app/Models/Consumable.php @@ -20,12 +20,6 @@ class Consumable extends SnipeModel 'requestable' => 'boolean' ]; - /** - * Set static properties to determine which checkout/checkin handlers we should use - */ - public static $checkoutClass = CheckoutConsumableNotification::class; - public static $checkinClass = null; - /** * Category validation rules diff --git a/app/Models/License.php b/app/Models/License.php index a3ac4c598..c953bf9dd 100755 --- a/app/Models/License.php +++ b/app/Models/License.php @@ -18,12 +18,6 @@ class License extends Depreciable { protected $presenter = 'App\Presenters\LicensePresenter'; - /** - * Set static properties to determine which checkout/checkin handlers we should use - */ - public static $checkoutClass = CheckoutLicenseNotification::class; - public static $checkinClass = CheckinLicenseNotification::class; - use SoftDeletes; use CompanyableTrait; diff --git a/app/Models/LicenseSeat.php b/app/Models/LicenseSeat.php index 500391041..c2abc20ea 100755 --- a/app/Models/LicenseSeat.php +++ b/app/Models/LicenseSeat.php @@ -17,12 +17,6 @@ class LicenseSeat extends Model implements ICompanyableChild protected $guarded = 'id'; protected $table = 'license_seats'; - /** - * Set static properties to determine which checkout/checkin handlers we should use - */ - public static $checkoutClass = CheckoutLicenseNotification::class; - public static $checkinClass = CheckinLicenseNotification::class; - public function getCompanyableParents() { return ['asset', 'license']; From 17fc59f98911e997597196a8b3afe6e8ef00354e Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Wed, 25 Jul 2018 10:02:06 +0200 Subject: [PATCH 09/42] Adds back the checkin/checkout events after #5916 --- .../Controllers/Accessories/AccessoryCheckinController.php | 5 ++++- .../Accessories/AccessoryCheckoutController.php | 5 ++++- .../Controllers/Components/ComponentCheckinController.php | 3 +++ .../Controllers/Components/ComponentCheckoutController.php | 6 +++++- .../Consumables/ConsumableCheckoutController.php | 7 +++++-- app/Http/Controllers/Licenses/LicenseCheckinController.php | 6 +++++- 6 files changed, 26 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/Accessories/AccessoryCheckinController.php b/app/Http/Controllers/Accessories/AccessoryCheckinController.php index 83d59ab77..7acfe855c 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckinController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckinController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Accessories; +use App\Events\AccessoryCheckedIn; use App\Http\Controllers\Controller; use App\Models\Accessory; use App\Models\User; @@ -46,7 +47,7 @@ class AccessoryCheckinController extends Controller * @throws \Illuminate\Auth\Access\AuthorizationException * @internal param int $accessoryId */ - public function store($accessoryUserId = null, $backto = null) + public function store(Request $request, $accessoryUserId = null, $backto = null) { // Check if the accessory exists if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) { @@ -63,6 +64,8 @@ class AccessoryCheckinController extends Controller $return_to = e($accessory_user->assigned_to); $accessory->logCheckin(User::find($return_to), e(Input::get('note'))); + event(new AccessoryCheckedIn($accessory, User::find($return_to), Auth::user(), $request->input('note'))); + return redirect()->route("accessories.show", $accessory->id)->with('success', trans('admin/accessories/message.checkin.success')); } // Redirect to the accessory management page with error diff --git a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php index 50c174f0c..830dbef36 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Accessories; +use App\Events\AccessoryCheckedOut; use App\Http\Controllers\Controller; use App\Models\Accessory; use App\Models\User; @@ -77,10 +78,12 @@ class AccessoryCheckoutController extends Controller 'assigned_to' => $request->get('assigned_to') ]); - $accessory->logCheckout(e(Input::get('note')), $user); + $logaction = $accessory->logCheckout(e(Input::get('note')), $user); DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first(); + event(new AccessoryCheckedOut($accessory, $user, $logaction)); + // Redirect to the new accessory page return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.checkout.success')); } diff --git a/app/Http/Controllers/Components/ComponentCheckinController.php b/app/Http/Controllers/Components/ComponentCheckinController.php index 89eab1f5f..cd070a506 100644 --- a/app/Http/Controllers/Components/ComponentCheckinController.php +++ b/app/Http/Controllers/Components/ComponentCheckinController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Components; +use App\Events\ComponentCheckedIn; use App\Http\Controllers\Controller; use App\Models\Actionlog; use App\Models\Asset; @@ -102,6 +103,8 @@ class ComponentCheckinController extends Controller DB::table('components_assets')->where('id', '=', $component_asset_id)->delete(); } + event(new ComponentCheckedIn($component, $component_assets, $request->input('checkin_qty'), $request->input('note'))); + return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success')); } diff --git a/app/Http/Controllers/Components/ComponentCheckoutController.php b/app/Http/Controllers/Components/ComponentCheckoutController.php index 8922317fa..e2c6adad4 100644 --- a/app/Http/Controllers/Components/ComponentCheckoutController.php +++ b/app/Http/Controllers/Components/ComponentCheckoutController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Components; +use App\Events\ComponentCheckedOut; use App\Http\Controllers\Controller; use App\Models\Asset; use App\Models\Component; @@ -86,7 +87,10 @@ class ComponentCheckoutController extends Controller 'asset_id' => $asset_id ]); - $component->logCheckout(e(Input::get('note')), $asset); + $logaction = $component->logCheckout(e(Input::get('note')), $asset); + + event(new ComponentCheckedOut($component, $asset, $logaction)); + return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success')); } } diff --git a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php index 6de35ff9e..b1d75634f 100644 --- a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php +++ b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php @@ -2,10 +2,11 @@ namespace App\Http\Controllers\Consumables; +use App\Events\ConsumableCheckedOut; +use App\Http\Controllers\Controller; use App\Models\Consumable; use App\Models\User; use Illuminate\Http\Request; -use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Input; @@ -67,7 +68,9 @@ class ConsumableCheckoutController extends Controller 'assigned_to' => e(Input::get('assigned_to')) ]); - $consumable->logCheckout(e(Input::get('note')), $user); + $logaction = $consumable->logCheckout(e(Input::get('note')), $user); + + event(new ConsumableCheckedOut($consumable, $user, $logaction)); // Redirect to the new consumable page return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.checkout.success')); diff --git a/app/Http/Controllers/Licenses/LicenseCheckinController.php b/app/Http/Controllers/Licenses/LicenseCheckinController.php index f942aa1e7..6144c3506 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckinController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckinController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Licenses; +use App\Events\LicenseCheckedIn; use App\Models\Asset; use App\Models\License; use App\Models\LicenseSeat; @@ -49,7 +50,7 @@ class LicenseCheckinController extends Controller * @return \Illuminate\Http\RedirectResponse * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function store($seatId = null, $backTo = null) + public function store(Request $request, $seatId = null, $backTo = null) { // Check if the asset exists if (is_null($licenseSeat = LicenseSeat::find($seatId))) { @@ -89,6 +90,9 @@ class LicenseCheckinController extends Controller // Was the asset updated? if ($licenseSeat->save()) { $licenseSeat->logCheckin($return_to, e(request('note'))); + + event(new LicenseCheckedIn($license, $return_to, Auth::user(), $request->input('note'))); + if ($backTo=='user') { return redirect()->route("users.show", $return_to->id)->with('success', trans('admin/licenses/message.checkin.success')); } From e24f292a1a90abe670348dc06d1ae1f86a8be0fa Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 00:05:45 +0200 Subject: [PATCH 10/42] Updates checkout events to not depend on log --- app/Events/AccessoryCheckedOut.php | 5 +++-- app/Events/AssetCheckedOut.php | 5 +++-- app/Events/ComponentCheckedOut.php | 8 ++++---- app/Events/ConsumableCheckedOut.php | 5 +++-- app/Events/LicenseCheckedOut.php | 5 +++-- .../Accessories/AccessoryCheckoutController.php | 2 +- .../Components/ComponentCheckoutController.php | 2 +- .../Consumables/ConsumableCheckoutController.php | 4 ++-- .../Controllers/Licenses/LicenseCheckoutController.php | 7 +++++++ 9 files changed, 27 insertions(+), 16 deletions(-) diff --git a/app/Events/AccessoryCheckedOut.php b/app/Events/AccessoryCheckedOut.php index 397500238..faf30adb3 100644 --- a/app/Events/AccessoryCheckedOut.php +++ b/app/Events/AccessoryCheckedOut.php @@ -22,10 +22,11 @@ class AccessoryCheckedOut * * @return void */ - public function __construct(Accessory $accessory, $checkedOutTo, Actionlog $logEntry) + public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $note) { $this->accessory = $accessory; $this->checkedOutTo = $checkedOutTo; - $this->logEntry = $logEntry; + $this->checkedOutBy = $checkedOutBy; + $this->note = $note; } } diff --git a/app/Events/AssetCheckedOut.php b/app/Events/AssetCheckedOut.php index 6d2fdea3a..1cd0669ad 100644 --- a/app/Events/AssetCheckedOut.php +++ b/app/Events/AssetCheckedOut.php @@ -22,10 +22,11 @@ class AssetCheckedOut * * @return void */ - public function __construct(Asset $asset, $checkedOutTo, Actionlog $logEntry) + public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $note) { $this->asset = $asset; $this->checkedOutTo = $checkedOutTo; - $this->logEntry = $logEntry; + $this->checkedOutBy = $checkedOutBy; + $this->note = $note; } } diff --git a/app/Events/ComponentCheckedOut.php b/app/Events/ComponentCheckedOut.php index 81826a4d2..f74e7d70f 100644 --- a/app/Events/ComponentCheckedOut.php +++ b/app/Events/ComponentCheckedOut.php @@ -2,7 +2,6 @@ namespace App\Events; -use App\Models\Actionlog; use App\Models\Component; use App\Models\User; use Illuminate\Broadcasting\Channel; @@ -15,17 +14,18 @@ class ComponentCheckedOut public $component; public $checkedOutTo; - public $logEntry; + public $checkedOutBy; /** * Create a new event instance. * * @return void */ - public function __construct(Component $component, $checkedOutTo, Actionlog $logEntry) + public function __construct(Component $component, $checkedOutTo, $quantity, User $checkedOutBy) { $this->component = $component; $this->checkedOutTo = $checkedOutTo; - $this->logEntry = $logEntry; + $this->quantity = $quantity; + $this->checkedOutBy = $checkedOutBy; } } diff --git a/app/Events/ConsumableCheckedOut.php b/app/Events/ConsumableCheckedOut.php index df97561c3..ca002cc2f 100644 --- a/app/Events/ConsumableCheckedOut.php +++ b/app/Events/ConsumableCheckedOut.php @@ -22,10 +22,11 @@ class ConsumableCheckedOut * * @return void */ - public function __construct(Consumable $consumable, $checkedOutTo, Actionlog $logEntry) + public function __construct(Consumable $consumable, $checkedOutTo, User $checkedOutBy, $note) { $this->consumable = $consumable; $this->checkedOutTo = $checkedOutTo; - $this->logEntry = $logEntry; + $this->checkedOutBy = $checkedOutBy; + $this->note = $note; } } diff --git a/app/Events/LicenseCheckedOut.php b/app/Events/LicenseCheckedOut.php index 5d1b8335e..a04b20b7b 100644 --- a/app/Events/LicenseCheckedOut.php +++ b/app/Events/LicenseCheckedOut.php @@ -22,10 +22,11 @@ class LicenseCheckedOut * * @return void */ - public function __construct(License $license, $checkedOutTo, Actionlog $logEntry) + public function __construct(License $license, $checkedOutTo, User $checkedOutBy, $note) { $this->license = $license; $this->checkedOutTo = $checkedOutTo; - $this->logEntry = $logEntry; + $this->checkedOutBy = $checkedOutBy; + $this->note = $note; } } diff --git a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php index 830dbef36..30e5fd25d 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php @@ -82,7 +82,7 @@ class AccessoryCheckoutController extends Controller DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first(); - event(new AccessoryCheckedOut($accessory, $user, $logaction)); + event(new AccessoryCheckedOut($accessory, $user, Auth::user(), $request->input('note')); // Redirect to the new accessory page return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.checkout.success')); diff --git a/app/Http/Controllers/Components/ComponentCheckoutController.php b/app/Http/Controllers/Components/ComponentCheckoutController.php index e2c6adad4..c979f8618 100644 --- a/app/Http/Controllers/Components/ComponentCheckoutController.php +++ b/app/Http/Controllers/Components/ComponentCheckoutController.php @@ -89,7 +89,7 @@ class ComponentCheckoutController extends Controller $logaction = $component->logCheckout(e(Input::get('note')), $asset); - event(new ComponentCheckedOut($component, $asset, $logaction)); + event(new ComponentCheckedOut($component, $asset, $request->input('assigned_qty'), Auth::user())); return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success')); } diff --git a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php index b1d75634f..ef2d30beb 100644 --- a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php +++ b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php @@ -42,7 +42,7 @@ class ConsumableCheckoutController extends Controller * @return \Illuminate\Http\RedirectResponse * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function store($consumableId) + public function store(Request $request, $consumableId) { if (is_null($consumable = Consumable::find($consumableId))) { return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.not_found')); @@ -70,7 +70,7 @@ class ConsumableCheckoutController extends Controller $logaction = $consumable->logCheckout(e(Input::get('note')), $user); - event(new ConsumableCheckedOut($consumable, $user, $logaction)); + event(new ConsumableCheckedOut($consumable, $user, Auth::user(), $request->input('note'))); // Redirect to the new consumable page return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.checkout.success')); diff --git a/app/Http/Controllers/Licenses/LicenseCheckoutController.php b/app/Http/Controllers/Licenses/LicenseCheckoutController.php index 570cd6b41..acde2d620 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckoutController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckoutController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Licenses; +use App\Events\LicenseCheckedOut; use App\Http\Requests\LicenseCheckoutRequest; use App\Models\Asset; use App\Models\License; @@ -104,6 +105,9 @@ class LicenseCheckoutController extends Controller } if ($licenseSeat->save()) { $licenseSeat->logCheckout(request('note'), $target); + + event(new LicenseCheckedOut($licenseSeat->license, $target, Auth::user(), request('note'))); + return true; } return false; @@ -119,6 +123,9 @@ class LicenseCheckoutController extends Controller if ($licenseSeat->save()) { $licenseSeat->logCheckout(request('note'), $target); + + event(new LicenseCheckedOut($licenseSeat->license, $target, Auth::user(), request('note'))); + return true; } return false; From e0423418d2ca391018e74db1c303dddda6ab9df3 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 00:23:58 +0200 Subject: [PATCH 11/42] Moves logging checkin/checkout to separate listener --- .../AccessoryCheckinController.php | 2 +- .../AccessoryCheckoutController.php | 2 - .../Assets/AssetCheckinController.php | 5 +- .../Components/ComponentCheckinController.php | 11 +-- .../ComponentCheckoutController.php | 2 - .../ConsumableCheckoutController.php | 2 - .../Licenses/LicenseCheckinController.php | 2 +- .../Licenses/LicenseCheckoutController.php | 2 - app/Listeners/LogListener.php | 92 +++++++++++++++++++ app/Models/Asset.php | 3 +- app/Providers/EventServiceProvider.php | 2 + 11 files changed, 100 insertions(+), 25 deletions(-) create mode 100644 app/Listeners/LogListener.php diff --git a/app/Http/Controllers/Accessories/AccessoryCheckinController.php b/app/Http/Controllers/Accessories/AccessoryCheckinController.php index 7acfe855c..e64d24f16 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckinController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckinController.php @@ -7,6 +7,7 @@ use App\Http\Controllers\Controller; use App\Models\Accessory; use App\Models\User; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Input; @@ -62,7 +63,6 @@ class AccessoryCheckinController extends Controller // Was the accessory updated? if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) { $return_to = e($accessory_user->assigned_to); - $accessory->logCheckin(User::find($return_to), e(Input::get('note'))); event(new AccessoryCheckedIn($accessory, User::find($return_to), Auth::user(), $request->input('note'))); diff --git a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php index 30e5fd25d..02107bcd2 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php @@ -78,8 +78,6 @@ class AccessoryCheckoutController extends Controller 'assigned_to' => $request->get('assigned_to') ]); - $logaction = $accessory->logCheckout(e(Input::get('note')), $user); - DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first(); event(new AccessoryCheckedOut($accessory, $user, Auth::user(), $request->input('note')); diff --git a/app/Http/Controllers/Assets/AssetCheckinController.php b/app/Http/Controllers/Assets/AssetCheckinController.php index 26f77e7fc..da141f8b3 100644 --- a/app/Http/Controllers/Assets/AssetCheckinController.php +++ b/app/Http/Controllers/Assets/AssetCheckinController.php @@ -7,6 +7,7 @@ use App\Helpers\Helper; use App\Http\Controllers\Controller; use App\Http\Requests\AssetCheckinRequest; use App\Models\Asset; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Redirect; use Illuminate\Support\Facades\View; @@ -83,9 +84,7 @@ class AssetCheckinController extends Controller // Was the asset updated? if ($asset->save()) { - $asset->logCheckin($target, e(request('note'))); - - + event(new AssetCheckedIn($asset, $target, Auth::user(), $request->input('note'))); if ($backto=='user') { diff --git a/app/Http/Controllers/Components/ComponentCheckinController.php b/app/Http/Controllers/Components/ComponentCheckinController.php index cd070a506..24c684c4b 100644 --- a/app/Http/Controllers/Components/ComponentCheckinController.php +++ b/app/Http/Controllers/Components/ComponentCheckinController.php @@ -8,6 +8,7 @@ use App\Models\Actionlog; use App\Models\Asset; use App\Models\Component; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Validator; @@ -87,16 +88,6 @@ class ComponentCheckinController extends Controller DB::table('components_assets')->where('id', $component_asset_id)->update(['assigned_qty' => $qty_remaining_in_checkout]); - $log = new Actionlog(); - $log->user_id = auth()->id(); - $log->action_type = 'checkin from'; - $log->target_type = Asset::class; - $log->target_id = $component_assets->asset_id; - $log->item_id = $component_assets->component_id; - $log->item_type = Component::class; - $log->note = $request->input('note'); - $log->save(); - // If the checked-in qty is exactly the same as the assigned_qty, // we can simply delete the associated components_assets record if ($qty_remaining_in_checkout == 0) { diff --git a/app/Http/Controllers/Components/ComponentCheckoutController.php b/app/Http/Controllers/Components/ComponentCheckoutController.php index c979f8618..c7e7fe3db 100644 --- a/app/Http/Controllers/Components/ComponentCheckoutController.php +++ b/app/Http/Controllers/Components/ComponentCheckoutController.php @@ -87,8 +87,6 @@ class ComponentCheckoutController extends Controller 'asset_id' => $asset_id ]); - $logaction = $component->logCheckout(e(Input::get('note')), $asset); - event(new ComponentCheckedOut($component, $asset, $request->input('assigned_qty'), Auth::user())); return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success')); diff --git a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php index ef2d30beb..0b3d8c6c9 100644 --- a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php +++ b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php @@ -68,8 +68,6 @@ class ConsumableCheckoutController extends Controller 'assigned_to' => e(Input::get('assigned_to')) ]); - $logaction = $consumable->logCheckout(e(Input::get('note')), $user); - event(new ConsumableCheckedOut($consumable, $user, Auth::user(), $request->input('note'))); // Redirect to the new consumable page diff --git a/app/Http/Controllers/Licenses/LicenseCheckinController.php b/app/Http/Controllers/Licenses/LicenseCheckinController.php index 6144c3506..961202846 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckinController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckinController.php @@ -9,6 +9,7 @@ use App\Models\LicenseSeat; use App\Models\User; use Illuminate\Http\Request; use App\Http\Controllers\Controller; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Input; use Illuminate\Support\Facades\Session; use Illuminate\Support\Facades\Validator; @@ -89,7 +90,6 @@ class LicenseCheckinController extends Controller // Was the asset updated? if ($licenseSeat->save()) { - $licenseSeat->logCheckin($return_to, e(request('note'))); event(new LicenseCheckedIn($license, $return_to, Auth::user(), $request->input('note'))); diff --git a/app/Http/Controllers/Licenses/LicenseCheckoutController.php b/app/Http/Controllers/Licenses/LicenseCheckoutController.php index acde2d620..1dd8935f6 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckoutController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckoutController.php @@ -104,7 +104,6 @@ class LicenseCheckoutController extends Controller $licenseSeat->assigned_to = $target->assigned_to; } if ($licenseSeat->save()) { - $licenseSeat->logCheckout(request('note'), $target); event(new LicenseCheckedOut($licenseSeat->license, $target, Auth::user(), request('note'))); @@ -122,7 +121,6 @@ class LicenseCheckoutController extends Controller $licenseSeat->assigned_to = request('assigned_to'); if ($licenseSeat->save()) { - $licenseSeat->logCheckout(request('note'), $target); event(new LicenseCheckedOut($licenseSeat->license, $target, Auth::user(), request('note'))); diff --git a/app/Listeners/LogListener.php b/app/Listeners/LogListener.php new file mode 100644 index 000000000..c3dfe7622 --- /dev/null +++ b/app/Listeners/LogListener.php @@ -0,0 +1,92 @@ +accessory->logCheckin($event->checkedOutTo, $event->note); + } + + public function onAccessoryCheckedOut(AccessoryCheckedOut $event) { + $event->accessory->logCheckout($event->note, $event->checkedOutTo); + } + + public function onAssetCheckedIn(AssetCheckedIn $event) { + $event->asset->logCheckin($event->checkedOutTo, $event->note); + } + + public function onAssetCheckedOut(AssetCheckedOut $event) { + $event->asset->logCheckout($event->note, $event->checkedOutTo); + } + + public function onComponentCheckedIn(ComponentCheckedIn $event) { + $log = new Actionlog(); + $log->user_id = $event->checkedInBy->id; + $log->action_type = 'checkin from'; + $log->target_type = Asset::class; + $log->target_id = $event->checkedOutTo->asset_id; + $log->item_id = $event->checkedOutTo->component_id; + $log->item_type = Component::class; + $log->note = $event->note; + $log->save(); + } + + public function onComponentCheckedOut(ComponentCheckedOut $event) { + $event->component->logCheckout($event->note, $event->checkedOutTo); + } + + public function onConsumableCheckedOut(ConsumableCheckedOut $event) { + $event->consumable->logCheckout($event->note, $event->checkedOutTo); + } + + public function onLicenseCheckedIn(LicenseCheckedIn $event) { + $event->license->logCheckin($event->checkedOutTo, $event->note); + } + + public function onLicenseCheckedOut(LicenseCheckedOut $event) { + $event->license->logCheckout($event->note, $event->checkedOutTo); + } + + /** + * Register the listeners for the subscriber. + * + * @param Illuminate\Events\Dispatcher $events + */ + public function subscribe($events) + { + $list = [ + 'AccessoryCheckedIn', + 'AccessoryCheckedout', + 'AssetCheckedIn', + 'AssetCheckedOut', + 'ComponentCheckedIn', + 'ComponentCheckedOut', + 'ConsumableCheckedOut', + 'LicenseCheckedIn', + 'LicenseCheckedOut', + ]; + + foreach($list as $event) { + $events->listen( + 'App\Events\\' . $event, + 'App\Listeners\LogListener@on' . $event + ); + } + } + +} \ No newline at end of file diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 051596ad2..68d2cf845 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -262,9 +262,8 @@ class Asset extends Depreciable } if ($this->save()) { - $loggedAction = $this->logCheckout($note, $target); - event(new AssetCheckedOut($this, $target, $loggedAction)); + event(new AssetCheckedOut($this, $target, Auth::user(), $note)); $this->increment('checkout_counter', 1); return true; diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 822f82c8c..df3c643b2 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -3,6 +3,7 @@ namespace App\Providers; use Illuminate\Support\Facades\Event; +use App\Listeners\LogListener; use App\Listeners\SendingCheckInNotificationsListener; use App\Listeners\SendingCheckOutNotificationsListener; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; @@ -33,6 +34,7 @@ class EventServiceProvider extends ServiceProvider protected $subscribe = [ SendingCheckOutNotificationsListener::class, SendingCheckInNotificationsListener::class, + LogListener::class ]; /** From 72b43b6526650bb97c8529de2e74444a3218cb8e Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 00:27:19 +0200 Subject: [PATCH 12/42] Updates checkout notifications to use new routes for accepting --- .../SendingCheckOutNotificationsListener.php | 119 +++++++++++------- .../CheckoutAccessoryNotification.php | 30 ++--- .../CheckoutAssetNotification.php | 29 ++--- .../CheckoutConsumableNotification.php | 24 ++-- .../CheckoutLicenseNotification.php | 24 ++-- 5 files changed, 112 insertions(+), 114 deletions(-) diff --git a/app/Listeners/SendingCheckOutNotificationsListener.php b/app/Listeners/SendingCheckOutNotificationsListener.php index 195962c49..1968c13d1 100644 --- a/app/Listeners/SendingCheckOutNotificationsListener.php +++ b/app/Listeners/SendingCheckOutNotificationsListener.php @@ -9,69 +9,100 @@ use App\Notifications\CheckoutAccessoryNotification; use App\Notifications\CheckoutAssetNotification; use App\Notifications\CheckoutConsumableNotification; use App\Notifications\CheckoutLicenseNotification; +use Illuminate\Support\Facades\Notification; class SendingCheckOutNotificationsListener { /** - * Handle user login events. + * Notify the user about the checked out consumable */ public function onConsumableCheckedOut($event) { /** - * Notify the user about the checked out consumable + * When the item wasn't checked out to a user, we can't send notifications */ - $this->sendNotification(CheckoutConsumableNotification::class, $event->logEntry); - } - - public function onAccessoryCheckedOut($event) { - /** - * Notify the user about the checked out accessory - */ - $this->sendNotification(CheckoutAccessoryNotification::class, $event->logEntry); - } - - public function onLicenseCheckedOut($event) { - /** - * Notify the user about the checked out license - */ - $this->sendNotification(CheckoutLicenseNotification::class, $event->logEntry); - } - - public function onAssetCheckedOut($event) { - /** - * Notify the user about the checked out asset - */ - $this->sendNotification(CheckoutAssetNotification::class, $event->logEntry); - } - - private function sendNotification($notificationClass, $logEntry) { - /** - * When the item isn't checked out to a user, we can't send notifications - */ - if(! $logEntry->target instanceof User) { + if(! $event->checkedOutTo instanceof User) { return; } - $params = [ - 'log_id' => $logEntry->id, - 'item' => $logEntry->item, - 'target_type' => $logEntry->target_type, - 'admin' => $logEntry->user, + Notification::send( + $this->getNotifiables($event), + new CheckoutConsumableNotification($event->consumable, $event->checkedOutTo, $event->checkedOutBy, $event->note) + ); + } + + /** + * Notify the user about the checked out accessory + */ + public function onAccessoryCheckedOut($event) { + /** + * When the item wasn't checked out to a user, we can't send notifications + */ + if(! $event->checkedOutTo instanceof User) { + return; + } - 'target' => $logEntry->target, - 'note' => $logEntry->note, - 'settings' => Setting::getSettings(), - ]; + Notification::send( + $this->getNotifiables($event), + new CheckoutAccessoryNotification($event->accessory, $event->checkedOutTo, $event->checkedOutBy, $event->note) + ); + } - $logEntry->target->notify(new $notificationClass($params)); + /** + * Notify the user about the checked out license + */ + public function onLicenseCheckedOut($event) { + /** + * When the item wasn't checked out to a user, we can't send notifications + */ + if(! $event->checkedOutTo instanceof User) { + return; + } + + Notification::send( + $this->getNotifiables($event), + new CheckoutLicenseNotification($event->license, $event->checkedOutTo, $event->checkedOutBy, $event->note) + ); + } + + /** + * Notify the user about the checked out asset + */ + public function onAssetCheckedOut($event) { + /** + * When the item wasn't checked out to a user, we can't send notifications + */ + if(! $event->checkedOutTo instanceof User) { + return; + } + + Notification::send( + $this->getNotifiables($event), + new CheckoutAssetNotification($event->asset, $event->checkedOutTo, $event->checkedOutBy, $event->note) + ); + } + + /** + * Gets the entities to be notified of the passed event + * + * @param Event $event + * @return Collection + */ + private function getNotifiables($event) { + $notifiables = collect(); + + /** + * Notify the user who checked out the item + */ + $notifiables->push($event->checkedOutTo); /** * Notify Admin users if the settings is activated */ if (Setting::getSettings()->admin_cc_email != '') { - $recipient = new AdminRecipient(); + $notifiables->push(new AdminRecipient()); + } - $recipient->notify(new $notificationClass($params)); - } + return $notifiables; } /** diff --git a/app/Notifications/CheckoutAccessoryNotification.php b/app/Notifications/CheckoutAccessoryNotification.php index 4e8950619..3b29a2198 100644 --- a/app/Notifications/CheckoutAccessoryNotification.php +++ b/app/Notifications/CheckoutAccessoryNotification.php @@ -2,6 +2,7 @@ namespace App\Notifications; +use App\Models\Accessory; use App\Models\Setting; use App\Models\SnipeModel; use App\Models\User; @@ -15,33 +16,20 @@ use Illuminate\Support\Facades\Mail; class CheckoutAccessoryNotification extends Notification { use Queueable; - /** - * @var - */ - private $params; /** * Create a new notification instance. - * - * @param $params */ - public function __construct($params) + public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $acceptance, $note) { - $this->target = $params['target']; - $this->item = $params['item']; - $this->admin = $params['admin']; - $this->log_id = $params['log_id']; - $this->note = ''; - $this->last_checkout = ''; - $this->expected_checkin = ''; - $this->target_type = $params['target_type']; - $this->settings = $params['settings']; - - if (array_key_exists('note', $params)) { - $this->note = $params['note']; - } + $this->item = $accessory; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->target = $checkedOutTo; + $this->acceptance = $acceptance; + $this->settings = Setting::getSettings(); } @@ -140,7 +128,7 @@ class CheckoutAccessoryNotification extends Notification 'target' => $this->target, 'eula' => $eula, 'req_accept' => $req_accept, - 'accept_url' => url('/').'/account/accept-asset/'.$this->log_id, + 'accept_url' => route('account.accept.item', ['accessory', $this->item->id]), ]) ->subject(trans('mail.Confirm_accessory_delivery')); diff --git a/app/Notifications/CheckoutAssetNotification.php b/app/Notifications/CheckoutAssetNotification.php index 8451d6dcd..f0323342f 100644 --- a/app/Notifications/CheckoutAssetNotification.php +++ b/app/Notifications/CheckoutAssetNotification.php @@ -2,6 +2,7 @@ namespace App\Notifications; +use App\Models\Asset; use App\Models\Setting; use App\Models\User; use Illuminate\Bus\Queueable; @@ -13,31 +14,24 @@ use Illuminate\Contracts\Queue\ShouldQueue; class CheckoutAssetNotification extends Notification { use Queueable; - /** - * @var - */ - private $params; /** * Create a new notification instance. * * @param $params */ - public function __construct($params) + public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $note) { - $this->target = $params['target']; - $this->item = $params['item']; - $this->admin = $params['admin']; - $this->log_id = $params['log_id']; - $this->note = ''; + + $this->item = $asset; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->target = $checkedOutTo; + + $this->settings = Setting::getSettings(); + $this->last_checkout = ''; $this->expected_checkin = ''; - $this->target_type = $params['target_type']; - $this->settings = $params['settings']; - - if (array_key_exists('note', $params)) { - $this->note = $params['note']; - } if ($this->item->last_checkout) { $this->last_checkout = \App\Helpers\Helper::getFormattedDateObject($this->item->last_checkout, 'date', @@ -151,12 +145,11 @@ class CheckoutAssetNotification extends Notification 'item' => $this->item, 'admin' => $this->admin, 'note' => $this->note, - 'log_id' => $this->note, 'target' => $this->target, 'fields' => $fields, 'eula' => $eula, 'req_accept' => $req_accept, - 'accept_url' => url('/').'/account/accept-asset/'.$this->log_id, + 'accept_url' => route('account.accept.item', ['asset', $this->item->id]), 'last_checkout' => $this->last_checkout, 'expected_checkin' => $this->expected_checkin, ]) diff --git a/app/Notifications/CheckoutConsumableNotification.php b/app/Notifications/CheckoutConsumableNotification.php index d9a80bfc3..7c6dfb7b3 100644 --- a/app/Notifications/CheckoutConsumableNotification.php +++ b/app/Notifications/CheckoutConsumableNotification.php @@ -2,6 +2,7 @@ namespace App\Notifications; +use App\Models\Consumable; use App\Models\Setting; use App\Models\SnipeModel; use App\Models\User; @@ -25,21 +26,15 @@ class CheckoutConsumableNotification extends Notification * * @param $params */ - public function __construct($params) + public function __construct(Consumable $consumable, $checkedOutTo, User $checkedOutBy, $note) { - $this->target = $params['target']; - $this->item = $params['item']; - $this->admin = $params['admin']; - $this->log_id = $params['log_id']; - $this->note = ''; - $this->last_checkout = ''; - $this->expected_checkin = ''; - $this->target_type = $params['target_type']; - $this->settings = $params['settings']; - if (array_key_exists('note', $params)) { - $this->note = $params['note']; - } + $this->item = $consumable; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->target = $checkedOutTo; + + $this->settings = Setting::getSettings(); } @@ -131,11 +126,10 @@ class CheckoutConsumableNotification extends Notification 'item' => $this->item, 'admin' => $this->admin, 'note' => $this->note, - 'log_id' => $this->note, 'target' => $this->target, 'eula' => $eula, 'req_accept' => $req_accept, - 'accept_url' => url('/').'/account/accept-asset/'.$this->log_id, + 'accept_url' => route('account.accept.item', ['consumable', $this->item->id]), ]) ->subject(trans('mail.Confirm_consumable_delivery')); diff --git a/app/Notifications/CheckoutLicenseNotification.php b/app/Notifications/CheckoutLicenseNotification.php index 93530e2cd..e5c697b2a 100644 --- a/app/Notifications/CheckoutLicenseNotification.php +++ b/app/Notifications/CheckoutLicenseNotification.php @@ -2,6 +2,7 @@ namespace App\Notifications; +use App\Models\License; use App\Models\Setting; use App\Models\SnipeModel; use App\Models\User; @@ -25,23 +26,14 @@ class CheckoutLicenseNotification extends Notification * * @param $params */ - public function __construct($params) + public function __construct(License $license, $checkedOutTo, User $checkedOutBy, $note) { - $this->target = $params['target']; - $this->item = $params['item']; - $this->admin = $params['admin']; - $this->log_id = $params['log_id']; - $this->note = ''; - $this->target_type = $params['target_type']; - $this->settings = $params['settings']; - $this->target_type = $params['target_type']; - - if (array_key_exists('note', $params)) { - $this->note = $params['note']; - } - - + $this->item = $license; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->target = $checkedOutTo; + $this->settings = Setting::getSettings(); } /** @@ -133,7 +125,7 @@ class CheckoutLicenseNotification extends Notification 'target' => $this->target, 'eula' => $eula, 'req_accept' => $req_accept, - 'accept_url' => url('/').'/account/accept-asset/'.$this->log_id, + 'accept_url' => route('account.accept.item', ['license', $this->item->id]), ]) ->subject(trans('mail.Confirm_license_delivery')); From 39e6b59335f58ca4ea60dd64582e9e724b858429 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 00:41:31 +0200 Subject: [PATCH 13/42] Fixes some typos --- .../Controllers/Accessories/AccessoryCheckoutController.php | 2 +- .../Controllers/Components/ComponentCheckinController.php | 2 +- app/Listeners/LogListener.php | 5 +++-- app/Notifications/CheckoutAccessoryNotification.php | 5 ++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php index 02107bcd2..13bc9e8fd 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php @@ -80,7 +80,7 @@ class AccessoryCheckoutController extends Controller DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first(); - event(new AccessoryCheckedOut($accessory, $user, Auth::user(), $request->input('note')); + event(new AccessoryCheckedOut($accessory, $user, Auth::user(), $request->input('note'))); // Redirect to the new accessory page return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.checkout.success')); diff --git a/app/Http/Controllers/Components/ComponentCheckinController.php b/app/Http/Controllers/Components/ComponentCheckinController.php index 24c684c4b..fd91daa84 100644 --- a/app/Http/Controllers/Components/ComponentCheckinController.php +++ b/app/Http/Controllers/Components/ComponentCheckinController.php @@ -94,7 +94,7 @@ class ComponentCheckinController extends Controller DB::table('components_assets')->where('id', '=', $component_asset_id)->delete(); } - event(new ComponentCheckedIn($component, $component_assets, $request->input('checkin_qty'), $request->input('note'))); + event(new ComponentCheckedIn($component, $component_assets, Auth::user(), $request->input('checkin_qty'), $request->input('note'))); return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success')); diff --git a/app/Listeners/LogListener.php b/app/Listeners/LogListener.php index c3dfe7622..2b35e6365 100644 --- a/app/Listeners/LogListener.php +++ b/app/Listeners/LogListener.php @@ -47,7 +47,8 @@ class LogListener } public function onComponentCheckedOut(ComponentCheckedOut $event) { - $event->component->logCheckout($event->note, $event->checkedOutTo); + // Since components don't have a "note" field, submit empty note + $event->component->logCheckout(null, $event->checkedOutTo); } public function onConsumableCheckedOut(ConsumableCheckedOut $event) { @@ -71,7 +72,7 @@ class LogListener { $list = [ 'AccessoryCheckedIn', - 'AccessoryCheckedout', + 'AccessoryCheckedOut', 'AssetCheckedIn', 'AssetCheckedOut', 'ComponentCheckedIn', diff --git a/app/Notifications/CheckoutAccessoryNotification.php b/app/Notifications/CheckoutAccessoryNotification.php index 3b29a2198..c00da8d63 100644 --- a/app/Notifications/CheckoutAccessoryNotification.php +++ b/app/Notifications/CheckoutAccessoryNotification.php @@ -20,15 +20,14 @@ class CheckoutAccessoryNotification extends Notification /** * Create a new notification instance. */ - public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $acceptance, $note) + public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $note) { $this->item = $accessory; $this->admin = $checkedOutBy; $this->note = $note; $this->target = $checkedOutTo; - $this->acceptance = $acceptance; - + $this->settings = Setting::getSettings(); } From 830a6cf67e9638931153dcc3d16d43583c2801a4 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 01:15:32 +0200 Subject: [PATCH 14/42] Adds accepting/declining to new controller --- app/Events/ItemAccepted.php | 32 +++++ app/Events/ItemDeclined.php | 32 +++++ .../Account/AcceptanceController.php | 119 +++++++++++++++ app/Listeners/LogListener.php | 24 ++++ resources/views/account/accept.blade.php | 135 ++++++++++++++++++ routes/web.php | 9 ++ 6 files changed, 351 insertions(+) create mode 100644 app/Events/ItemAccepted.php create mode 100644 app/Events/ItemDeclined.php create mode 100644 app/Http/Controllers/Account/AcceptanceController.php create mode 100644 resources/views/account/accept.blade.php diff --git a/app/Events/ItemAccepted.php b/app/Events/ItemAccepted.php new file mode 100644 index 000000000..3c854d9ea --- /dev/null +++ b/app/Events/ItemAccepted.php @@ -0,0 +1,32 @@ +item = $item; + $this->acceptedBy = $acceptedBy; + $this->signature = $signature; + } +} diff --git a/app/Events/ItemDeclined.php b/app/Events/ItemDeclined.php new file mode 100644 index 000000000..a735c193e --- /dev/null +++ b/app/Events/ItemDeclined.php @@ -0,0 +1,32 @@ +item = $item; + $this->declinedBy = $declinedBy; + $this->signature = $signature; + } +} diff --git a/app/Http/Controllers/Account/AcceptanceController.php b/app/Http/Controllers/Account/AcceptanceController.php new file mode 100644 index 000000000..ecf992427 --- /dev/null +++ b/app/Http/Controllers/Account/AcceptanceController.php @@ -0,0 +1,119 @@ +getItemById($type, $id); + + if (is_null($item)) { + return redirect()->reoute('account.accept')->with('error', trans('admin/hardware/message.does_not_exist')); + } + + if ($item->isAccepted()) { + return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.asset_already_accepted')); + } + + if (! $item->isCheckedOutTo(Auth::user())) { + return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.incorrect_user_accepted')); + } + + if (!Company::isCurrentUserHasAccess($item)) { + return redirect()->route('account.accept')->with('error', trans('general.insufficient_permissions')); + } + + return view('account/accept', compact('item')); + } + + public function update(Request $request, $type, $id) { + $item = $this->getItemById($type, $id); + + if (is_null($item)) { + return redirect()->reoute('account.accept')->with('error', trans('admin/hardware/message.does_not_exist')); + } + + if ($item->isAccepted()) { + return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.asset_already_accepted')); + } + + if (! $item->isCheckedOutTo(Auth::user())) { + return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.incorrect_user_accepted')); + } + + if (!Company::isCurrentUserHasAccess($item)) { + return redirect()->route('account.accept')->with('error', trans('general.insufficient_permissions')); + } + + if (!$request->filled('asset_acceptance')) { + return redirect()->back()->with('error', trans('admin/users/message.error.accept_or_decline')); + } + + /** + * Get the signature and save it + */ + if ($request->filled('signature_output')) { + $path = config('app.private_uploads').'/signatures'; + $sig_filename = "siglog-" .Str::uuid() . '-'.date('Y-m-d-his').".png"; + $data_uri = e($request->input('signature_output')); + $encoded_image = explode(",", $data_uri); + $decoded_image = base64_decode($encoded_image[1]); + file_put_contents($path."/".$sig_filename, $decoded_image); + } + + + if ($request->input('asset_acceptance') == 'accepted') { + + $item->accept(Auth::user(), $sig_filename); + + event(new ItemAccepted($item, Auth::user(), $sig_filename)); + + $return_msg = trans('admin/users/message.accepted'); + + } else { + + $item->decline(Auth::user(), $sig_filename); + + event(new ItemDeclined($item, Auth::user(), $sig_filename)); + + $return_msg = trans('admin/users/message.declined'); + + } + + return redirect()->to('account/accept')->with('success', $return_msg); + } + + private function getItemById($type, $id) : ? Acceptable { + switch ($type) { + case 'asset': + $item = Asset::findOrFail($id); + break; + case 'consumable': + $item = Consumable::findOrFail($id); + break; + case 'license': + $item = License::findOrFail($id); + break; + default: + $item = null; + break; + } + + return $item; + } +} \ No newline at end of file diff --git a/app/Listeners/LogListener.php b/app/Listeners/LogListener.php index 2b35e6365..858c9ec29 100644 --- a/app/Listeners/LogListener.php +++ b/app/Listeners/LogListener.php @@ -9,6 +9,8 @@ use App\Events\AssetCheckedOut; use App\Events\ComponentCheckedIn; use App\Events\ComponentCheckedOut; use App\Events\ConsumableCheckedOut; +use App\Events\ItemAccepted; +use App\Events\ItemDeclined; use App\Events\LicenseCheckedIn; use App\Events\LicenseCheckedOut; use App\Models\Actionlog; @@ -63,6 +65,26 @@ class LogListener $event->license->logCheckout($event->note, $event->checkedOutTo); } + public function onItemAccepted(ItemAccepted $event) { + $logaction = new Actionlog(); + $logaction->item()->associate($event->item); + $logaction->target()->associate($event->acceptedBy); + $logaction->accept_signature = $event->signature; + $logaction->action_type = 'accepted'; + + $logaction->save(); + } + + public function onItemDeclined(ItemDeclined $event) { + $logaction = new Actionlog(); + $logaction->item()->associate($event->item); + $logaction->target()->associate($event->declinedBy); + $logaction->accept_signature = $event->signature; + $logaction->action_type = 'declined'; + + $logaction->save(); + } + /** * Register the listeners for the subscriber. * @@ -80,6 +102,8 @@ class LogListener 'ConsumableCheckedOut', 'LicenseCheckedIn', 'LicenseCheckedOut', + 'ItemAccepted', + 'ItemDeclined', ]; foreach($list as $event) { diff --git a/resources/views/account/accept.blade.php b/resources/views/account/accept.blade.php new file mode 100644 index 000000000..9073f8516 --- /dev/null +++ b/resources/views/account/accept.blade.php @@ -0,0 +1,135 @@ +@extends('layouts/default') + +{{-- Page title --}} +@section('title') + Accept {{ $item->present()->name() }} + @parent +@stop + + +{{-- Page content --}} +@section('content') + + + + + + + +
+ + + + +
+
+
+
+
+
+ +
+ +
+ +
+ + @if ($item->getEula()) +
+
+ {!! $item->getEula() !!} +
+
+ @endif + + @if ($snipeSettings->require_accept_signature=='1') +
+ +

Sign below to indicate that you agree to the terms of service:

+ +
+
+ + +
+
+ +
+
+
+ @endif + +
+ +
+ +
+
+
+
+ +@stop + +@section('moar_scripts') + + + +@stop diff --git a/routes/web.php b/routes/web.php index 20974f5f8..4b6d13a7c 100644 --- a/routes/web.php +++ b/routes/web.php @@ -274,6 +274,15 @@ Route::group([ 'prefix' => 'account', 'middleware' => ['auth']], function () { # Account Dashboard Route::get('/', [ 'as' => 'account', 'uses' => 'ViewAssetsController@getIndex' ]); + + Route::get('accept', 'Account\AcceptanceController@index') + ->name('account.accept'); + + Route::get('accept/{type}/{id}', 'Account\AcceptanceController@edit') + ->name('account.accept.item'); + + Route::post('accept/{type}/{id}', 'Account\AcceptanceController@update'); + }); From 43437aac14a07c40dbeeeb6076186b827a4746c8 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 01:16:42 +0200 Subject: [PATCH 15/42] Adds acceptable contract to asset --- app/Models/Asset.php | 51 ++++++++++++++++++++++++++++- app/Models/Contracts/Acceptable.php | 11 +++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 app/Models/Contracts/Acceptable.php diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 68d2cf845..0898e5190 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -5,7 +5,9 @@ use App\Events\AssetCheckedOut; use App\Exceptions\CheckoutNotAllowed; use App\Http\Traits\UniqueSerialTrait; use App\Http\Traits\UniqueUndeletedTrait; +use App\Models\Contracts\Acceptable as AcceptableContract; use App\Models\Traits\Searchable; +use App\Models\User; use App\Presenters\Presentable; use AssetPresenter; use Auth; @@ -24,7 +26,7 @@ use App\Notifications\CheckoutAssetNotification; * * @version v1.0 */ -class Asset extends Depreciable +class Asset extends Depreciable implements AcceptableContract { protected $presenter = 'App\Presenters\AssetPresenter'; use Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait, UniqueSerialTrait; @@ -35,6 +37,53 @@ class Asset extends Depreciable const ACCEPTANCE_PENDING = 'pending'; + /** + * Accept the asset + * + * @param User $acceptedBy The user who accepts the asset + * @param string $signature The filename of the signature, if provided + */ + public function accept(User $acceptedBy, $signature = null) { + $this->accepted = 'accepted'; + $this->save(); + } + + /** + * Decline the asset + * + * @param User $declinedBy The user who declines the asset + * @param string $signature The filename of the signature, if provided + */ + public function decline(User $declinedBy, $signature = null) { + $this->assigned_to = null; + $this->assigned_type = null; + $this->accepted = null; + $this->save(); + } + + /** + * Is the asset already accepted? + * + * @return boolean + */ + public function isAccepted() : bool { + return $this->accepted != 'pending'; + } + + /** + * Is the asset checked out to this user? + * + * @param User $user + * @return boolean + */ + public function isCheckedOutTo(User $user) { + if (is_null($this->assignedTo)) { + return false; + } + + return $this->assignedTo->is($user); + } + /** * The database table used by the model. diff --git a/app/Models/Contracts/Acceptable.php b/app/Models/Contracts/Acceptable.php new file mode 100644 index 000000000..de253607a --- /dev/null +++ b/app/Models/Contracts/Acceptable.php @@ -0,0 +1,11 @@ + Date: Sat, 28 Jul 2018 12:43:09 +0200 Subject: [PATCH 16/42] Moves license checkout stuff to the license seat Since we are really checking out a license seat instead of the whole license, we operate the checkin/checkout on the license seat instance. --- ...eCheckedIn.php => LicenseSeatCheckedIn.php} | 7 ++++--- ...heckedOut.php => LicenseSeatCheckedOut.php} | 9 +++++---- .../Licenses/LicenseCheckoutController.php | 5 +++-- .../SendingCheckInNotificationsListener.php | 8 ++++---- app/Models/LicenseSeat.php | 14 +++++++++++--- ....php => CheckinLicenseSeatNotification.php} | 7 ++++--- ...php => CheckoutLicenseSeatNotification.php} | 12 ++++++++---- app/Presenters/LicenseSeatPresenter.php | 18 ++++++++++++++++++ 8 files changed, 57 insertions(+), 23 deletions(-) rename app/Events/{LicenseCheckedIn.php => LicenseSeatCheckedIn.php} (74%) rename app/Events/{LicenseCheckedOut.php => LicenseSeatCheckedOut.php} (70%) rename app/Notifications/{CheckinLicenseNotification.php => CheckinLicenseSeatNotification.php} (93%) rename app/Notifications/{CheckoutLicenseNotification.php => CheckoutLicenseSeatNotification.php} (90%) create mode 100644 app/Presenters/LicenseSeatPresenter.php diff --git a/app/Events/LicenseCheckedIn.php b/app/Events/LicenseSeatCheckedIn.php similarity index 74% rename from app/Events/LicenseCheckedIn.php rename to app/Events/LicenseSeatCheckedIn.php index 36562296b..80e7ecbaf 100644 --- a/app/Events/LicenseCheckedIn.php +++ b/app/Events/LicenseSeatCheckedIn.php @@ -4,6 +4,7 @@ namespace App\Events; use App\Models\Actionlog; use App\Models\License; +use App\Models\LicenseSeat; use App\Models\User; use Illuminate\Broadcasting\Channel; use Illuminate\Foundation\Events\Dispatchable; @@ -13,7 +14,7 @@ class LicenseCheckedIn { use Dispatchable, SerializesModels; - public $license; + public $licenseSeat; public $checkedOutTo; public $checkedInBy; public $note; @@ -23,9 +24,9 @@ class LicenseCheckedIn * * @return void */ - public function __construct(License $license, $checkedOutTo, User $checkedInBy, $note) + public function __construct(LicenseSeat $licenseSeat, $checkedOutTo, User $checkedInBy, $note) { - $this->license = $license; + $this->licenseSeat = $licenseSeat; $this->checkedOutTo = $checkedOutTo; $this->checkedInBy = $checkedInBy; $this->note = $note; diff --git a/app/Events/LicenseCheckedOut.php b/app/Events/LicenseSeatCheckedOut.php similarity index 70% rename from app/Events/LicenseCheckedOut.php rename to app/Events/LicenseSeatCheckedOut.php index a04b20b7b..6b057be43 100644 --- a/app/Events/LicenseCheckedOut.php +++ b/app/Events/LicenseSeatCheckedOut.php @@ -4,16 +4,17 @@ namespace App\Events; use App\Models\Actionlog; use App\Models\License; +use App\Models\LicenseSeat; use App\Models\User; use Illuminate\Broadcasting\Channel; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; -class LicenseCheckedOut +class LicenseSeatCheckedOut { use Dispatchable, SerializesModels; - public $license; + public $licenseSeat; public $checkedOutTo; public $logEntry; @@ -22,9 +23,9 @@ class LicenseCheckedOut * * @return void */ - public function __construct(License $license, $checkedOutTo, User $checkedOutBy, $note) + public function __construct(LicenseSeat $licenseSeat, $checkedOutTo, User $checkedOutBy, $note) { - $this->license = $license; + $this->licenseSeat = $licenseSeat; $this->checkedOutTo = $checkedOutTo; $this->checkedOutBy = $checkedOutBy; $this->note = $note; diff --git a/app/Http/Controllers/Licenses/LicenseCheckoutController.php b/app/Http/Controllers/Licenses/LicenseCheckoutController.php index 1dd8935f6..a1a02c823 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckoutController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckoutController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Licenses; use App\Events\LicenseCheckedOut; +use App\Events\LicenseSeatCheckedOut; use App\Http\Requests\LicenseCheckoutRequest; use App\Models\Asset; use App\Models\License; @@ -105,7 +106,7 @@ class LicenseCheckoutController extends Controller } if ($licenseSeat->save()) { - event(new LicenseCheckedOut($licenseSeat->license, $target, Auth::user(), request('note'))); + event(new LicenseSeatCheckedOut($licenseSeat, $target, Auth::user(), request('note'))); return true; } @@ -122,7 +123,7 @@ class LicenseCheckoutController extends Controller if ($licenseSeat->save()) { - event(new LicenseCheckedOut($licenseSeat->license, $target, Auth::user(), request('note'))); + event(new LicenseSeatCheckedOut($licenseSeat, $target, Auth::user(), request('note'))); return true; } diff --git a/app/Listeners/SendingCheckInNotificationsListener.php b/app/Listeners/SendingCheckInNotificationsListener.php index 8a84c42fa..aa176e19f 100644 --- a/app/Listeners/SendingCheckInNotificationsListener.php +++ b/app/Listeners/SendingCheckInNotificationsListener.php @@ -53,7 +53,7 @@ class SendingCheckInNotificationsListener /** * Notify the user about the checked in license */ - public function onLicenseCheckedIn($event) { + public function onLicenseSeatCheckedIn($event) { /** * When the item wasn't checked out to a user, we can't send notifications */ @@ -63,7 +63,7 @@ class SendingCheckInNotificationsListener Notification::send( $this->getNotifiables($event), - new CheckinLicenseNotification($event->license, $event->checkedOutTo, $event->checkedInBy, $event->note) + new CheckinLicenseSeatNotification($event->licenseSeat, $event->checkedOutTo, $event->checkedInBy, $event->note) ); } @@ -109,8 +109,8 @@ class SendingCheckInNotificationsListener ); $events->listen( - 'App\Events\LicenseCheckedIn', - 'App\Listeners\SendingCheckInNotificationsListener@onLicenseCheckedIn' + 'App\Events\LicenseSeatCheckedIn', + 'App\Listeners\SendingCheckInNotificationsListener@onLicenseSeatCheckedIn' ); } diff --git a/app/Models/LicenseSeat.php b/app/Models/LicenseSeat.php index c2abc20ea..5e387930e 100755 --- a/app/Models/LicenseSeat.php +++ b/app/Models/LicenseSeat.php @@ -2,17 +2,21 @@ namespace App\Models; use App\Models\Loggable; +use App\Notifications\CheckinLicenseNotification; +use App\Notifications\CheckoutLicenseNotification; +use App\Presenters\Presentable; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; -use App\Notifications\CheckoutLicenseNotification; -use App\Notifications\CheckinLicenseNotification; -class LicenseSeat extends Model implements ICompanyableChild +class LicenseSeat extends SnipeModel implements ICompanyableChild { use CompanyableChildTrait; use SoftDeletes; use Loggable; + protected $presenter = 'App\Presenters\LicenseSeatPresenter'; + use Presentable; + protected $dates = ['deleted_at']; protected $guarded = 'id'; protected $table = 'license_seats'; @@ -22,6 +26,10 @@ class LicenseSeat extends Model implements ICompanyableChild return ['asset', 'license']; } + public function getEula() { + return $this->license->getEula(); + } + /** * Establishes the seat -> license relationship * diff --git a/app/Notifications/CheckinLicenseNotification.php b/app/Notifications/CheckinLicenseSeatNotification.php similarity index 93% rename from app/Notifications/CheckinLicenseNotification.php rename to app/Notifications/CheckinLicenseSeatNotification.php index 3ff06d2ca..69554d493 100644 --- a/app/Notifications/CheckinLicenseNotification.php +++ b/app/Notifications/CheckinLicenseSeatNotification.php @@ -3,6 +3,7 @@ namespace App\Notifications; use App\Models\License; +use App\Models\LicenseSeat; use App\Models\Setting; use App\Models\SnipeModel; use App\Models\User; @@ -13,7 +14,7 @@ use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Mail; -class CheckinLicenseNotification extends Notification +class CheckinLicenseSeatNotification extends Notification { use Queueable; /** @@ -26,10 +27,10 @@ class CheckinLicenseNotification extends Notification * * @param $params */ - public function __construct(License $license, $checkedOutTo, User $checkedInBy, $note) + public function __construct(LicenseSeat $licenseSeat, $checkedOutTo, User $checkedInBy, $note) { $this->target = $checkedOutTo; - $this->item = $license; + $this->item = $licenseSeat->license; $this->admin = $checkedInBy; $this->note = $note; $this->settings = Setting::getSettings(); diff --git a/app/Notifications/CheckoutLicenseNotification.php b/app/Notifications/CheckoutLicenseSeatNotification.php similarity index 90% rename from app/Notifications/CheckoutLicenseNotification.php rename to app/Notifications/CheckoutLicenseSeatNotification.php index e5c697b2a..74df6e2f4 100644 --- a/app/Notifications/CheckoutLicenseNotification.php +++ b/app/Notifications/CheckoutLicenseSeatNotification.php @@ -3,6 +3,7 @@ namespace App\Notifications; use App\Models\License; +use App\Models\LicenseSeat; use App\Models\Setting; use App\Models\SnipeModel; use App\Models\User; @@ -13,7 +14,7 @@ use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Mail; -class CheckoutLicenseNotification extends Notification +class CheckoutLicenseSeatNotification extends Notification { use Queueable; /** @@ -26,12 +27,13 @@ class CheckoutLicenseNotification extends Notification * * @param $params */ - public function __construct(License $license, $checkedOutTo, User $checkedOutBy, $note) + public function __construct(LicenseSeat $licenseSeat, $checkedOutTo, User $checkedOutBy, $acceptance, $note) { - $this->item = $license; + $this->item = $licenseSeat->license; $this->admin = $checkedOutBy; $this->note = $note; $this->target = $checkedOutTo; + $this->acceptance = $acceptance; $this->settings = Setting::getSettings(); } @@ -117,6 +119,8 @@ class CheckoutLicenseNotification extends Notification $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, @@ -125,7 +129,7 @@ class CheckoutLicenseNotification extends Notification 'target' => $this->target, 'eula' => $eula, 'req_accept' => $req_accept, - 'accept_url' => route('account.accept.item', ['license', $this->item->id]), + 'accept_url' => $accept_url, ]) ->subject(trans('mail.Confirm_license_delivery')); diff --git a/app/Presenters/LicenseSeatPresenter.php b/app/Presenters/LicenseSeatPresenter.php new file mode 100644 index 000000000..5267aa729 --- /dev/null +++ b/app/Presenters/LicenseSeatPresenter.php @@ -0,0 +1,18 @@ +model->license->name; + } +} From 8648d53d25fba99c6f2b6ffe01ec6e3ad7537e84 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 12:45:33 +0200 Subject: [PATCH 17/42] Adds checkout acceptances A checkout acceptance gets generated for every item that needs to be checked out. This resource tracks the user user who can accept the item and their signature --- ...{ItemDeclined.php => CheckoutAccepted.php} | 13 +--- ...{ItemAccepted.php => CheckoutDeclined.php} | 13 +--- .../Account/AcceptanceController.php | 73 +++++++++---------- app/Listeners/LogListener.php | 34 ++++++--- .../SendingCheckOutNotificationsListener.php | 60 +++++++++++++-- app/Models/CheckoutAcceptance.php | 50 +++++++++++++ .../CheckoutAccessoryNotification.php | 7 +- .../CheckoutAssetNotification.php | 7 +- .../CheckoutConsumableNotification.php | 7 +- ...3826_create_checkout_acceptances_table.php | 41 +++++++++++ .../create.blade.php} | 6 +- .../views/account/accept/index.blade.php | 61 ++++++++++++++++ resources/views/layouts/default.blade.php | 6 +- routes/web.php | 4 +- 14 files changed, 296 insertions(+), 86 deletions(-) rename app/Events/{ItemDeclined.php => CheckoutAccepted.php} (57%) rename app/Events/{ItemAccepted.php => CheckoutDeclined.php} (58%) create mode 100644 app/Models/CheckoutAcceptance.php create mode 100644 database/migrations/2018_07_28_023826_create_checkout_acceptances_table.php rename resources/views/account/{accept.blade.php => accept/create.blade.php} (96%) create mode 100755 resources/views/account/accept/index.blade.php diff --git a/app/Events/ItemDeclined.php b/app/Events/CheckoutAccepted.php similarity index 57% rename from app/Events/ItemDeclined.php rename to app/Events/CheckoutAccepted.php index a735c193e..0b26e6947 100644 --- a/app/Events/ItemDeclined.php +++ b/app/Events/CheckoutAccepted.php @@ -4,29 +4,24 @@ namespace App\Events; use App\Models\Accessory; use App\Models\Actionlog; +use App\Models\CheckoutAcceptance; use App\Models\Contracts\Acceptable; use App\Models\User; use Illuminate\Broadcasting\Channel; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; -class ItemDeclined +class CheckoutAccepted { use Dispatchable, SerializesModels; - public $item; - public $declinedBy; - public $signature; - /** * Create a new event instance. * * @return void */ - public function __construct(Acceptable $item, User $declinedBy, string $signature) + public function __construct(CheckoutAcceptance $acceptance) { - $this->item = $item; - $this->declinedBy = $declinedBy; - $this->signature = $signature; + $this->acceptance = $acceptance; } } diff --git a/app/Events/ItemAccepted.php b/app/Events/CheckoutDeclined.php similarity index 58% rename from app/Events/ItemAccepted.php rename to app/Events/CheckoutDeclined.php index 3c854d9ea..2aae6dd91 100644 --- a/app/Events/ItemAccepted.php +++ b/app/Events/CheckoutDeclined.php @@ -4,29 +4,24 @@ namespace App\Events; use App\Models\Accessory; use App\Models\Actionlog; +use App\Models\CheckoutAcceptance; use App\Models\Contracts\Acceptable; use App\Models\User; use Illuminate\Broadcasting\Channel; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; -class ItemAccepted +class CheckoutDeclined { use Dispatchable, SerializesModels; - - public $item; - public $acceptedBy; - public $signature; /** * Create a new event instance. * * @return void */ - public function __construct(Acceptable $item, User $acceptedBy, string $signature) + public function __construct(CheckoutAcceptance $acceptance) { - $this->item = $item; - $this->acceptedBy = $acceptedBy; - $this->signature = $signature; + $this->acceptance = $acceptance; } } diff --git a/app/Http/Controllers/Account/AcceptanceController.php b/app/Http/Controllers/Account/AcceptanceController.php index ecf992427..5c7235a2a 100644 --- a/app/Http/Controllers/Account/AcceptanceController.php +++ b/app/Http/Controllers/Account/AcceptanceController.php @@ -1,10 +1,13 @@ pending()->get(); + + return view('account/accept.index', compact('acceptances')); } - public function edit(Request $request, $type, $id) { + public function create(Request $request, $id) { - $item = $this->getItemById($type, $id); + $acceptance = CheckoutAcceptance::find($id); - if (is_null($item)) { + if (is_null($acceptance)) { return redirect()->reoute('account.accept')->with('error', trans('admin/hardware/message.does_not_exist')); } - if ($item->isAccepted()) { + if (! $acceptance->isPending()) { return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.asset_already_accepted')); } - if (! $item->isCheckedOutTo(Auth::user())) { + if (! $acceptance->isCheckedOutTo(Auth::user())) { return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.incorrect_user_accepted')); } - if (!Company::isCurrentUserHasAccess($item)) { + if (!Company::isCurrentUserHasAccess($acceptance->checkoutable)) { return redirect()->route('account.accept')->with('error', trans('general.insufficient_permissions')); } - return view('account/accept', compact('item')); + return view('account/accept.create', compact('acceptance')); } - public function update(Request $request, $type, $id) { - $item = $this->getItemById($type, $id); + public function store(Request $request, $id) { + + $acceptance = CheckoutAcceptance::find($id); - if (is_null($item)) { + if (is_null($acceptance)) { return redirect()->reoute('account.accept')->with('error', trans('admin/hardware/message.does_not_exist')); } - if ($item->isAccepted()) { + if (! $acceptance->isPending()) { return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.asset_already_accepted')); - } + } - if (! $item->isCheckedOutTo(Auth::user())) { + if (! $acceptance->isCheckedOutTo(Auth::user())) { return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.incorrect_user_accepted')); } - if (!Company::isCurrentUserHasAccess($item)) { + if (!Company::isCurrentUserHasAccess($acceptance->checkoutable)) { return redirect()->route('account.accept')->with('error', trans('general.insufficient_permissions')); - } + } if (!$request->filled('asset_acceptance')) { return redirect()->back()->with('error', trans('admin/users/message.error.accept_or_decline')); @@ -79,41 +85,30 @@ class AcceptanceController extends Controller { if ($request->input('asset_acceptance') == 'accepted') { - $item->accept(Auth::user(), $sig_filename); + $acceptance->accepted_at = now(); + $acceptance->signature_filename = $sig_filename; + $acceptance->save(); - event(new ItemAccepted($item, Auth::user(), $sig_filename)); + // TODO: Update state for the checkoutable + + event(new CheckoutAccepted($acceptance)); $return_msg = trans('admin/users/message.accepted'); } else { - $item->decline(Auth::user(), $sig_filename); + $acceptance->declined_at = now(); + $acceptance->signature_filename = $sig_filename; + $acceptance->save(); - event(new ItemDeclined($item, Auth::user(), $sig_filename)); + // TODO: Update state for the checkoutable + + event(new CheckoutDeclined($acceptance)); $return_msg = trans('admin/users/message.declined'); } return redirect()->to('account/accept')->with('success', $return_msg); - } - - private function getItemById($type, $id) : ? Acceptable { - switch ($type) { - case 'asset': - $item = Asset::findOrFail($id); - break; - case 'consumable': - $item = Consumable::findOrFail($id); - break; - case 'license': - $item = License::findOrFail($id); - break; - default: - $item = null; - break; - } - - return $item; } } \ No newline at end of file diff --git a/app/Listeners/LogListener.php b/app/Listeners/LogListener.php index 858c9ec29..9cd8ec271 100644 --- a/app/Listeners/LogListener.php +++ b/app/Listeners/LogListener.php @@ -6,6 +6,8 @@ use App\Events\AccessoryCheckedIn; use App\Events\AccessoryCheckedOut; use App\Events\AssetCheckedIn; use App\Events\AssetCheckedOut; +use App\Events\CheckoutAccepted; +use App\Events\CheckoutDeclined; use App\Events\ComponentCheckedIn; use App\Events\ComponentCheckedOut; use App\Events\ConsumableCheckedOut; @@ -16,6 +18,7 @@ use App\Events\LicenseCheckedOut; use App\Models\Actionlog; use App\Models\Asset; use App\Models\Component; +use App\Models\LicenseSeat; class LogListener @@ -65,23 +68,34 @@ class LogListener $event->license->logCheckout($event->note, $event->checkedOutTo); } - public function onItemAccepted(ItemAccepted $event) { + public function onCheckoutAccepted(CheckoutAccepted $event) { $logaction = new Actionlog(); - $logaction->item()->associate($event->item); - $logaction->target()->associate($event->acceptedBy); - $logaction->accept_signature = $event->signature; + + $logaction->item()->associate($event->acceptance->checkoutable); + $logaction->target()->associate($event->acceptance->assignedTo); + $logaction->accept_signature = $event->acceptance->signature_filename; $logaction->action_type = 'accepted'; + // TODO: log the actual license seat that was checked out + if($event->acceptance->checkoutable instanceof LicenseSeat) { + $logaction->item()->associate($event->acceptance->checkoutable->license); + } + $logaction->save(); } - public function onItemDeclined(ItemDeclined $event) { + public function onCheckoutDeclined(CheckoutDeclined $event) { $logaction = new Actionlog(); - $logaction->item()->associate($event->item); - $logaction->target()->associate($event->declinedBy); - $logaction->accept_signature = $event->signature; + $logaction->item()->associate($event->acceptance->checkoutable); + $logaction->target()->associate($event->acceptance->assignedTo); + $logaction->accept_signature = $event->acceptance->signature_filename; $logaction->action_type = 'declined'; + // TODO: log the actual license seat that was checked out + if($event->acceptance->checkoutable instanceof LicenseSeat) { + $logaction->item()->associate($event->acceptance->checkoutable->license); + } + $logaction->save(); } @@ -102,8 +116,8 @@ class LogListener 'ConsumableCheckedOut', 'LicenseCheckedIn', 'LicenseCheckedOut', - 'ItemAccepted', - 'ItemDeclined', + 'CheckoutAccepted', + 'CheckoutDeclined', ]; foreach($list as $event) { diff --git a/app/Listeners/SendingCheckOutNotificationsListener.php b/app/Listeners/SendingCheckOutNotificationsListener.php index 1968c13d1..ed459c5a5 100644 --- a/app/Listeners/SendingCheckOutNotificationsListener.php +++ b/app/Listeners/SendingCheckOutNotificationsListener.php @@ -2,6 +2,7 @@ namespace App\Listeners; +use App\Models\CheckoutAcceptance; use App\Models\Recipients\AdminRecipient; use App\Models\Setting; use App\Models\User; @@ -9,6 +10,7 @@ use App\Notifications\CheckoutAccessoryNotification; use App\Notifications\CheckoutAssetNotification; use App\Notifications\CheckoutConsumableNotification; use App\Notifications\CheckoutLicenseNotification; +use App\Notifications\CheckoutLicenseSeatNotification; use Illuminate\Support\Facades\Notification; class SendingCheckOutNotificationsListener @@ -24,9 +26,20 @@ class SendingCheckOutNotificationsListener return; } + /** + * Make a checkout acceptance and attach it in the notification + */ + $acceptance = null; + if ($event->consumable->requireAcceptance()) { + $acceptance = new CheckoutAcceptance; + $acceptance->checkoutable()->associate($event->consumable); + $acceptance->assignedTo()->associate($event->checkedOutTo); + $acceptance->save(); + } + Notification::send( $this->getNotifiables($event), - new CheckoutConsumableNotification($event->consumable, $event->checkedOutTo, $event->checkedOutBy, $event->note) + new CheckoutConsumableNotification($event->consumable, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note) ); } @@ -41,16 +54,27 @@ class SendingCheckOutNotificationsListener return; } + /** + * Make a checkout acceptance and attach it in the notification + */ + $acceptance = null; + if ($event->accessory->requireAcceptance()) { + $acceptance = new CheckoutAcceptance; + $acceptance->checkoutable()->associate($event->accessory); + $acceptance->assignedTo()->associate($event->checkedOutTo); + $acceptance->save(); + } + Notification::send( $this->getNotifiables($event), - new CheckoutAccessoryNotification($event->accessory, $event->checkedOutTo, $event->checkedOutBy, $event->note) + new CheckoutAccessoryNotification($event->accessory, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note) ); } /** * Notify the user about the checked out license */ - public function onLicenseCheckedOut($event) { + public function onLicenseSeatCheckedOut($event) { /** * When the item wasn't checked out to a user, we can't send notifications */ @@ -58,9 +82,20 @@ class SendingCheckOutNotificationsListener return; } + /** + * Make a checkout acceptance and attach it in the notification + */ + $acceptance = null; + if ($event->licenseSeat->license->requireAcceptance()) { + $acceptance = new CheckoutAcceptance; + $acceptance->checkoutable()->associate($event->licenseSeat); + $acceptance->assignedTo()->associate($event->checkedOutTo); + $acceptance->save(); + } + Notification::send( $this->getNotifiables($event), - new CheckoutLicenseNotification($event->license, $event->checkedOutTo, $event->checkedOutBy, $event->note) + new CheckoutLicenseSeatNotification($event->licenseSeat, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note) ); } @@ -75,9 +110,20 @@ class SendingCheckOutNotificationsListener return; } + /** + * Make a checkout acceptance and attach it in the notification + */ + $acceptance = null; + if ($event->asset->requireAcceptance()) { + $acceptance = new CheckoutAcceptance; + $acceptance->checkoutable()->associate($event->asset); + $acceptance->assignedTo()->associate($event->checkedOutTo); + $acceptance->save(); + } + Notification::send( $this->getNotifiables($event), - new CheckoutAssetNotification($event->asset, $event->checkedOutTo, $event->checkedOutBy, $event->note) + new CheckoutAssetNotification($event->asset, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note) ); } @@ -123,8 +169,8 @@ class SendingCheckOutNotificationsListener ); $events->listen( - 'App\Events\LicenseCheckedOut', - 'App\Listeners\SendingCheckOutNotificationsListener@onLicenseCheckedOut' + 'App\Events\LicenseSeatCheckedOut', + 'App\Listeners\SendingCheckOutNotificationsListener@onLicenseSeatCheckedOut' ); $events->listen( diff --git a/app/Models/CheckoutAcceptance.php b/app/Models/CheckoutAcceptance.php new file mode 100644 index 000000000..f3d2355e5 --- /dev/null +++ b/app/Models/CheckoutAcceptance.php @@ -0,0 +1,50 @@ +morphTo(); + } + + public function assignedTo() { + return $this->belongsTo(User::class); + } + + public function isPending() { + return $this->accepted_at == null && $this->declined_at == null; + } + + public function isCheckedOutTo(User $user) { + return $this->assignedTo->is($user); + } + + public function scopeForUser(Builder $query, User $user) { + return $query->where('assigned_to_id', $user->id); + } + + public function scopePending(Builder $query) { + return $query->whereNull('accepted_at')->whereNull('declined_at'); + } +} diff --git a/app/Notifications/CheckoutAccessoryNotification.php b/app/Notifications/CheckoutAccessoryNotification.php index c00da8d63..11368bf06 100644 --- a/app/Notifications/CheckoutAccessoryNotification.php +++ b/app/Notifications/CheckoutAccessoryNotification.php @@ -20,13 +20,14 @@ class CheckoutAccessoryNotification extends Notification /** * Create a new notification instance. */ - public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $note) + public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $acceptance, $note) { $this->item = $accessory; $this->admin = $checkedOutBy; $this->note = $note; $this->target = $checkedOutTo; + $this->acceptance = $acceptance; $this->settings = Setting::getSettings(); @@ -119,6 +120,8 @@ class CheckoutAccessoryNotification extends Notification $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-accessory', [ 'item' => $this->item, @@ -127,7 +130,7 @@ class CheckoutAccessoryNotification extends Notification 'target' => $this->target, 'eula' => $eula, 'req_accept' => $req_accept, - 'accept_url' => route('account.accept.item', ['accessory', $this->item->id]), + 'accept_url' => $accept_url, ]) ->subject(trans('mail.Confirm_accessory_delivery')); diff --git a/app/Notifications/CheckoutAssetNotification.php b/app/Notifications/CheckoutAssetNotification.php index f0323342f..db4ba294f 100644 --- a/app/Notifications/CheckoutAssetNotification.php +++ b/app/Notifications/CheckoutAssetNotification.php @@ -20,13 +20,14 @@ class CheckoutAssetNotification extends Notification * * @param $params */ - public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $note) + public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $acceptance, $note) { $this->item = $asset; $this->admin = $checkedOutBy; $this->note = $note; $this->target = $checkedOutTo; + $this->acceptance = $acceptance; $this->settings = Setting::getSettings(); @@ -140,6 +141,8 @@ class CheckoutAssetNotification extends Notification $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, @@ -149,7 +152,7 @@ class CheckoutAssetNotification extends Notification 'fields' => $fields, 'eula' => $eula, 'req_accept' => $req_accept, - 'accept_url' => route('account.accept.item', ['asset', $this->item->id]), + 'accept_url' => $accept_url, 'last_checkout' => $this->last_checkout, 'expected_checkin' => $this->expected_checkin, ]) diff --git a/app/Notifications/CheckoutConsumableNotification.php b/app/Notifications/CheckoutConsumableNotification.php index 7c6dfb7b3..40780c7ab 100644 --- a/app/Notifications/CheckoutConsumableNotification.php +++ b/app/Notifications/CheckoutConsumableNotification.php @@ -26,13 +26,14 @@ class CheckoutConsumableNotification extends Notification * * @param $params */ - public function __construct(Consumable $consumable, $checkedOutTo, User $checkedOutBy, $note) + 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->settings = Setting::getSettings(); @@ -121,6 +122,8 @@ class CheckoutConsumableNotification extends Notification $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, @@ -129,7 +132,7 @@ class CheckoutConsumableNotification extends Notification 'target' => $this->target, 'eula' => $eula, 'req_accept' => $req_accept, - 'accept_url' => route('account.accept.item', ['consumable', $this->item->id]), + 'accept_url' => $accept_url, ]) ->subject(trans('mail.Confirm_consumable_delivery')); diff --git a/database/migrations/2018_07_28_023826_create_checkout_acceptances_table.php b/database/migrations/2018_07_28_023826_create_checkout_acceptances_table.php new file mode 100644 index 000000000..6d1f1f2b3 --- /dev/null +++ b/database/migrations/2018_07_28_023826_create_checkout_acceptances_table.php @@ -0,0 +1,41 @@ +increments('id'); + + $table->morphs('checkoutable'); + $table->integer('assigned_to_id')->unsigned(); + + $table->string('signature_filename'); + + $table->timestamp('accepted_at')->nullable(); + $table->timestamp('declined_at')->nullable(); + + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('checkout_acceptances'); + } +} diff --git a/resources/views/account/accept.blade.php b/resources/views/account/accept/create.blade.php similarity index 96% rename from resources/views/account/accept.blade.php rename to resources/views/account/accept/create.blade.php index 9073f8516..67ca326e1 100644 --- a/resources/views/account/accept.blade.php +++ b/resources/views/account/accept/create.blade.php @@ -2,7 +2,7 @@ {{-- Page title --}} @section('title') - Accept {{ $item->present()->name() }} + Accept {{ $acceptance->checkoutable->present()->name() }} @parent @stop @@ -52,10 +52,10 @@ - @if ($item->getEula()) + @if ($acceptance->checkoutable->getEula())
- {!! $item->getEula() !!} + {!! $acceptance->checkoutable->getEula() !!}
@endif diff --git a/resources/views/account/accept/index.blade.php b/resources/views/account/accept/index.blade.php new file mode 100755 index 000000000..4a699ea4f --- /dev/null +++ b/resources/views/account/accept/index.blade.php @@ -0,0 +1,61 @@ +@extends('layouts/default') + +{{-- Page title --}} +@section('title') +Accept assets {{ $user->present()->fullName() }} +@parent +@stop + +{{-- Account page content --}} +@section('content') +
+
+
+ +
+ + +
+ + + + + + + + + @foreach ($acceptances as $acceptance) + + + + + @endforeach + +
NameActions
{{ $acceptance->checkoutable->present()->name }}Accept/Decline
+
+ +
+
+
+
+ +@stop + +@section('moar_scripts') + @include ('partials.bootstrap-table') +@stop diff --git a/resources/views/layouts/default.blade.php b/resources/views/layouts/default.blade.php index 196b085bb..874a1eef0 100644 --- a/resources/views/layouts/default.blade.php +++ b/resources/views/layouts/default.blade.php @@ -317,7 +317,11 @@ Requested Assets - +
  • + + + Accept Assets +
  • diff --git a/routes/web.php b/routes/web.php index 4b6d13a7c..eae920934 100644 --- a/routes/web.php +++ b/routes/web.php @@ -278,10 +278,10 @@ Route::group([ 'prefix' => 'account', 'middleware' => ['auth']], function () { Route::get('accept', 'Account\AcceptanceController@index') ->name('account.accept'); - Route::get('accept/{type}/{id}', 'Account\AcceptanceController@edit') + Route::get('accept/{id}', 'Account\AcceptanceController@create') ->name('account.accept.item'); - Route::post('accept/{type}/{id}', 'Account\AcceptanceController@update'); + Route::post('accept/{id}', 'Account\AcceptanceController@store'); }); From 1bdf71b58455d8f2bffbf4c73c3ffcfb6c9980fa Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 13:09:21 +0200 Subject: [PATCH 18/42] Handle side effects of accepting/declining When declining an asset, it gets checked in. --- .../Account/AcceptanceController.php | 12 +---- app/Models/Accessory.php | 3 ++ app/Models/Asset.php | 52 ++++--------------- app/Models/CheckoutAcceptance.php | 22 ++++++++ app/Models/Consumable.php | 3 ++ app/Models/Contracts/Acceptable.php | 11 ---- app/Models/LicenseSeat.php | 3 ++ app/Models/Traits/Acceptable.php | 27 ++++++++++ 8 files changed, 69 insertions(+), 64 deletions(-) delete mode 100644 app/Models/Contracts/Acceptable.php create mode 100644 app/Models/Traits/Acceptable.php diff --git a/app/Http/Controllers/Account/AcceptanceController.php b/app/Http/Controllers/Account/AcceptanceController.php index 5c7235a2a..3e37871aa 100644 --- a/app/Http/Controllers/Account/AcceptanceController.php +++ b/app/Http/Controllers/Account/AcceptanceController.php @@ -85,11 +85,7 @@ class AcceptanceController extends Controller { if ($request->input('asset_acceptance') == 'accepted') { - $acceptance->accepted_at = now(); - $acceptance->signature_filename = $sig_filename; - $acceptance->save(); - - // TODO: Update state for the checkoutable + $acceptance->accept($sig_filename); event(new CheckoutAccepted($acceptance)); @@ -97,11 +93,7 @@ class AcceptanceController extends Controller { } else { - $acceptance->declined_at = now(); - $acceptance->signature_filename = $sig_filename; - $acceptance->save(); - - // TODO: Update state for the checkoutable + $acceptance->decline($sig_filename); event(new CheckoutDeclined($acceptance)); diff --git a/app/Models/Accessory.php b/app/Models/Accessory.php index cdba5f320..76b50704c 100755 --- a/app/Models/Accessory.php +++ b/app/Models/Accessory.php @@ -1,6 +1,7 @@ accepted = 'accepted'; - $this->save(); - } - - /** - * Decline the asset - * - * @param User $declinedBy The user who declines the asset - * @param string $signature The filename of the signature, if provided - */ - public function decline(User $declinedBy, $signature = null) { + * @param User $acceptedBy + * @param string $signature + */ + public function declinedCheckout(User $declinedBy, $signature) { $this->assigned_to = null; $this->assigned_type = null; $this->accepted = null; - $this->save(); - } - - /** - * Is the asset already accepted? - * - * @return boolean - */ - public function isAccepted() : bool { - return $this->accepted != 'pending'; - } - - /** - * Is the asset checked out to this user? - * - * @param User $user - * @return boolean - */ - public function isCheckedOutTo(User $user) { - if (is_null($this->assignedTo)) { - return false; - } - - return $this->assignedTo->is($user); + $this->save(); } diff --git a/app/Models/CheckoutAcceptance.php b/app/Models/CheckoutAcceptance.php index f3d2355e5..28442a0ae 100644 --- a/app/Models/CheckoutAcceptance.php +++ b/app/Models/CheckoutAcceptance.php @@ -40,6 +40,28 @@ class CheckoutAcceptance extends Model return $this->assignedTo->is($user); } + public function accept($signature_filename) { + $this->accepted_at = now(); + $this->signature_filename = $signature_filename; + $this->save(); + + /** + * Update state for the checked out item + */ + $this->checkoutable->acceptedCheckout($this->assignedTo, $signature_filename); + } + + public function decline($signature_filename) { + $this->declined_at = now(); + $this->signature_filename = $signature_filename; + $this->save(); + + /** + * Update state for the checked out item + */ + $this->checkoutable->declinedCheckout($this->assignedTo, $signature_filename); + } + public function scopeForUser(Builder $query, User $user) { return $query->where('assigned_to_id', $user->id); } diff --git a/app/Models/Consumable.php b/app/Models/Consumable.php index 1b3a0da15..596106c26 100644 --- a/app/Models/Consumable.php +++ b/app/Models/Consumable.php @@ -1,6 +1,7 @@ Date: Sat, 28 Jul 2018 13:21:11 +0200 Subject: [PATCH 19/42] Updates asset acceptance report to show unaccepted assets --- app/Http/Controllers/ReportsController.php | 16 +++++++++++++++- app/Models/Asset.php | 13 ------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/app/Http/Controllers/ReportsController.php b/app/Http/Controllers/ReportsController.php index 05e0a0f13..79d41f7b9 100644 --- a/app/Http/Controllers/ReportsController.php +++ b/app/Http/Controllers/ReportsController.php @@ -7,6 +7,7 @@ use App\Models\Actionlog; use App\Models\Asset; use App\Models\AssetMaintenance; use App\Models\CustomField; +use App\Models\CheckoutAcceptance; use App\Models\Depreciation; use App\Models\License; use App\Models\Setting; @@ -805,7 +806,20 @@ class ReportsController extends Controller public function getAssetAcceptanceReport() { $this->authorize('reports.view'); - $assetsForReport = Asset::notYetAccepted()->with('company')->get(); + + /** + * Get all assets with pending checkout acceptances + */ + + $acceptances = CheckoutAcceptance::pending()->get(); + + $assetsForReport = $acceptances + ->filter(function($acceptance) { + return $acceptance->checkoutable_type == 'App\Models\Asset'; + }) + ->map(function($acceptance) { + return $acceptance->checkoutable; + }); return view('reports/unaccepted_assets', compact('assetsForReport')); } diff --git a/app/Models/Asset.php b/app/Models/Asset.php index ec38931b4..35206b41e 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -34,7 +34,6 @@ class Asset extends Depreciable const ASSET = 'asset'; const USER = 'user'; - const ACCEPTANCE_PENDING = 'pending'; use Acceptable; /** @@ -263,18 +262,6 @@ class Asset extends Depreciable $this->location_id = $target->id; } } - - /** - * Does the user have to confirm that they accept the asset? - * - * If so, set the acceptance-status to "pending". - * This value is used in the unaccepted assets reports, for example - * - * @see https://github.com/snipe/snipe-it/issues/5772 - */ - if ($this->requireAcceptance() && $target instanceof User) { - $this->accepted = self::ACCEPTANCE_PENDING; - } if ($this->save()) { From 62195a805af48be810957f40d6cb0bc27e27b962 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 13:32:29 +0200 Subject: [PATCH 20/42] Adding some comments --- .../Account/AcceptanceController.php | 23 +++++++++- app/Models/CheckoutAcceptance.php | 42 +++++++++++++++++++ app/Models/Traits/Acceptable.php | 6 ++- 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Account/AcceptanceController.php b/app/Http/Controllers/Account/AcceptanceController.php index 3e37871aa..198e11e99 100644 --- a/app/Http/Controllers/Account/AcceptanceController.php +++ b/app/Http/Controllers/Account/AcceptanceController.php @@ -18,12 +18,24 @@ use Illuminate\Support\Str; class AcceptanceController extends Controller { - public function index(Request $request) { + /** + * Show a listing of pending checkout acceptances for the current user + * + * @return View + */ + public function index() { $acceptances = CheckoutAcceptance::forUser(Auth::user())->pending()->get(); return view('account/accept.index', compact('acceptances')); } - public function create(Request $request, $id) { + + /** + * Shows a form to either accept or decline the checkout acceptance + * + * @param int $id + * @return mixed + */ + public function create($id) { $acceptance = CheckoutAcceptance::find($id); @@ -46,6 +58,13 @@ class AcceptanceController extends Controller { return view('account/accept.create', compact('acceptance')); } + /** + * Stores the accept/decline of the checkout acceptance + * + * @param Request $request + * @param int $id + * @return Redirect + */ public function store(Request $request, $id) { $acceptance = CheckoutAcceptance::find($id); diff --git a/app/Models/CheckoutAcceptance.php b/app/Models/CheckoutAcceptance.php index 28442a0ae..6ff850f41 100644 --- a/app/Models/CheckoutAcceptance.php +++ b/app/Models/CheckoutAcceptance.php @@ -24,22 +24,48 @@ class CheckoutAcceptance extends Model 'deleted_at' ]; + /** + * The resource that was is out + * + * @return Illuminate\Database\Eloquent\Relations\MorphTo + */ public function checkoutable() { return $this->morphTo(); } + /** + * The user that the checkoutable was checked out to + * + * @return Illuminate\Database\Eloquent\Relations\BelongsTo + */ public function assignedTo() { return $this->belongsTo(User::class); } + /** + * Is this checkout acceptance pending? + * + * @return boolean + */ public function isPending() { return $this->accepted_at == null && $this->declined_at == null; } + /** + * Was the checkoutable checked out to this user? + * + * @param User $user + * @return boolean + */ public function isCheckedOutTo(User $user) { return $this->assignedTo->is($user); } + /** + * Accept the checkout acceptance + * + * @param string $signature_filename + */ public function accept($signature_filename) { $this->accepted_at = now(); $this->signature_filename = $signature_filename; @@ -51,6 +77,11 @@ class CheckoutAcceptance extends Model $this->checkoutable->acceptedCheckout($this->assignedTo, $signature_filename); } + /** + * Decline the checkout acceptance + * + * @param string $signature_filename + */ public function decline($signature_filename) { $this->declined_at = now(); $this->signature_filename = $signature_filename; @@ -62,10 +93,21 @@ class CheckoutAcceptance extends Model $this->checkoutable->declinedCheckout($this->assignedTo, $signature_filename); } + /** + * Filter checkout acceptences by the user + * @param Illuminate\Database\Eloquent\Builder $query + * @param User $user + * @return Illuminate\Database\Eloquent\Builder + */ public function scopeForUser(Builder $query, User $user) { return $query->where('assigned_to_id', $user->id); } + /** + * Filter to only get pending acceptances + * @param Illuminate\Database\Eloquent\Builder $query + * @return Illuminate\Database\Eloquent\Builder + */ public function scopePending(Builder $query) { return $query->whereNull('accepted_at')->whereNull('declined_at'); } diff --git a/app/Models/Traits/Acceptable.php b/app/Models/Traits/Acceptable.php index 0837b9a9a..a0c93a861 100644 --- a/app/Models/Traits/Acceptable.php +++ b/app/Models/Traits/Acceptable.php @@ -7,7 +7,11 @@ use App\Models\CustomField; use App\Models\User; use Illuminate\Database\Eloquent\Builder; - +/** + * This trait allows models to have a callback after their checkout gets accepted or declined. + * + * @author Till Deeke + */ trait Acceptable { /** * Run after the checkout acceptance was accepted by the user From b903ca05f7e729b76fb1e7ab570ecd1c93863c03 Mon Sep 17 00:00:00 2001 From: snipe Date: Fri, 7 Sep 2018 17:27:19 -0700 Subject: [PATCH 21/42] Added manager ID to fillable fields in Location model This should enable the API to update manager ID --- app/Models/Location.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Models/Location.php b/app/Models/Location.php index a6e2007cd..8ae167b54 100755 --- a/app/Models/Location.php +++ b/app/Models/Location.php @@ -56,6 +56,7 @@ class Location extends SnipeModel 'country', 'zip', 'ldap_ou', + 'manager_id', 'currency', 'image', ]; From 10d19be66c320f03363c7715167e5b2feed5416b Mon Sep 17 00:00:00 2001 From: snipe Date: Fri, 7 Sep 2018 17:27:31 -0700 Subject: [PATCH 22/42] Added manager ID to validation --- app/Models/Location.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Models/Location.php b/app/Models/Location.php index 8ae167b54..3dec481dd 100755 --- a/app/Models/Location.php +++ b/app/Models/Location.php @@ -20,13 +20,13 @@ class Location extends SnipeModel protected $dates = ['deleted_at']; protected $table = 'locations'; protected $rules = array( - 'name' => 'required|min:2|max:255|unique_undeleted', - 'city' => 'min:2|max:255|nullable', - 'country' => 'min:2|max:2|nullable', + 'name' => 'required|min:2|max:255|unique_undeleted', + 'city' => 'min:2|max:255|nullable', + 'country' => 'min:2|max:2|nullable', 'address' => 'max:80|nullable', 'address2' => 'max:80|nullable', - 'zip' => 'min:3|max:10|nullable', - // 'manager_id' => 'exists:users' + 'zip' => 'min:3|max:10|nullable', + 'manager_id' => 'exists:users,id|nullable' ); /** From e45b4efa1a5b9a14d599991a839a18f2a73d754c Mon Sep 17 00:00:00 2001 From: snipe Date: Fri, 7 Sep 2018 18:08:18 -0700 Subject: [PATCH 23/42] Better info buttons on audit TODO: translated strings instead --- resources/views/hardware/audit.blade.php | 6 ++++-- resources/views/hardware/quickscan.blade.php | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/resources/views/hardware/audit.blade.php b/resources/views/hardware/audit.blade.php index 5a7289d4b..7b3de62d6 100644 --- a/resources/views/hardware/audit.blade.php +++ b/resources/views/hardware/audit.blade.php @@ -59,8 +59,10 @@ - sdd -

    Checking this box will edit the asset record to reflect this new location. Leaving it unchecked will simply note the location in the audit log.

    + + + + diff --git a/resources/views/hardware/quickscan.blade.php b/resources/views/hardware/quickscan.blade.php index b3df1139d..620b4aad0 100644 --- a/resources/views/hardware/quickscan.blade.php +++ b/resources/views/hardware/quickscan.blade.php @@ -51,7 +51,7 @@
    +
    From 007e8fbdf988e0722103a3ac47ea78928d3a628f Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Mon, 10 Sep 2018 16:40:26 +0200 Subject: [PATCH 24/42] =?UTF-8?q?simplified=20checkout=20event=20handling?= =?UTF-8?q?=20per=20@uberbrady=E2=80=99s=20suggestion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This generalizes the checkout events into the CheckoutableCheckedOut and CheckoutableCheckedIn events. --- app/Events/AccessoryCheckedOut.php | 32 --- app/Events/AssetCheckedIn.php | 33 ---- app/Events/AssetCheckedOut.php | 32 --- ...heckedIn.php => CheckoutableCheckedIn.php} | 13 +- ...ckedOut.php => CheckoutableCheckedOut.php} | 13 +- app/Events/ComponentCheckedIn.php | 35 ---- app/Events/ConsumableCheckedOut.php | 32 --- app/Events/LicenseSeatCheckedIn.php | 34 ---- app/Events/LicenseSeatCheckedOut.php | 33 ---- .../AccessoryCheckinController.php | 4 +- .../AccessoryCheckoutController.php | 4 +- .../Assets/AssetCheckinController.php | 4 +- .../Components/ComponentCheckinController.php | 5 +- .../ComponentCheckoutController.php | 3 +- .../ConsumableCheckoutController.php | 4 +- .../Licenses/LicenseCheckinController.php | 4 +- .../Licenses/LicenseCheckoutController.php | 9 +- app/Listeners/CheckoutableListener.php | 184 ++++++++++++++++++ app/Listeners/LogListener.php | 59 +----- .../SendingCheckInNotificationsListener.php | 117 ----------- .../SendingCheckOutNotificationsListener.php | 182 ----------------- app/Models/Asset.php | 3 +- app/Providers/EventServiceProvider.php | 8 +- 23 files changed, 229 insertions(+), 618 deletions(-) delete mode 100644 app/Events/AccessoryCheckedOut.php delete mode 100644 app/Events/AssetCheckedIn.php delete mode 100644 app/Events/AssetCheckedOut.php rename app/Events/{AccessoryCheckedIn.php => CheckoutableCheckedIn.php} (62%) rename app/Events/{ComponentCheckedOut.php => CheckoutableCheckedOut.php} (59%) delete mode 100644 app/Events/ComponentCheckedIn.php delete mode 100644 app/Events/ConsumableCheckedOut.php delete mode 100644 app/Events/LicenseSeatCheckedIn.php delete mode 100644 app/Events/LicenseSeatCheckedOut.php create mode 100644 app/Listeners/CheckoutableListener.php delete mode 100644 app/Listeners/SendingCheckInNotificationsListener.php delete mode 100644 app/Listeners/SendingCheckOutNotificationsListener.php diff --git a/app/Events/AccessoryCheckedOut.php b/app/Events/AccessoryCheckedOut.php deleted file mode 100644 index faf30adb3..000000000 --- a/app/Events/AccessoryCheckedOut.php +++ /dev/null @@ -1,32 +0,0 @@ -accessory = $accessory; - $this->checkedOutTo = $checkedOutTo; - $this->checkedOutBy = $checkedOutBy; - $this->note = $note; - } -} diff --git a/app/Events/AssetCheckedIn.php b/app/Events/AssetCheckedIn.php deleted file mode 100644 index 455b2b5d4..000000000 --- a/app/Events/AssetCheckedIn.php +++ /dev/null @@ -1,33 +0,0 @@ -asset = $asset; - $this->checkedOutTo = $checkedOutTo; - $this->checkedInBy = $checkedInBy; - $this->note = $note; - } -} diff --git a/app/Events/AssetCheckedOut.php b/app/Events/AssetCheckedOut.php deleted file mode 100644 index 1cd0669ad..000000000 --- a/app/Events/AssetCheckedOut.php +++ /dev/null @@ -1,32 +0,0 @@ -asset = $asset; - $this->checkedOutTo = $checkedOutTo; - $this->checkedOutBy = $checkedOutBy; - $this->note = $note; - } -} diff --git a/app/Events/AccessoryCheckedIn.php b/app/Events/CheckoutableCheckedIn.php similarity index 62% rename from app/Events/AccessoryCheckedIn.php rename to app/Events/CheckoutableCheckedIn.php index 812e072b2..0f6a96491 100644 --- a/app/Events/AccessoryCheckedIn.php +++ b/app/Events/CheckoutableCheckedIn.php @@ -2,30 +2,27 @@ namespace App\Events; -use App\Models\Accessory; -use App\Models\Actionlog; use App\Models\User; -use Illuminate\Broadcasting\Channel; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; -class AccessoryCheckedIn +class CheckoutableCheckedIn { use Dispatchable, SerializesModels; - public $accessory; + public $checkoutable; public $checkedOutTo; public $checkedInBy; public $note; - + /** * Create a new event instance. * * @return void */ - public function __construct(Accessory $accessory, $checkedOutTo, User $checkedInBy, $note) + public function __construct($checkoutable, $checkedOutTo, User $checkedInBy, $note) { - $this->accessory = $accessory; + $this->checkoutable = $checkoutable; $this->checkedOutTo = $checkedOutTo; $this->checkedInBy = $checkedInBy; $this->note = $note; diff --git a/app/Events/ComponentCheckedOut.php b/app/Events/CheckoutableCheckedOut.php similarity index 59% rename from app/Events/ComponentCheckedOut.php rename to app/Events/CheckoutableCheckedOut.php index f74e7d70f..5e6ffd243 100644 --- a/app/Events/ComponentCheckedOut.php +++ b/app/Events/CheckoutableCheckedOut.php @@ -2,30 +2,29 @@ namespace App\Events; -use App\Models\Component; use App\Models\User; -use Illuminate\Broadcasting\Channel; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; -class ComponentCheckedOut +class CheckoutableCheckedOut { use Dispatchable, SerializesModels; - public $component; + public $checkoutable; public $checkedOutTo; public $checkedOutBy; + public $note; /** * Create a new event instance. * * @return void */ - public function __construct(Component $component, $checkedOutTo, $quantity, User $checkedOutBy) + public function __construct($checkoutable, $checkedOutTo, User $checkedOutBy, $note) { - $this->component = $component; + $this->checkoutable = $checkoutable; $this->checkedOutTo = $checkedOutTo; - $this->quantity = $quantity; $this->checkedOutBy = $checkedOutBy; + $this->note = $note; } } diff --git a/app/Events/ComponentCheckedIn.php b/app/Events/ComponentCheckedIn.php deleted file mode 100644 index 283ea6e35..000000000 --- a/app/Events/ComponentCheckedIn.php +++ /dev/null @@ -1,35 +0,0 @@ -component = $component; - $this->checkedOutTo = $checkedOutTo; - $this->checkedInBy = $checkedInBy; - $this->quantity = $quantity; - $this->note = $note; - } -} diff --git a/app/Events/ConsumableCheckedOut.php b/app/Events/ConsumableCheckedOut.php deleted file mode 100644 index ca002cc2f..000000000 --- a/app/Events/ConsumableCheckedOut.php +++ /dev/null @@ -1,32 +0,0 @@ -consumable = $consumable; - $this->checkedOutTo = $checkedOutTo; - $this->checkedOutBy = $checkedOutBy; - $this->note = $note; - } -} diff --git a/app/Events/LicenseSeatCheckedIn.php b/app/Events/LicenseSeatCheckedIn.php deleted file mode 100644 index 80e7ecbaf..000000000 --- a/app/Events/LicenseSeatCheckedIn.php +++ /dev/null @@ -1,34 +0,0 @@ -licenseSeat = $licenseSeat; - $this->checkedOutTo = $checkedOutTo; - $this->checkedInBy = $checkedInBy; - $this->note = $note; - } -} diff --git a/app/Events/LicenseSeatCheckedOut.php b/app/Events/LicenseSeatCheckedOut.php deleted file mode 100644 index 6b057be43..000000000 --- a/app/Events/LicenseSeatCheckedOut.php +++ /dev/null @@ -1,33 +0,0 @@ -licenseSeat = $licenseSeat; - $this->checkedOutTo = $checkedOutTo; - $this->checkedOutBy = $checkedOutBy; - $this->note = $note; - } -} diff --git a/app/Http/Controllers/Accessories/AccessoryCheckinController.php b/app/Http/Controllers/Accessories/AccessoryCheckinController.php index e64d24f16..930e9b661 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckinController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckinController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers\Accessories; -use App\Events\AccessoryCheckedIn; +use App\Events\CheckoutableCheckedIn; use App\Http\Controllers\Controller; use App\Models\Accessory; use App\Models\User; @@ -64,7 +64,7 @@ class AccessoryCheckinController extends Controller if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) { $return_to = e($accessory_user->assigned_to); - event(new AccessoryCheckedIn($accessory, User::find($return_to), Auth::user(), $request->input('note'))); + event(new CheckoutableCheckedIn($accessory, User::find($return_to), Auth::user(), $request->input('note'))); return redirect()->route("accessories.show", $accessory->id)->with('success', trans('admin/accessories/message.checkin.success')); } diff --git a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php index 13bc9e8fd..5ec8a8704 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers\Accessories; -use App\Events\AccessoryCheckedOut; +use App\Events\CheckoutableCheckedOut; use App\Http\Controllers\Controller; use App\Models\Accessory; use App\Models\User; @@ -80,7 +80,7 @@ class AccessoryCheckoutController extends Controller DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first(); - event(new AccessoryCheckedOut($accessory, $user, Auth::user(), $request->input('note'))); + event(new CheckoutableCheckedOut($accessory, $user, Auth::user(), $request->input('note'))); // Redirect to the new accessory page return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.checkout.success')); diff --git a/app/Http/Controllers/Assets/AssetCheckinController.php b/app/Http/Controllers/Assets/AssetCheckinController.php index da141f8b3..e87a1df23 100644 --- a/app/Http/Controllers/Assets/AssetCheckinController.php +++ b/app/Http/Controllers/Assets/AssetCheckinController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers\Assets; -use App\Events\AssetCheckedIn; +use App\Events\CheckoutableCheckedIn; use App\Helpers\Helper; use App\Http\Controllers\Controller; use App\Http\Requests\AssetCheckinRequest; @@ -85,7 +85,7 @@ class AssetCheckinController extends Controller // Was the asset updated? if ($asset->save()) { - event(new AssetCheckedIn($asset, $target, Auth::user(), $request->input('note'))); + event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note'))); if ($backto=='user') { return redirect()->route("users.show", $user->id)->with('success', trans('admin/hardware/message.checkin.success')); diff --git a/app/Http/Controllers/Components/ComponentCheckinController.php b/app/Http/Controllers/Components/ComponentCheckinController.php index fd91daa84..dfba1dcf8 100644 --- a/app/Http/Controllers/Components/ComponentCheckinController.php +++ b/app/Http/Controllers/Components/ComponentCheckinController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Components; +use App\Events\CheckoutableCheckedIn; use App\Events\ComponentCheckedIn; use App\Http\Controllers\Controller; use App\Models\Actionlog; @@ -94,7 +95,9 @@ class ComponentCheckinController extends Controller DB::table('components_assets')->where('id', '=', $component_asset_id)->delete(); } - event(new ComponentCheckedIn($component, $component_assets, Auth::user(), $request->input('checkin_qty'), $request->input('note'))); + $asset = Asset::find($component_assets->asset_id); + + event(new CheckoutableCheckedIn($component, $asset, Auth::user(), $request->input('note'))); return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success')); diff --git a/app/Http/Controllers/Components/ComponentCheckoutController.php b/app/Http/Controllers/Components/ComponentCheckoutController.php index c7e7fe3db..bfa9e8967 100644 --- a/app/Http/Controllers/Components/ComponentCheckoutController.php +++ b/app/Http/Controllers/Components/ComponentCheckoutController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Components; +use App\Events\CheckoutableCheckedOut; use App\Events\ComponentCheckedOut; use App\Http\Controllers\Controller; use App\Models\Asset; @@ -87,7 +88,7 @@ class ComponentCheckoutController extends Controller 'asset_id' => $asset_id ]); - event(new ComponentCheckedOut($component, $asset, $request->input('assigned_qty'), Auth::user())); + event(new CheckoutableCheckedOut($component, $asset, Auth::user(), $request->input('note'))); return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success')); } diff --git a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php index 0b3d8c6c9..e3e3e11b0 100644 --- a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php +++ b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers\Consumables; -use App\Events\ConsumableCheckedOut; +use App\Events\CheckoutableCheckedOut; use App\Http\Controllers\Controller; use App\Models\Consumable; use App\Models\User; @@ -68,7 +68,7 @@ class ConsumableCheckoutController extends Controller 'assigned_to' => e(Input::get('assigned_to')) ]); - event(new ConsumableCheckedOut($consumable, $user, Auth::user(), $request->input('note'))); + event(new CheckoutableCheckedOut($consumable, $user, Auth::user(), $request->input('note'))); // Redirect to the new consumable page return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.checkout.success')); diff --git a/app/Http/Controllers/Licenses/LicenseCheckinController.php b/app/Http/Controllers/Licenses/LicenseCheckinController.php index 961202846..4bddbd575 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckinController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckinController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers\Licenses; -use App\Events\LicenseCheckedIn; +use App\Events\CheckoutableCheckedIn; use App\Models\Asset; use App\Models\License; use App\Models\LicenseSeat; @@ -91,7 +91,7 @@ class LicenseCheckinController extends Controller // Was the asset updated? if ($licenseSeat->save()) { - event(new LicenseCheckedIn($license, $return_to, Auth::user(), $request->input('note'))); + event(new CheckoutableCheckedIn($license, $return_to, Auth::user(), $request->input('note'))); if ($backTo=='user') { return redirect()->route("users.show", $return_to->id)->with('success', trans('admin/licenses/message.checkin.success')); diff --git a/app/Http/Controllers/Licenses/LicenseCheckoutController.php b/app/Http/Controllers/Licenses/LicenseCheckoutController.php index a1a02c823..5d878a150 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckoutController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckoutController.php @@ -2,17 +2,16 @@ namespace App\Http\Controllers\Licenses; -use App\Events\LicenseCheckedOut; -use App\Events\LicenseSeatCheckedOut; +use App\Events\CheckoutableCheckedOut; use App\Http\Requests\LicenseCheckoutRequest; use App\Models\Asset; use App\Models\License; use App\Models\LicenseSeat; use App\Models\User; use Illuminate\Http\Request; -use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Input; +use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Validator; class LicenseCheckoutController extends Controller @@ -106,7 +105,7 @@ class LicenseCheckoutController extends Controller } if ($licenseSeat->save()) { - event(new LicenseSeatCheckedOut($licenseSeat, $target, Auth::user(), request('note'))); + event(new CheckoutableCheckedOut($licenseSeat, $target, Auth::user(), request('note'))); return true; } @@ -123,7 +122,7 @@ class LicenseCheckoutController extends Controller if ($licenseSeat->save()) { - event(new LicenseSeatCheckedOut($licenseSeat, $target, Auth::user(), request('note'))); + event(new CheckoutableCheckedOut($licenseSeat, $target, Auth::user(), request('note'))); return true; } diff --git a/app/Listeners/CheckoutableListener.php b/app/Listeners/CheckoutableListener.php new file mode 100644 index 000000000..e06c6e1e4 --- /dev/null +++ b/app/Listeners/CheckoutableListener.php @@ -0,0 +1,184 @@ +checkedOutTo instanceof User) { + return; + } + + /** + * Make a checkout acceptance and attach it in the notification + */ + $acceptance = $this->getCheckoutAcceptance($event); + + Notification::send( + $this->getNotifiables($event), + $this->getCheckoutNotification($event, $acceptance) + ); + } + + /** + * Notify the user about the checked in checkoutable + */ + public function onCheckedIn($event) { + /** + * When the item wasn't checked out to a user, we can't send notifications + */ + if(!$event->checkedOutTo instanceof User) { + return; + } + + /** + * Send the appropriate notification + */ + Notification::send( + $this->getNotifiables($event), + $this->getCheckinNotification($event) + ); + } + + /** + * Generates a checkout acceptance + * @param Event $event + * @return mixed + */ + private function getCheckoutAcceptance($event) { + if (!$event->checkoutable->requireAcceptance()) { + return null; + } + + $acceptance = new CheckoutAcceptance; + $acceptance->checkoutable()->associate($event->checkoutable); + $acceptance->assignedTo()->associate($event->checkedOutTo); + $acceptance->save(); + + return $acceptance; + } + + /** + * Gets the entities to be notified of the passed event + * + * @param Event $event + * @return Collection + */ + private function getNotifiables($event) { + $notifiables = collect(); + + /** + * Notify the user who checked out the item + */ + $notifiables->push($event->checkedOutTo); + + /** + * Notify Admin users if the settings is activated + */ + if (Setting::getSettings()->admin_cc_email != '') { + $notifiables->push(new AdminRecipient()); + } + + return $notifiables; + } + + /** + * Get the appropriate notification for the event + * + * @param CheckoutableCheckedIn $event + * @return Notification + */ + private function getCheckinNotification($event) { + + $model = get_class($event->checkoutable); + + $notificationClass = null; + + switch (get_class($event->checkoutable)) { + case Accessory::class: + $notificationClass = CheckinAccessoryNotification::class; + break; + case Asset::class: + $notificationClass = CheckinAssetNotification::class; + break; + case LicenseSeat::class: + $notificationClass = CheckinLicenseSeatNotification::class; + break; + } + + return new $notificationClass($event->checkoutable, $event->checkedOutTo, $event->checkedInBy, $event->note); + } + + /** + * Get the appropriate notification for the event + * + * @param CheckoutableCheckedIn $event + * @param CheckoutAcceptance $acceptance + * @return Notification + */ + private function getCheckoutNotification($event, $acceptance) { + $notificationClass = null; + + switch (get_class($event->checkoutable)) { + case Accessory::class: + $notificationClass = CheckoutAccessoryNotification::class; + break; + case Asset::class: + $notificationClass = CheckoutAssetNotification::class; + break; + case Consumable::class: + $notificationClass = CheckoutConsumableNotification::class; + break; + case LicenseSeat::class: + $notificationClass = CheckoutLicenseSeatNotification::class; + break; + } + + return new $notificationClass($event->checkoutable, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note); + } + + /** + * Register the listeners for the subscriber. + * + * @param Illuminate\Events\Dispatcher $events + */ + public function subscribe($events) + { + $events->listen( + 'App\Events\CheckoutableCheckedIn', + 'App\Listeners\CheckoutableListener@onCheckedIn' + ); + + $events->listen( + 'App\Events\CheckoutableCheckedOut', + 'App\Listeners\CheckoutableListener@onCheckedOut' + ); + } + +} \ No newline at end of file diff --git a/app/Listeners/LogListener.php b/app/Listeners/LogListener.php index 9cd8ec271..cdb4fda1f 100644 --- a/app/Listeners/LogListener.php +++ b/app/Listeners/LogListener.php @@ -8,6 +8,8 @@ use App\Events\AssetCheckedIn; use App\Events\AssetCheckedOut; use App\Events\CheckoutAccepted; use App\Events\CheckoutDeclined; +use App\Events\CheckoutableCheckedIn; +use App\Events\CheckoutableCheckedOut; use App\Events\ComponentCheckedIn; use App\Events\ComponentCheckedOut; use App\Events\ConsumableCheckedOut; @@ -23,51 +25,15 @@ use App\Models\LicenseSeat; class LogListener { - public function onAccessoryCheckedIn(AccessoryCheckedIn $event) { - $event->accessory->logCheckin($event->checkedOutTo, $event->note); + + public function onCheckoutableCheckedIn(CheckoutableCheckedIn $event) { + $event->checkoutable->logCheckin($event->checkedOutTo, $event->note); } - public function onAccessoryCheckedOut(AccessoryCheckedOut $event) { - $event->accessory->logCheckout($event->note, $event->checkedOutTo); + public function onCheckoutableCheckedOut(CheckoutableCheckedOut $event) { + $event->checkoutable->logCheckout($event->note, $event->checkedOutTo); } - public function onAssetCheckedIn(AssetCheckedIn $event) { - $event->asset->logCheckin($event->checkedOutTo, $event->note); - } - - public function onAssetCheckedOut(AssetCheckedOut $event) { - $event->asset->logCheckout($event->note, $event->checkedOutTo); - } - - public function onComponentCheckedIn(ComponentCheckedIn $event) { - $log = new Actionlog(); - $log->user_id = $event->checkedInBy->id; - $log->action_type = 'checkin from'; - $log->target_type = Asset::class; - $log->target_id = $event->checkedOutTo->asset_id; - $log->item_id = $event->checkedOutTo->component_id; - $log->item_type = Component::class; - $log->note = $event->note; - $log->save(); - } - - public function onComponentCheckedOut(ComponentCheckedOut $event) { - // Since components don't have a "note" field, submit empty note - $event->component->logCheckout(null, $event->checkedOutTo); - } - - public function onConsumableCheckedOut(ConsumableCheckedOut $event) { - $event->consumable->logCheckout($event->note, $event->checkedOutTo); - } - - public function onLicenseCheckedIn(LicenseCheckedIn $event) { - $event->license->logCheckin($event->checkedOutTo, $event->note); - } - - public function onLicenseCheckedOut(LicenseCheckedOut $event) { - $event->license->logCheckout($event->note, $event->checkedOutTo); - } - public function onCheckoutAccepted(CheckoutAccepted $event) { $logaction = new Actionlog(); @@ -107,15 +73,8 @@ class LogListener public function subscribe($events) { $list = [ - 'AccessoryCheckedIn', - 'AccessoryCheckedOut', - 'AssetCheckedIn', - 'AssetCheckedOut', - 'ComponentCheckedIn', - 'ComponentCheckedOut', - 'ConsumableCheckedOut', - 'LicenseCheckedIn', - 'LicenseCheckedOut', + 'CheckoutableCheckedIn', + 'CheckoutableCheckedOut', 'CheckoutAccepted', 'CheckoutDeclined', ]; diff --git a/app/Listeners/SendingCheckInNotificationsListener.php b/app/Listeners/SendingCheckInNotificationsListener.php deleted file mode 100644 index aa176e19f..000000000 --- a/app/Listeners/SendingCheckInNotificationsListener.php +++ /dev/null @@ -1,117 +0,0 @@ -checkedOutTo instanceof User) { - return; - } - - Notification::send( - $this->getNotifiables($event), - new CheckinAccessoryNotification($event->accessory, $event->checkedOutTo, $event->checkedInBy, $event->note) - ); - } - - /** - * Notify the user about the checked in asset - */ - public function onAssetCheckedIn($event) { - /** - * When the item wasn't checked out to a user, we can't send notifications - */ - if(! $event->checkedOutTo instanceof User) { - return; - } - - Notification::send( - $this->getNotifiables($event), - new CheckinAssetNotification($event->asset, $event->checkedOutTo, $event->checkedInBy, $event->note) - ); - } - - /** - * Notify the user about the checked in license - */ - public function onLicenseSeatCheckedIn($event) { - /** - * When the item wasn't checked out to a user, we can't send notifications - */ - if(! $event->checkedOutTo instanceof User) { - return; - } - - Notification::send( - $this->getNotifiables($event), - new CheckinLicenseSeatNotification($event->licenseSeat, $event->checkedOutTo, $event->checkedInBy, $event->note) - ); - } - - /** - * Gets the entities to be notified of the passed event - * - * @param Event $event - * @return Collection - */ - private function getNotifiables($event) { - $notifiables = collect(); - - /** - * Notify the user who checked out the item - */ - $notifiables->push($event->checkedOutTo); - - /** - * Notify Admin users if the settings is activated - */ - if (Setting::getSettings()->admin_cc_email != '') { - $notifiables->push(new AdminRecipient()); - } - - return $notifiables; - } - - /** - * Register the listeners for the subscriber. - * - * @param Illuminate\Events\Dispatcher $events - */ - public function subscribe($events) - { - $events->listen( - 'App\Events\AccessoryCheckedIn', - 'App\Listeners\SendingCheckInNotificationsListener@onAccessoryCheckedIn' - ); - - $events->listen( - 'App\Events\AssetCheckedIn', - 'App\Listeners\SendingCheckInNotificationsListener@onAssetCheckedIn' - ); - - $events->listen( - 'App\Events\LicenseSeatCheckedIn', - 'App\Listeners\SendingCheckInNotificationsListener@onLicenseSeatCheckedIn' - ); - } - -} \ No newline at end of file diff --git a/app/Listeners/SendingCheckOutNotificationsListener.php b/app/Listeners/SendingCheckOutNotificationsListener.php deleted file mode 100644 index ed459c5a5..000000000 --- a/app/Listeners/SendingCheckOutNotificationsListener.php +++ /dev/null @@ -1,182 +0,0 @@ -checkedOutTo instanceof User) { - return; - } - - /** - * Make a checkout acceptance and attach it in the notification - */ - $acceptance = null; - if ($event->consumable->requireAcceptance()) { - $acceptance = new CheckoutAcceptance; - $acceptance->checkoutable()->associate($event->consumable); - $acceptance->assignedTo()->associate($event->checkedOutTo); - $acceptance->save(); - } - - Notification::send( - $this->getNotifiables($event), - new CheckoutConsumableNotification($event->consumable, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note) - ); - } - - /** - * Notify the user about the checked out accessory - */ - public function onAccessoryCheckedOut($event) { - /** - * When the item wasn't checked out to a user, we can't send notifications - */ - if(! $event->checkedOutTo instanceof User) { - return; - } - - /** - * Make a checkout acceptance and attach it in the notification - */ - $acceptance = null; - if ($event->accessory->requireAcceptance()) { - $acceptance = new CheckoutAcceptance; - $acceptance->checkoutable()->associate($event->accessory); - $acceptance->assignedTo()->associate($event->checkedOutTo); - $acceptance->save(); - } - - Notification::send( - $this->getNotifiables($event), - new CheckoutAccessoryNotification($event->accessory, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note) - ); - } - - /** - * Notify the user about the checked out license - */ - public function onLicenseSeatCheckedOut($event) { - /** - * When the item wasn't checked out to a user, we can't send notifications - */ - if(! $event->checkedOutTo instanceof User) { - return; - } - - /** - * Make a checkout acceptance and attach it in the notification - */ - $acceptance = null; - if ($event->licenseSeat->license->requireAcceptance()) { - $acceptance = new CheckoutAcceptance; - $acceptance->checkoutable()->associate($event->licenseSeat); - $acceptance->assignedTo()->associate($event->checkedOutTo); - $acceptance->save(); - } - - Notification::send( - $this->getNotifiables($event), - new CheckoutLicenseSeatNotification($event->licenseSeat, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note) - ); - } - - /** - * Notify the user about the checked out asset - */ - public function onAssetCheckedOut($event) { - /** - * When the item wasn't checked out to a user, we can't send notifications - */ - if(! $event->checkedOutTo instanceof User) { - return; - } - - /** - * Make a checkout acceptance and attach it in the notification - */ - $acceptance = null; - if ($event->asset->requireAcceptance()) { - $acceptance = new CheckoutAcceptance; - $acceptance->checkoutable()->associate($event->asset); - $acceptance->assignedTo()->associate($event->checkedOutTo); - $acceptance->save(); - } - - Notification::send( - $this->getNotifiables($event), - new CheckoutAssetNotification($event->asset, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note) - ); - } - - /** - * Gets the entities to be notified of the passed event - * - * @param Event $event - * @return Collection - */ - private function getNotifiables($event) { - $notifiables = collect(); - - /** - * Notify the user who checked out the item - */ - $notifiables->push($event->checkedOutTo); - - /** - * Notify Admin users if the settings is activated - */ - if (Setting::getSettings()->admin_cc_email != '') { - $notifiables->push(new AdminRecipient()); - } - - return $notifiables; - } - - /** - * Register the listeners for the subscriber. - * - * @param Illuminate\Events\Dispatcher $events - */ - public function subscribe($events) - { - $events->listen( - 'App\Events\ConsumableCheckedOut', - 'App\Listeners\SendingCheckOutNotificationsListener@onConsumableCheckedOut' - ); - - $events->listen( - 'App\Events\AccessoryCheckedOut', - 'App\Listeners\SendingCheckOutNotificationsListener@onAccessoryCheckedOut' - ); - - $events->listen( - 'App\Events\LicenseSeatCheckedOut', - 'App\Listeners\SendingCheckOutNotificationsListener@onLicenseSeatCheckedOut' - ); - - $events->listen( - 'App\Events\AssetCheckedOut', - 'App\Listeners\SendingCheckOutNotificationsListener@onAssetCheckedOut' - ); - } - -} \ No newline at end of file diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 35206b41e..ceb6ee696 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -2,6 +2,7 @@ namespace App\Models; use App\Events\AssetCheckedOut; +use App\Events\CheckoutableCheckedOut; use App\Exceptions\CheckoutNotAllowed; use App\Http\Traits\UniqueSerialTrait; use App\Http\Traits\UniqueUndeletedTrait; @@ -265,7 +266,7 @@ class Asset extends Depreciable if ($this->save()) { - event(new AssetCheckedOut($this, $target, Auth::user(), $note)); + event(new CheckoutableCheckedOut($this, $target, Auth::user(), $note)); $this->increment('checkout_counter', 1); return true; diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index df3c643b2..28c3aecf0 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -3,9 +3,8 @@ namespace App\Providers; use Illuminate\Support\Facades\Event; +use App\Listeners\CheckoutableListener; use App\Listeners\LogListener; -use App\Listeners\SendingCheckInNotificationsListener; -use App\Listeners\SendingCheckOutNotificationsListener; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; class EventServiceProvider extends ServiceProvider @@ -32,9 +31,8 @@ class EventServiceProvider extends ServiceProvider * @var array */ protected $subscribe = [ - SendingCheckOutNotificationsListener::class, - SendingCheckInNotificationsListener::class, - LogListener::class + LogListener::class, + CheckoutableListener::class ]; /** From 86f49d34c3b7b5d7c2425ff5dc48d1c9b8a09987 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Mon, 10 Sep 2018 17:13:16 +0200 Subject: [PATCH 25/42] Redirects users from old acceptance screen to new overview --- app/Http/Controllers/ViewAssetsController.php | 120 +----------------- routes/web.php | 4 - 2 files changed, 1 insertion(+), 123 deletions(-) diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index 2308b804a..8022815ba 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -199,124 +199,6 @@ class ViewAssetsController extends Controller // Get the acceptance screen public function getAcceptAsset($logID = null) { - - $findlog = Actionlog::where('id', $logID)->first(); - - if (!$findlog) { - return redirect()->to('account/view-assets')->with('error', 'No matching record.'); - } - - if ($findlog->accepted_id!='') { - return redirect()->to('account/view-assets')->with('error', trans('admin/users/message.error.asset_already_accepted')); - } - - $user = Auth::user(); - - - // TODO - Fix this for non-assets - if (($findlog->item_type==Asset::class) && ($user->id != $findlog->item->assigned_to)) { - return redirect()->to('account/view-assets')->with('error', trans('admin/users/message.error.incorrect_user_accepted')); - } - - - $item = $findlog->item; - - // Check if the asset exists - if (is_null($item)) { - // Redirect to the asset management page - return redirect()->to('account')->with('error', trans('admin/hardware/message.does_not_exist')); - } - if (!Company::isCurrentUserHasAccess($item)) { - return redirect()->route('requestable-assets')->with('error', trans('general.insufficient_permissions')); - } - return view('account/accept-asset', compact('item'))->with('findlog', $findlog)->with('item', $item); - } - - // Save the acceptance - public function postAcceptAsset(Request $request, $logID = null) - { - - // Check if the asset exists - if (is_null($findlog = Actionlog::where('id', $logID)->first())) { - // Redirect to the asset management page - return redirect()->to('account/view-assets')->with('error', trans('admin/hardware/message.does_not_exist')); - } - - - if ($findlog->accepted_id!='') { - // Redirect to the asset management page - return redirect()->to('account/view-assets')->with('error', trans('admin/users/message.error.asset_already_accepted')); - } - - if (!Input::has('asset_acceptance')) { - return redirect()->back()->with('error', trans('admin/users/message.error.accept_or_decline')); - } - - $user = Auth::user(); - - if (($findlog->item_type==Asset::class) && ($user->id != $findlog->item->assigned_to)) { - return redirect()->to('account/view-assets')->with('error', trans('admin/users/message.error.incorrect_user_accepted')); - } - - if ($request->filled('signature_output')) { - $path = config('app.private_uploads').'/signatures'; - $sig_filename = "siglog-".$findlog->id.'-'.date('Y-m-d-his').".png"; - $data_uri = e($request->get('signature_output')); - $encoded_image = explode(",", $data_uri); - $decoded_image = base64_decode($encoded_image[1]); - file_put_contents($path."/".$sig_filename, $decoded_image); - } - - - $logaction = new Actionlog(); - - if (Input::get('asset_acceptance')=='accepted') { - $logaction_msg = 'accepted'; - $accepted="accepted"; - $return_msg = trans('admin/users/message.accepted'); - } else { - $logaction_msg = 'declined'; - $accepted="rejected"; - $return_msg = trans('admin/users/message.declined'); - } - $logaction->item_id = $findlog->item_id; - $logaction->item_type = $findlog->item_type; - - // Asset - if (($findlog->item_id!='') && ($findlog->item_type==Asset::class)) { - if (Input::get('asset_acceptance')!='accepted') { - DB::table('assets') - ->where('id', $findlog->item_id) - ->update(array('assigned_to' => null)); - } - } - - $logaction->target_id = $findlog->target_id; - $logaction->target_type = User::class; - $logaction->note = e(Input::get('note')); - $logaction->updated_at = date("Y-m-d H:i:s"); - - - if (isset($sig_filename)) { - $logaction->accept_signature = $sig_filename; - } - $log = $logaction->logaction($logaction_msg); - - $update_checkout = DB::table('action_logs') - ->where('id', $findlog->id) - ->update(array('accepted_id' => $logaction->id)); - - if (($findlog->item_id!='') && ($findlog->item_type==Asset::class)) { - $affected_asset = $logaction->item; - $affected_asset->accepted = $accepted; - $affected_asset->save(); - } - - if ($update_checkout) { - return redirect()->to('account/view-assets')->with('success', $return_msg); - - } - return redirect()->to('account/view-assets')->with('error', 'Something went wrong '); - + return redirect()->route('account.accept'); } } diff --git a/routes/web.php b/routes/web.php index eae920934..f59362c76 100644 --- a/routes/web.php +++ b/routes/web.php @@ -251,10 +251,6 @@ Route::group([ 'prefix' => 'account', 'middleware' => ['auth']], function () { 'accept-asset/{logID}', [ 'as' => 'account/accept-assets', 'uses' => 'ViewAssetsController@getAcceptAsset' ] ); - Route::post( - 'accept-asset/{logID}', - [ 'as' => 'account/asset-accepted', 'uses' => 'ViewAssetsController@postAcceptAsset' ] - ); # Profile Route::get( From bbd1d6059a9e9a46410ad9c51fe58ebb209611e8 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Mon, 10 Sep 2018 17:16:07 +0200 Subject: [PATCH 26/42] Fixes errors in tests --- .../2018_07_28_023826_create_checkout_acceptances_table.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/migrations/2018_07_28_023826_create_checkout_acceptances_table.php b/database/migrations/2018_07_28_023826_create_checkout_acceptances_table.php index 6d1f1f2b3..67bc3b800 100644 --- a/database/migrations/2018_07_28_023826_create_checkout_acceptances_table.php +++ b/database/migrations/2018_07_28_023826_create_checkout_acceptances_table.php @@ -19,7 +19,7 @@ class CreateCheckoutAcceptancesTable extends Migration $table->morphs('checkoutable'); $table->integer('assigned_to_id')->unsigned(); - $table->string('signature_filename'); + $table->string('signature_filename')->nullable(); $table->timestamp('accepted_at')->nullable(); $table->timestamp('declined_at')->nullable(); From e445e201f312ce8c379532ec03a241a1d161e87a Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Mon, 10 Sep 2018 17:37:24 +0200 Subject: [PATCH 27/42] Adds a migration to create checkout acceptances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This creates the checkout acceptances for assets (basically what was previously shown in the „Unaccepted Assets“-report) when migrating the database --- ...kout_acceptances_for_unaccepted_assets.php | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 database/migrations/2018_09_10_082212_create_checkout_acceptances_for_unaccepted_assets.php diff --git a/database/migrations/2018_09_10_082212_create_checkout_acceptances_for_unaccepted_assets.php b/database/migrations/2018_09_10_082212_create_checkout_acceptances_for_unaccepted_assets.php new file mode 100644 index 000000000..ddf6949b6 --- /dev/null +++ b/database/migrations/2018_09_10_082212_create_checkout_acceptances_for_unaccepted_assets.php @@ -0,0 +1,43 @@ +where('assigned_type', 'App\Models\User')->where('accepted', 'pending')->get(); + + $acceptances = []; + + foreach($assets as $asset) { + $acceptances[] = [ + 'checkoutable_type' => 'App\Models\Asset', + 'checkoutable_id' => $asset->id, + 'assigned_to_id' => $asset->assigned_to, + ]; + } + + DB::table('checkout_acceptances')->insert($acceptances); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} From 05b03df6001a34a5d25f2a31bae09c945e96f1d3 Mon Sep 17 00:00:00 2001 From: Juho Taipale Date: Thu, 13 Sep 2018 08:49:50 +0300 Subject: [PATCH 28/42] Fix for issue #6165 (#6168) * Fix problem when using ValidatingTrait * Checking that email alerts are enabled when trying to send expected check-in alerts (fix for issue #6169) --- .../Commands/SendExpectedCheckinAlerts.php | 7 +------ app/Models/CustomField.php | 20 +++++++++++++++---- app/Models/CustomFieldset.php | 11 +++++++--- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/app/Console/Commands/SendExpectedCheckinAlerts.php b/app/Console/Commands/SendExpectedCheckinAlerts.php index a78f0e1ae..d0d3ee155 100644 --- a/app/Console/Commands/SendExpectedCheckinAlerts.php +++ b/app/Console/Commands/SendExpectedCheckinAlerts.php @@ -57,17 +57,12 @@ class SendExpectedCheckinAlerts extends Command } } - // Send a rollup to the admin, if settings dictate $recipient = new \App\Models\Recipients\AlertRecipient(); - if (($assets) && ($assets->count() > 0) && ($settings->alert_email!='')) { + if (($assets) && ($assets->count() > 0) && ($settings->alerts_enabled && $settings->alert_email != '')) { $recipient->notify(new ExpectedCheckinAdminNotification($assets)); } - - - - } } diff --git a/app/Models/CustomField.php b/app/Models/CustomField.php index 390c6500b..0d2565db1 100644 --- a/app/Models/CustomField.php +++ b/app/Models/CustomField.php @@ -12,9 +12,14 @@ use Illuminate\Validation\Rule; class CustomField extends Model { - use ValidatingTrait, UniqueUndeletedTrait; - public $guarded=["id"]; - public static $PredefinedFormats=[ + use ValidatingTrait, + UniqueUndeletedTrait; + + public $guarded = [ + "id" + ]; + + public static $PredefinedFormats = [ "ANY" => "", "CUSTOM REGEX" => "", "ALPHA" => "alpha", @@ -31,6 +36,14 @@ class CustomField extends Model "BOOLEAN" => "boolean", ]; + /** + * Validation rules. + * At least empty array must be provided if using ValidatingTrait. + * + * @var array + */ + protected $rules = []; + /** * The attributes that are mass assignable. * @@ -57,7 +70,6 @@ class CustomField extends Model */ public static $table_name = "assets"; - /** * Convert the custom field's name property to a db-safe string. * diff --git a/app/Models/CustomFieldset.php b/app/Models/CustomFieldset.php index e25248f2c..9b83051cb 100644 --- a/app/Models/CustomFieldset.php +++ b/app/Models/CustomFieldset.php @@ -7,10 +7,16 @@ use Watson\Validating\ValidatingTrait; class CustomFieldset extends Model { + use ValidatingTrait; + protected $guarded=["id"]; - public $rules=[ - "name" => "required|unique:custom_fieldsets" + /** + * Validation rules + * @var array + */ + protected $rules = [ + "name" => "required|unique:custom_fieldsets" ]; /** @@ -21,7 +27,6 @@ class CustomFieldset extends Model * @var boolean */ protected $injectUniqueIdentifier = true; - use ValidatingTrait; /** From b692f67779f1b24fda5f4e748425254b9a2d80c8 Mon Sep 17 00:00:00 2001 From: Ivan Nieto Date: Thu, 13 Sep 2018 00:50:45 -0500 Subject: [PATCH 29/42] Revision of #5471 (#6148) * Search functionality in accessories/{accessory} issue #5471: From the collection of users displayed just filtered the data with the method where() and concat() for the user can search for first name or last name. The solution is case sensitive. * A better fix to issue #5471. Now using the established relationship to querying for the users. Also Case-insensitive. * Fixed previous commit that has magic number in the find method parameter of AccessoriesController. --- app/Http/Controllers/Api/AccessoriesController.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Api/AccessoriesController.php b/app/Http/Controllers/Api/AccessoriesController.php index 1669bd318..4e4793dcf 100644 --- a/app/Http/Controllers/Api/AccessoriesController.php +++ b/app/Http/Controllers/Api/AccessoriesController.php @@ -143,13 +143,16 @@ class AccessoriesController extends Controller $accessory->lastCheckoutArray = $accessory->lastCheckout->toArray(); $accessory_users = $accessory->users; - - if($request->filled('search')){ - $accessory_users = $accessory_users->where('first_name', $request->input('search'))->concat($accessory_users->where('last_name', $request->input('search'))); + + if ($request->filled('search')) { + $accessory_users = $accessory->users() + ->where('first_name', 'like', '%'.$request->input('search').'%') + ->orWhere('last_name', 'like', '%'.$request->input('search').'%') + ->get(); } $total = $accessory_users->count(); - + return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory, $accessory_users, $total); } From d84366c6c50cd296a273a9a4d634942ee15e9b18 Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 19 Sep 2018 17:24:29 -0700 Subject: [PATCH 30/42] Add empty array to groups if none filled in --- app/Http/Controllers/Api/UsersController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index dca2e9e4f..7fca6bf3a 100644 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -203,6 +203,8 @@ class UsersController extends Controller if ($user->save()) { if ($request->filled('groups')) { $user->groups()->sync($request->input('groups')); + } else { + $user->groups()->sync(array()); } return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.create'))); } From 24aaa36532d7dd998408310361fb492020a330cb Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 19 Sep 2018 17:27:34 -0700 Subject: [PATCH 31/42] Applied 3159e7713af3760d1774e5a037487a5f7d68ebe8 to develop --- docker/entrypoint.sh | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 0486302c9..2a1e48940 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -17,8 +17,24 @@ else fi # create data directories -for dir in 'data/private_uploads' 'data/uploads' 'data/uploads/avatars' 'data/uploads/barcodes' 'data/uploads/categories' 'data/uploads/companies' 'data/uploads/departments' 'data/uploads/locations' 'data/uploads/manufacturers' 'data/uploads/models' 'data/uploads/suppliers' 'dumps' 'keys'; do - mkdir -p "/var/lib/snipeit/$dir" +for dir in \ + 'data/private_uploads' \ + 'data/uploads/accessories' \ + 'data/uploads/avatars' \ + 'data/uploads/barcodes' \ + 'data/uploads/categories' \ + 'data/uploads/companies' \ + 'data/uploads/components' \ + 'data/uploads/consumables' \ + 'data/uploads/departments' \ + 'data/uploads/locations' \ + 'data/uploads/manufacturers' \ + 'data/uploads/models' \ + 'data/uploads/suppliers' \ + 'dumps' \ + 'keys' +do + [ ! -d "/var/lib/snipeit/$dir" ] && mkdir -p "/var/lib/snipeit/$dir" done chown -R docker:root /var/lib/snipeit/data/* @@ -26,9 +42,10 @@ chown -R docker:root /var/lib/snipeit/dumps chown -R docker:root /var/lib/snipeit/keys # If the Oauth DB files are not present copy the vendor files over to the db migrations -if [ ! -f "/var/www/html/database/migrations/*create_oauth*" ]; then +if [ ! -f "/var/www/html/database/migrations/*create_oauth*" ] +then cp -ax /var/www/html/vendor/laravel/passport/database/migrations/* /var/www/html/database/migrations/ fi -. /etc/apache2/envvars +. /etc/apache2/envvars exec apache2 -DNO_DETACH < /dev/null From 4b2093b485fb55b93186b339b82fef2f022c27d8 Mon Sep 17 00:00:00 2001 From: snipe Date: Fri, 21 Sep 2018 15:51:26 -0700 Subject: [PATCH 32/42] Added counts to location show() API method --- .../Controllers/Api/LocationsController.php | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/Api/LocationsController.php b/app/Http/Controllers/Api/LocationsController.php index 2ec5ecc3a..452b5b73b 100644 --- a/app/Http/Controllers/Api/LocationsController.php +++ b/app/Http/Controllers/Api/LocationsController.php @@ -106,7 +106,26 @@ class LocationsController extends Controller public function show($id) { $this->authorize('view', Location::class); - $location = Location::findOrFail($id); + $location = Location::with('parent', 'manager', 'childLocations') + ->select([ + 'locations.id', + 'locations.name', + 'locations.address', + 'locations.address2', + 'locations.city', + 'locations.state', + 'locations.zip', + 'locations.country', + 'locations.parent_id', + 'locations.manager_id', + 'locations.created_at', + 'locations.updated_at', + 'locations.image', + 'locations.currency' + ]) + ->withCount('assignedAssets') + ->withCount('assets') + ->withCount('users')->findOrFail($id); return (new LocationsTransformer)->transformLocation($location); } From d5cf0f1fbd1110cd69dfa899e35f0f7300a5575a Mon Sep 17 00:00:00 2001 From: snipe Date: Mon, 24 Sep 2018 19:04:00 -0700 Subject: [PATCH 33/42] Prevent deleting manufactureres via API if they have items/models --- .../Api/ManufacturersController.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Api/ManufacturersController.php b/app/Http/Controllers/Api/ManufacturersController.php index 6c1339a78..0b9a95ecc 100644 --- a/app/Http/Controllers/Api/ManufacturersController.php +++ b/app/Http/Controllers/Api/ManufacturersController.php @@ -83,7 +83,7 @@ class ManufacturersController extends Controller public function show($id) { $this->authorize('view', Manufacturer::class); - $manufacturer = Manufacturer::findOrFail($id); + $manufacturer = Manufacturer::withCount('assets as assets_count', 'licenses as licenses_count', 'consumables as consumables_count', 'accessories as accessories_count', 'models as models_count' )->findOrFail($id); return (new ManufacturersTransformer)->transformManufacturer($manufacturer); } @@ -120,11 +120,21 @@ class ManufacturersController extends Controller */ public function destroy($id) { + $this->authorize('delete', Manufacturer::class); - $manufacturer = Manufacturer::findOrFail($id); + $manufacturer = Manufacturer::withCount('assets as assets_count', 'licenses as licenses_count', 'consumables as consumables_count', 'accessories as accessories_count', 'models as models_count' )->findOrFail($id); $this->authorize('delete', $manufacturer); - $manufacturer->delete(); - return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/manufacturers/message.delete.success'))); + + if (($manufacturer->assets_count == 0) && ($manufacturer->licenses_count==0) && ($manufacturer->consumables_count==0) && ($manufacturer->accessories_count==0) && ($manufacturer->models==0) && ($manufacturer->deleted_at=='')) { + $manufacturer->delete(); + return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/manufacturers/message.delete.success'))); + } + + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/manufacturers/message.delete.error'))); + + + + } From 9108ff8caacef6a4aa86c90e420910e7e28a9770 Mon Sep 17 00:00:00 2001 From: snipe Date: Mon, 24 Sep 2018 19:06:12 -0700 Subject: [PATCH 34/42] fixed typo --- app/Http/Controllers/Api/ManufacturersController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Api/ManufacturersController.php b/app/Http/Controllers/Api/ManufacturersController.php index 0b9a95ecc..a943cc21d 100644 --- a/app/Http/Controllers/Api/ManufacturersController.php +++ b/app/Http/Controllers/Api/ManufacturersController.php @@ -125,7 +125,7 @@ class ManufacturersController extends Controller $manufacturer = Manufacturer::withCount('assets as assets_count', 'licenses as licenses_count', 'consumables as consumables_count', 'accessories as accessories_count', 'models as models_count' )->findOrFail($id); $this->authorize('delete', $manufacturer); - if (($manufacturer->assets_count == 0) && ($manufacturer->licenses_count==0) && ($manufacturer->consumables_count==0) && ($manufacturer->accessories_count==0) && ($manufacturer->models==0) && ($manufacturer->deleted_at=='')) { + if (($manufacturer->assets_count == 0) && ($manufacturer->licenses_count==0) && ($manufacturer->consumables_count==0) && ($manufacturer->accessories_count==0) && ($manufacturer->models_count==0) && ($manufacturer->deleted_at=='')) { $manufacturer->delete(); return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/manufacturers/message.delete.success'))); } From 115f718cd9fb1fb4a155854167486f47bc5c2bf5 Mon Sep 17 00:00:00 2001 From: snipe Date: Mon, 24 Sep 2018 19:10:24 -0700 Subject: [PATCH 35/42] Fix view if model doesnt have a manufacturer --- resources/views/models/view.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/models/view.blade.php b/resources/views/models/view.blade.php index 251e4849e..d110bbcdc 100755 --- a/resources/views/models/view.blade.php +++ b/resources/views/models/view.blade.php @@ -112,7 +112,7 @@ {{ $model->manufacturer->name }} @endcan - @endif + @if ($model->manufacturer->url)
  • {{ $model->manufacturer->url }} @@ -138,7 +138,7 @@ {{ $model->manufacturer->support_email }}
  • @endif - + @endif @if ($model->model_number)
  • {{ trans('general.model_no') }}: From f85e3edfc7361986e676e32398276dff8f1fbb3d Mon Sep 17 00:00:00 2001 From: nix Date: Tue, 25 Sep 2018 22:24:50 +0200 Subject: [PATCH 36/42] fixed checking permissions for users with no permissions set (#6229) When a user has no permissions set (=NULL) in the database (like after an LDAP import) but is a member of a group with permissions, those group permissions would not have be applied, effectively denying every access regardless of group permissions. --- app/Models/User.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Models/User.php b/app/Models/User.php index 71fa797dc..1dc59b1ba 100755 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -126,12 +126,13 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo $user_permissions = json_decode($this->permissions, true); + $is_user_section_permissions_set = ($user_permissions != '') && array_key_exists($section, $user_permissions); //If the user is explicitly granted, return true - if (($user_permissions!='') && ((array_key_exists($section, $user_permissions)) && ($user_permissions[$section]=='1'))) { + if ($is_user_section_permissions_set && ($user_permissions[$section]=='1')) { return true; } // If the user is explicitly denied, return false - if (($user_permissions=='') || array_key_exists($section, $user_permissions) && ($user_permissions[$section]=='-1')) { + if ($is_user_section_permissions_set && ($user_permissions[$section]=='-1')) { return false; } From bcf9d2f75cc407d1c5525f67502d913fc1f2e1b7 Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 25 Sep 2018 00:36:43 -0700 Subject: [PATCH 37/42] =?UTF-8?q?Don=E2=80=99t=20require=20statuslabel=20v?= =?UTF-8?q?iew=20to=20check=20for=20deployable=20status?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Api/StatuslabelsController.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/Http/Controllers/Api/StatuslabelsController.php b/app/Http/Controllers/Api/StatuslabelsController.php index ba7854147..4d5f9d016 100644 --- a/app/Http/Controllers/Api/StatuslabelsController.php +++ b/app/Http/Controllers/Api/StatuslabelsController.php @@ -238,9 +238,6 @@ class StatuslabelsController extends Controller */ public function checkIfDeployable($id) { $statuslabel = Statuslabel::findOrFail($id); - - $this->authorize('view', $statuslabel); - if ($statuslabel->getStatuslabelType()=='deployable') { return '1'; } From 73e88faa00a99ca67487c7913eed64fc1d2e91d3 Mon Sep 17 00:00:00 2001 From: Ivan Nieto Date: Tue, 25 Sep 2018 16:01:21 -0500 Subject: [PATCH 38/42] Using changes proposed by dmeltzer to eliminate has_licenses function in some tests code (#6218) --- tests/unit/DepreciationTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/DepreciationTest.php b/tests/unit/DepreciationTest.php index bb31de772..b883b891b 100644 --- a/tests/unit/DepreciationTest.php +++ b/tests/unit/DepreciationTest.php @@ -39,11 +39,11 @@ class DepreciationTest extends BaseTest { $category = $this->createValidCategory('license-graphics-category'); $depreciation = $this->createValidDepreciation('computer', ['name' => 'New Depreciation']); - $licenses = factory(App\Models\License::class, 5)->states('photoshop')->create([ + $licenses = factory(App\Models\LicenseModel::class, 5)->states('photoshop')->create([ 'depreciation_id'=>$depreciation->id, 'category_id' => $category->id ]); - $this->assertEquals(5,$depreciation->has_licenses()); + $this->assertEquals(5,$depreciation->licenses()->count()); } } From 82affd515433da6e9dc0a5d84c1e75af6911e78f Mon Sep 17 00:00:00 2001 From: Wes Hulette Date: Wed, 26 Sep 2018 15:10:11 -0400 Subject: [PATCH 39/42] Added PHP_CS & PHPMD files (#6234) Added PHP_CS & PHP mess detector file to help with code standardization. --- .php_cs.dist | 39 +++++++++++++++++++++++++++++++++++++ phpmd.xml | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 .php_cs.dist create mode 100644 phpmd.xml diff --git a/.php_cs.dist b/.php_cs.dist new file mode 100644 index 000000000..87846540f --- /dev/null +++ b/.php_cs.dist @@ -0,0 +1,39 @@ +notPath('bootstrap/cache') + ->notPath('storage') + ->notPath('vendor') + ->notPath('node_modules') + ->in(__DIR__) + ->name('*.php') + ->notName('*.blade.php') + ->ignoreDotFiles(true) + ->ignoreVCS(true) +; + +return PhpCsFixer\Config::create() + ->setRules(array( + '@Symfony' => true, + 'class_definition' => [ + 'multiLineExtendsEachSingleLine' => true, + ], + 'ordered_class_elements' => [ + 'use_trait', 'constant_public', 'constant_protected', 'constant_private', + 'property_public', 'property_protected', 'property_private', 'construct', + 'destruct', 'magic', 'phpunit', 'method_public', 'method_protected', + 'method_private' + ], + 'function_declaration' => ['closure_function_spacing' => 'none'], + 'binary_operator_spaces' => ['default' => 'align_single_space_minimal'], + 'array_syntax' => ['syntax' => 'short'], + 'concat_space' => ['spacing' => 'one'], + 'blank_line_after_namespace' => true, + 'linebreak_after_opening_tag' => true, + 'not_operator_with_successor_space' => true, + 'ordered_imports' => true, + 'phpdoc_order' => true, + )) + ->setFinder($finder); diff --git a/phpmd.xml b/phpmd.xml new file mode 100644 index 000000000..d991b90fb --- /dev/null +++ b/phpmd.xml @@ -0,0 +1,54 @@ + + + + Inspired by https://github.com/phpmd/phpmd/issues/137 + using http://phpmd.org/documentation/creating-a-ruleset.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 3 + + + + + + + \ No newline at end of file From 16e56646b8d350df926d824576bf4985ef5e6dfd Mon Sep 17 00:00:00 2001 From: Wes Hulette Date: Wed, 26 Sep 2018 17:07:41 -0400 Subject: [PATCH 40/42] Fixed #5965: Allow multiple alert email addresses (#6233) * Fixed #5965: Allow multiple alert email addresses * Style changes based on PR feedback. --- .gitignore | 4 ++ .../Commands/SendExpectedCheckinAlerts.php | 30 +++++------ app/Console/Commands/SendExpirationAlerts.php | 51 +++++++------------ app/Console/Commands/SendInventoryAlerts.php | 33 +++++------- app/Models/Recipients/AlertRecipient.php | 13 ++--- app/Models/Recipients/Recipient.php | 6 +-- app/Notifications/InventoryAlert.php | 16 +++--- 7 files changed, 63 insertions(+), 90 deletions(-) diff --git a/.gitignore b/.gitignore index ee8f53384..8aed658e1 100755 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,7 @@ tests/_support/_generated/* *.cache .vagrant + +\.php_cs\.dist + +phpmd\.xml diff --git a/app/Console/Commands/SendExpectedCheckinAlerts.php b/app/Console/Commands/SendExpectedCheckinAlerts.php index d0d3ee155..d10791200 100644 --- a/app/Console/Commands/SendExpectedCheckinAlerts.php +++ b/app/Console/Commands/SendExpectedCheckinAlerts.php @@ -2,17 +2,15 @@ namespace App\Console\Commands; - use App\Models\Asset; use App\Models\Setting; -use Illuminate\Console\Command; -use App\Notifications\ExpectedCheckinNotification; use App\Notifications\ExpectedCheckinAdminNotification; +use App\Notifications\ExpectedCheckinNotification; use Carbon\Carbon; +use Illuminate\Console\Command; class SendExpectedCheckinAlerts extends Command { - /** * The console command name. * @@ -29,8 +27,6 @@ class SendExpectedCheckinAlerts extends Command /** * Create a new command instance. - * - * @return void */ public function __construct() { @@ -44,25 +40,25 @@ class SendExpectedCheckinAlerts extends Command */ public function handle() { - $settings = Setting::getSettings(); + $settings = Setting::getSettings(); $whenNotify = Carbon::now()->addDays(7); - $assets = Asset::with('assignedTo')->whereNotNull('assigned_to')->whereNotNull('expected_checkin')->where('expected_checkin', '<=', $whenNotify)->get(); + $assets = Asset::with('assignedTo')->whereNotNull('assigned_to')->whereNotNull('expected_checkin')->where('expected_checkin', '<=', $whenNotify)->get(); - $this->info($whenNotify.' is deadline'); - $this->info($assets->count().' assets'); + $this->info($whenNotify . ' is deadline'); + $this->info($assets->count() . ' assets'); foreach ($assets as $asset) { - if ($asset->assigned && $asset->checkedOutToUser()) { + if ($asset->assigned && $asset->checkedOutToUser()) { $asset->assigned->notify((new ExpectedCheckinNotification($asset))); } } - // Send a rollup to the admin, if settings dictate - $recipient = new \App\Models\Recipients\AlertRecipient(); - - if (($assets) && ($assets->count() > 0) && ($settings->alerts_enabled && $settings->alert_email != '')) { - $recipient->notify(new ExpectedCheckinAdminNotification($assets)); + if (($assets) && ($assets->count() > 0) && ($settings->alert_email != '')) { + // Send a rollup to the admin, if settings dictate + $recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) { + return new AlertRecipient($item); + }); + Notification::send($recipients, new ExpectedCheckinAdminNotification($assets)); } - } } diff --git a/app/Console/Commands/SendExpirationAlerts.php b/app/Console/Commands/SendExpirationAlerts.php index cdc62f8cc..f78768ea5 100644 --- a/app/Console/Commands/SendExpirationAlerts.php +++ b/app/Console/Commands/SendExpirationAlerts.php @@ -4,16 +4,14 @@ namespace App\Console\Commands; use App\Models\Asset; use App\Models\License; +use App\Models\Recipients\AlertRecipient; use App\Models\Setting; -use DB; -use App\Notifications\ExpiringLicenseNotification; use App\Notifications\ExpiringAssetsNotification; - +use App\Notifications\ExpiringLicenseNotification; use Illuminate\Console\Command; class SendExpirationAlerts extends Command { - /** * The console command name. * @@ -30,8 +28,6 @@ class SendExpirationAlerts extends Command /** * Create a new command instance. - * - * @return void */ public function __construct() { @@ -45,46 +41,35 @@ class SendExpirationAlerts extends Command */ public function handle() { - - $settings = Setting::getSettings(); + $settings = Setting::getSettings(); $threshold = $settings->alert_interval; + if (($settings->alert_email != '') && ($settings->alerts_enabled == 1)) { - // Expiring Assets - $assets = Asset::getExpiringWarrantee(Setting::getSettings()->alert_interval); - $this->info(trans_choice('mail.assets_warrantee_alert', $assets->count(), ['count'=>$assets->count(), 'threshold' => $threshold])); - - // Expiring licenses - $licenses = License::getExpiringLicenses($threshold); - - $this->info(trans_choice('mail.license_expiring_alert', $licenses->count(), ['count'=>$licenses->count(), 'threshold' => $threshold])); - - $recipient = new \App\Models\Recipients\AlertRecipient(); - - if ((Setting::getSettings()->alert_email!='') && ($settings->alerts_enabled==1)) { + // Send a rollup to the admin, if settings dictate + $recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) { + return new AlertRecipient($item); + }); + // Expiring Assets + $assets = Asset::getExpiringWarrantee(Setting::getSettings()->alert_interval); if ($assets->count() > 0) { - // Send a rollup to the admin, if settings dictate - $recipient->notify(new ExpiringAssetsNotification($assets, $threshold)); + $this->info(trans_choice('mail.assets_warrantee_alert', $assets->count(), ['count' => $assets->count(), 'threshold' => $threshold])); + Notification::send($recipients, new ExpiringAssetsNotification($assets, $threshold)); } + // Expiring licenses + $licenses = License::getExpiringLicenses($threshold); if ($licenses->count() > 0) { - $recipient->notify(new ExpiringLicenseNotification($licenses, $threshold)); + $this->info(trans_choice('mail.license_expiring_alert', $licenses->count(), ['count' => $licenses->count(), 'threshold' => $threshold])); + Notification::send($recipients, new ExpiringLicenseNotification($licenses, $threshold)); } - - } else { - - if ($settings->alert_email=='') { + if ($settings->alert_email == '') { $this->error('Could not send email. No alert email configured in settings'); - } elseif ($settings->alerts_enabled!=1) { + } elseif (1 != $settings->alerts_enabled) { $this->info('Alerts are disabled in the settings. No mail will be sent'); } - } - - - - } } diff --git a/app/Console/Commands/SendInventoryAlerts.php b/app/Console/Commands/SendInventoryAlerts.php index 1f96fca9d..93b04978b 100644 --- a/app/Console/Commands/SendInventoryAlerts.php +++ b/app/Console/Commands/SendInventoryAlerts.php @@ -2,13 +2,12 @@ namespace App\Console\Commands; -use App\Models\Setting; -use DB; -use Mail; use App\Helpers\Helper; +use App\Models\Recipients\AlertRecipient; +use App\Models\Setting; use App\Notifications\InventoryAlert; - use Illuminate\Console\Command; +use Illuminate\Support\Facades\Notification; class SendInventoryAlerts extends Command { @@ -28,8 +27,6 @@ class SendInventoryAlerts extends Command /** * Create a new command instance. - * - * @return void */ public function __construct() { @@ -45,28 +42,24 @@ class SendInventoryAlerts extends Command { $settings = Setting::getSettings(); - if (($settings->alert_email!='') && ($settings->alerts_enabled==1)) { - + if (($settings->alert_email != '') && ($settings->alerts_enabled == 1)) { $items = Helper::checkLowInventory(); - // Send a rollup to the admin, if settings dictate - $recipient = new \App\Models\Recipients\AlertRecipient(); + if (($items) && (count($items) > 0)) { + $this->info(trans_choice('mail.low_inventory_alert', count($items))); + // Send a rollup to the admin, if settings dictate + $recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) { + return new AlertRecipient($item); + }); - if (($items) && (count($items) > 0) && ($settings->alert_email!='')) { - - $this->info( trans_choice('mail.low_inventory_alert',count($items)) ); - - $recipient->notify(new InventoryAlert($items, $settings->alert_threshold)); + Notification::send($recipients, new InventoryAlert($items, $settings->alert_threshold)); } - } else { - if (Setting::getSettings()->alert_email=='') { + if ($settings->alert_email == '') { $this->error('Could not send email. No alert email configured in settings'); - } elseif (Setting::getSettings()->alerts_enabled!=1) { + } elseif (1 != $settings->alerts_enabled) { $this->info('Alerts are disabled in the settings. No mail will be sent'); } } - - } } diff --git a/app/Models/Recipients/AlertRecipient.php b/app/Models/Recipients/AlertRecipient.php index af9e3a012..07557649c 100644 --- a/app/Models/Recipients/AlertRecipient.php +++ b/app/Models/Recipients/AlertRecipient.php @@ -1,14 +1,11 @@ email = $settings->alert_email; + $this->email = trim($email); } - } diff --git a/app/Models/Recipients/Recipient.php b/app/Models/Recipients/Recipient.php index 888dd8e61..d04fd1f11 100644 --- a/app/Models/Recipients/Recipient.php +++ b/app/Models/Recipients/Recipient.php @@ -1,12 +1,12 @@ markdown('notifications.markdown.report-low-inventory', + $message = (new MailMessage)->markdown( + 'notifications.markdown.report-low-inventory', [ 'items' => $this->items, 'threshold' => $this->threshold, - ]) + ] + ) ->subject(trans('mail.Low_Inventory_Report')); return $message; - - } /** From 309f1027451c62196fb35888368746cb3033ef67 Mon Sep 17 00:00:00 2001 From: Ivan Nieto Date: Thu, 27 Sep 2018 00:33:34 -0500 Subject: [PATCH 41/42] Weird syntax in layout.blade.php. Change the use of 'or' operator to the null coalesce operator (#6240) --- resources/views/vendor/mail/html/layout.blade.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/views/vendor/mail/html/layout.blade.php b/resources/views/vendor/mail/html/layout.blade.php index b2af14ec2..4c537ac38 100644 --- a/resources/views/vendor/mail/html/layout.blade.php +++ b/resources/views/vendor/mail/html/layout.blade.php @@ -31,7 +31,7 @@ - {{ $header or '' }} + {{ $header ?? '' }} @@ -42,14 +42,14 @@
    {{ Illuminate\Mail\Markdown::parse($slot) }} - {{ $subcopy or '' }} + {{ $subcopy ?? '' }}
    - {{ $footer or '' }} + {{ $footer ?? '' }} @@ -57,3 +57,4 @@ + \ No newline at end of file From e5d0f74ba7506da97e3528ccce07453a0080db7e Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 27 Sep 2018 12:06:11 -0700 Subject: [PATCH 42/42] Fixed #6248 - add free seats to licenses API endpoint --- app/Http/Controllers/Api/LicensesController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Api/LicensesController.php b/app/Http/Controllers/Api/LicensesController.php index f85fa2314..5df6c52d3 100644 --- a/app/Http/Controllers/Api/LicensesController.php +++ b/app/Http/Controllers/Api/LicensesController.php @@ -150,7 +150,7 @@ class LicensesController extends Controller public function show($id) { $this->authorize('view', License::class); - $license = License::findOrFail($id); + $license = License::withCount('freeSeats')->findOrFail($id); $license = $license->load('assignedusers', 'licenseSeats.user', 'licenseSeats.asset'); return (new LicensesTransformer)->transformLicense($license); }