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/.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/app/Console/Commands/SendExpectedCheckinAlerts.php b/app/Console/Commands/SendExpectedCheckinAlerts.php index a78f0e1ae..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,30 +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->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/Events/CheckoutAccepted.php b/app/Events/CheckoutAccepted.php new file mode 100644 index 000000000..0b26e6947 --- /dev/null +++ b/app/Events/CheckoutAccepted.php @@ -0,0 +1,27 @@ +acceptance = $acceptance; + } +} diff --git a/app/Events/CheckoutDeclined.php b/app/Events/CheckoutDeclined.php new file mode 100644 index 000000000..2aae6dd91 --- /dev/null +++ b/app/Events/CheckoutDeclined.php @@ -0,0 +1,27 @@ +acceptance = $acceptance; + } +} diff --git a/app/Events/CheckoutableCheckedIn.php b/app/Events/CheckoutableCheckedIn.php new file mode 100644 index 000000000..0f6a96491 --- /dev/null +++ b/app/Events/CheckoutableCheckedIn.php @@ -0,0 +1,30 @@ +checkoutable = $checkoutable; + $this->checkedOutTo = $checkedOutTo; + $this->checkedInBy = $checkedInBy; + $this->note = $note; + } +} diff --git a/app/Events/CheckoutableCheckedOut.php b/app/Events/CheckoutableCheckedOut.php new file mode 100644 index 000000000..5e6ffd243 --- /dev/null +++ b/app/Events/CheckoutableCheckedOut.php @@ -0,0 +1,30 @@ +checkoutable = $checkoutable; + $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 83d59ab77..930e9b661 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckinController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckinController.php @@ -2,10 +2,12 @@ namespace App\Http\Controllers\Accessories; +use App\Events\CheckoutableCheckedIn; 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; @@ -46,7 +48,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))) { @@ -61,7 +63,8 @@ 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 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 50c174f0c..5ec8a8704 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\CheckoutableCheckedOut; use App\Http\Controllers\Controller; use App\Models\Accessory; use App\Models\User; @@ -77,10 +78,10 @@ class AccessoryCheckoutController extends Controller 'assigned_to' => $request->get('assigned_to') ]); - $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 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/Account/AcceptanceController.php b/app/Http/Controllers/Account/AcceptanceController.php new file mode 100644 index 000000000..198e11e99 --- /dev/null +++ b/app/Http/Controllers/Account/AcceptanceController.php @@ -0,0 +1,125 @@ +pending()->get(); + + return view('account/accept.index', compact('acceptances')); + } + + /** + * Shows a form to either accept or decline the checkout acceptance + * + * @param int $id + * @return mixed + */ + public function create($id) { + + $acceptance = CheckoutAcceptance::find($id); + + if (is_null($acceptance)) { + return redirect()->reoute('account.accept')->with('error', trans('admin/hardware/message.does_not_exist')); + } + + if (! $acceptance->isPending()) { + return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.asset_already_accepted')); + } + + if (! $acceptance->isCheckedOutTo(Auth::user())) { + return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.incorrect_user_accepted')); + } + + if (!Company::isCurrentUserHasAccess($acceptance->checkoutable)) { + return redirect()->route('account.accept')->with('error', trans('general.insufficient_permissions')); + } + + 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); + + if (is_null($acceptance)) { + return redirect()->reoute('account.accept')->with('error', trans('admin/hardware/message.does_not_exist')); + } + + if (! $acceptance->isPending()) { + return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.asset_already_accepted')); + } + + if (! $acceptance->isCheckedOutTo(Auth::user())) { + return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.incorrect_user_accepted')); + } + + 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')); + } + + /** + * 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') { + + $acceptance->accept($sig_filename); + + event(new CheckoutAccepted($acceptance)); + + $return_msg = trans('admin/users/message.accepted'); + + } else { + + $acceptance->decline($sig_filename); + + event(new CheckoutDeclined($acceptance)); + + $return_msg = trans('admin/users/message.declined'); + + } + + return redirect()->to('account/accept')->with('success', $return_msg); + } +} \ No newline at end of file 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); } 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); } 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); } diff --git a/app/Http/Controllers/Api/ManufacturersController.php b/app/Http/Controllers/Api/ManufacturersController.php index 6c1339a78..a943cc21d 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_count==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'))); + + + + } 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'; } 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'))); } diff --git a/app/Http/Controllers/Assets/AssetCheckinController.php b/app/Http/Controllers/Assets/AssetCheckinController.php index 7eed360a6..e87a1df23 100644 --- a/app/Http/Controllers/Assets/AssetCheckinController.php +++ b/app/Http/Controllers/Assets/AssetCheckinController.php @@ -2,10 +2,12 @@ namespace App\Http\Controllers\Assets; +use App\Events\CheckoutableCheckedIn; 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; @@ -82,8 +84,8 @@ class AssetCheckinController extends Controller // Was the asset updated? if ($asset->save()) { - $asset->logCheckin($target, e(request('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 89eab1f5f..dfba1dcf8 100644 --- a/app/Http/Controllers/Components/ComponentCheckinController.php +++ b/app/Http/Controllers/Components/ComponentCheckinController.php @@ -2,11 +2,14 @@ namespace App\Http\Controllers\Components; +use App\Events\CheckoutableCheckedIn; +use App\Events\ComponentCheckedIn; use App\Http\Controllers\Controller; 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; @@ -86,22 +89,16 @@ 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) { DB::table('components_assets')->where('id', '=', $component_asset_id)->delete(); } + $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 8922317fa..bfa9e8967 100644 --- a/app/Http/Controllers/Components/ComponentCheckoutController.php +++ b/app/Http/Controllers/Components/ComponentCheckoutController.php @@ -2,6 +2,8 @@ namespace App\Http\Controllers\Components; +use App\Events\CheckoutableCheckedOut; +use App\Events\ComponentCheckedOut; use App\Http\Controllers\Controller; use App\Models\Asset; use App\Models\Component; @@ -86,7 +88,8 @@ class ComponentCheckoutController extends Controller 'asset_id' => $asset_id ]); - $component->logCheckout(e(Input::get('note')), $asset); + 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 6de35ff9e..e3e3e11b0 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\CheckoutableCheckedOut; +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; @@ -41,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')); @@ -67,7 +68,7 @@ class ConsumableCheckoutController extends Controller 'assigned_to' => e(Input::get('assigned_to')) ]); - $consumable->logCheckout(e(Input::get('note')), $user); + 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 f942aa1e7..4bddbd575 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckinController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckinController.php @@ -2,12 +2,14 @@ namespace App\Http\Controllers\Licenses; +use App\Events\CheckoutableCheckedIn; 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 Illuminate\Support\Facades\Session; use Illuminate\Support\Facades\Validator; @@ -49,7 +51,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))) { @@ -88,7 +90,9 @@ class LicenseCheckinController extends Controller // Was the asset updated? if ($licenseSeat->save()) { - $licenseSeat->logCheckin($return_to, e(request('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 570cd6b41..5d878a150 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckoutController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckoutController.php @@ -2,15 +2,16 @@ namespace App\Http\Controllers\Licenses; +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 @@ -103,7 +104,9 @@ class LicenseCheckoutController extends Controller $licenseSeat->assigned_to = $target->assigned_to; } if ($licenseSeat->save()) { - $licenseSeat->logCheckout(request('note'), $target); + + event(new CheckoutableCheckedOut($licenseSeat, $target, Auth::user(), request('note'))); + return true; } return false; @@ -118,7 +121,9 @@ class LicenseCheckoutController extends Controller $licenseSeat->assigned_to = request('assigned_to'); if ($licenseSeat->save()) { - $licenseSeat->logCheckout(request('note'), $target); + + event(new CheckoutableCheckedOut($licenseSeat, $target, Auth::user(), request('note'))); + return true; } return false; 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/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/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 new file mode 100644 index 000000000..cdb4fda1f --- /dev/null +++ b/app/Listeners/LogListener.php @@ -0,0 +1,90 @@ +checkoutable->logCheckin($event->checkedOutTo, $event->note); + } + + public function onCheckoutableCheckedOut(CheckoutableCheckedOut $event) { + $event->checkoutable->logCheckout($event->note, $event->checkedOutTo); + } + + public function onCheckoutAccepted(CheckoutAccepted $event) { + $logaction = new Actionlog(); + + $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 onCheckoutDeclined(CheckoutDeclined $event) { + $logaction = new Actionlog(); + $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(); + } + + /** + * Register the listeners for the subscriber. + * + * @param Illuminate\Events\Dispatcher $events + */ + public function subscribe($events) + { + $list = [ + 'CheckoutableCheckedIn', + 'CheckoutableCheckedOut', + 'CheckoutAccepted', + 'CheckoutDeclined', + ]; + + 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/Accessory.php b/app/Models/Accessory.php index 8fa27515e..76b50704c 100755 --- a/app/Models/Accessory.php +++ b/app/Models/Accessory.php @@ -1,6 +1,7 @@ ['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 4d8ca1c89..c2d48cee2 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -1,10 +1,14 @@ assigned_to = null; + $this->assigned_type = null; + $this->accepted = null; + $this->save(); + } /** @@ -252,21 +263,11 @@ 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()) { - $this->logCheckout($note, $target); + + event(new CheckoutableCheckedOut($this, $target, Auth::user(), $note)); + $this->increment('checkout_counter', 1); return true; } diff --git a/app/Models/CheckoutAcceptance.php b/app/Models/CheckoutAcceptance.php new file mode 100644 index 000000000..6ff850f41 --- /dev/null +++ b/app/Models/CheckoutAcceptance.php @@ -0,0 +1,114 @@ +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; + $this->save(); + + /** + * Update state for the checked out item + */ + $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; + $this->save(); + + /** + * Update state for the checked out item + */ + $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/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..596106c26 100644 --- a/app/Models/Consumable.php +++ b/app/Models/Consumable.php @@ -1,6 +1,7 @@ '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/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; /** 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..ab514c0a5 100755 --- a/app/Models/LicenseSeat.php +++ b/app/Models/LicenseSeat.php @@ -2,32 +2,37 @@ namespace App\Models; use App\Models\Loggable; +use App\Models\Traits\Acceptable; +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'; - /** - * Set static properties to determine which checkout/checkin handlers we should use - */ - public static $checkoutClass = CheckoutLicenseNotification::class; - public static $checkinClass = CheckinLicenseNotification::class; + use Acceptable; public function getCompanyableParents() { return ['asset', 'license']; } + public function getEula() { + return $this->license->getEula(); + } + /** * Establishes the seat -> license relationship * diff --git a/app/Models/Location.php b/app/Models/Location.php index a6e2007cd..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' ); /** @@ -56,6 +56,7 @@ class Location extends SnipeModel 'country', 'zip', 'ldap_ou', + 'manager_id', 'currency', 'image', ]; 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; } 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 @@ + */ +trait Acceptable { + /** + * Run after the checkout acceptance was accepted by the user + * + * @param User $acceptedBy + * @param string $signature + */ + public function acceptedCheckout(User $acceptedBy, $signature) {} + + /** + * Run after the checkout acceptance was declined by the user + * + * @param User $acceptedBy + * @param string $signature + */ + public function declinedCheckout(User $declinedBy, $signature) {} +} 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; } 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/CheckinLicenseSeatNotification.php similarity index 86% rename from app/Notifications/CheckinLicenseNotification.php rename to app/Notifications/CheckinLicenseSeatNotification.php index f8a52ccf3..69554d493 100644 --- a/app/Notifications/CheckinLicenseNotification.php +++ b/app/Notifications/CheckinLicenseSeatNotification.php @@ -2,6 +2,8 @@ namespace App\Notifications; +use App\Models\License; +use App\Models\LicenseSeat; use App\Models\Setting; use App\Models\SnipeModel; use App\Models\User; @@ -12,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; /** @@ -25,19 +27,13 @@ class CheckinLicenseNotification extends Notification * * @param $params */ - public function __construct($params) + public function __construct(LicenseSeat $licenseSeat, $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 = $licenseSeat->license; + $this->admin = $checkedInBy; + $this->note = $note; + $this->settings = Setting::getSettings(); } /** diff --git a/app/Notifications/CheckoutAccessoryNotification.php b/app/Notifications/CheckoutAccessoryNotification.php index 4e8950619..11368bf06 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(); } @@ -132,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, @@ -140,7 +130,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' => $accept_url, ]) ->subject(trans('mail.Confirm_accessory_delivery')); diff --git a/app/Notifications/CheckoutAssetNotification.php b/app/Notifications/CheckoutAssetNotification.php index 8451d6dcd..db4ba294f 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,25 @@ 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, $acceptance, $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->acceptance = $acceptance; + + $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', @@ -146,17 +141,18 @@ 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, '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' => $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 d9a80bfc3..40780c7ab 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,16 @@ class CheckoutConsumableNotification extends Notification * * @param $params */ - public function __construct($params) + public function __construct(Consumable $consumable, $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 = $consumable; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->target = $checkedOutTo; + $this->acceptance = $acceptance; + + $this->settings = Setting::getSettings(); } @@ -126,16 +122,17 @@ 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, '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' => $accept_url, ]) ->subject(trans('mail.Confirm_consumable_delivery')); diff --git a/app/Notifications/CheckoutLicenseNotification.php b/app/Notifications/CheckoutLicenseSeatNotification.php similarity index 85% rename from app/Notifications/CheckoutLicenseNotification.php rename to app/Notifications/CheckoutLicenseSeatNotification.php index 93530e2cd..74df6e2f4 100644 --- a/app/Notifications/CheckoutLicenseNotification.php +++ b/app/Notifications/CheckoutLicenseSeatNotification.php @@ -2,6 +2,8 @@ namespace App\Notifications; +use App\Models\License; +use App\Models\LicenseSeat; use App\Models\Setting; use App\Models\SnipeModel; use App\Models\User; @@ -12,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; /** @@ -25,23 +27,15 @@ class CheckoutLicenseNotification extends Notification * * @param $params */ - public function __construct($params) + public function __construct(LicenseSeat $licenseSeat, $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->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 = $licenseSeat->license; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->target = $checkedOutTo; + $this->acceptance = $acceptance; + $this->settings = Setting::getSettings(); } /** @@ -125,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, @@ -133,7 +129,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' => $accept_url, ]) ->subject(trans('mail.Confirm_license_delivery')); diff --git a/app/Notifications/InventoryAlert.php b/app/Notifications/InventoryAlert.php index 3dee49134..0d9a26fc2 100644 --- a/app/Notifications/InventoryAlert.php +++ b/app/Notifications/InventoryAlert.php @@ -3,8 +3,8 @@ namespace App\Notifications; use Illuminate\Bus\Queueable; -use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; +use Illuminate\Notifications\Messages\MailMessage; class InventoryAlert extends Notification { @@ -33,14 +33,13 @@ class InventoryAlert extends Notification */ public function via($notifiable) { - $notifyBy = []; - $notifyBy[]='mail'; + $notifyBy[] = 'mail'; + return $notifyBy; } public function toSlack($notifiable) { - } /** @@ -51,17 +50,16 @@ class InventoryAlert extends Notification */ public function toMail($params) { - - $message = (new MailMessage)->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; - - } /** 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; + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 9d3e0f8a9..28c3aecf0 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -3,6 +3,8 @@ namespace App\Providers; use Illuminate\Support\Facades\Event; +use App\Listeners\CheckoutableListener; +use App\Listeners\LogListener; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; class EventServiceProvider extends ServiceProvider @@ -23,6 +25,15 @@ class EventServiceProvider extends ServiceProvider ], ]; + /** + * The subscriber classes to register. + * + * @var array + */ + protected $subscribe = [ + LogListener::class, + CheckoutableListener::class + ]; /** * Register any events for your application. 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..67bc3b800 --- /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')->nullable(); + + $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/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() + { + // + } +} 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 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 diff --git a/resources/views/account/accept/create.blade.php b/resources/views/account/accept/create.blade.php new file mode 100644 index 000000000..67ca326e1 --- /dev/null +++ b/resources/views/account/accept/create.blade.php @@ -0,0 +1,135 @@ +@extends('layouts/default') + +{{-- Page title --}} +@section('title') + Accept {{ $acceptance->checkoutable->present()->name() }} + @parent +@stop + + +{{-- Page content --}} +@section('content') + + + + + + + +
+ + + + +
+
+
+
+
+
+ +
+ +
+ +
+ + @if ($acceptance->checkoutable->getEula()) +
+
+ {!! $acceptance->checkoutable->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/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/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 @@
+
diff --git a/resources/views/layouts/default.blade.php b/resources/views/layouts/default.blade.php index 8e1663f2b..09b3291a5 100644 --- a/resources/views/layouts/default.blade.php +++ b/resources/views/layouts/default.blade.php @@ -309,7 +309,11 @@ Requested Assets - +
  • + + + Accept Assets +
  • 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') }}: 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 diff --git a/routes/web.php b/routes/web.php index 20974f5f8..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( @@ -274,6 +270,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/{id}', 'Account\AcceptanceController@create') + ->name('account.accept.item'); + + Route::post('accept/{id}', 'Account\AcceptanceController@store'); + }); 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()); } }