mirror of
https://github.com/grokability/snipe-it.git
synced 2026-03-12 17:52:00 +08:00
Merge branch 'develop' into livewire4
# Conflicts: # composer.lock
This commit is contained in:
@@ -113,7 +113,7 @@ class MoveUploadsToNewDisk extends Command
|
||||
$filename = basename($private_upload[$x]);
|
||||
|
||||
try {
|
||||
Storage::put($private_type . '/' . $filename, file_get_contents($private_upload[$i]));
|
||||
Storage::put($private_type . '/' . $filename, file_get_contents($private_upload[$x]));
|
||||
$new_url = Storage::url($private_type . '/' . $filename, $filename);
|
||||
$this->info($type_count . '. PRIVATE: ' . $filename . ' was copied to ' . $new_url);
|
||||
} catch (\Exception $e) {
|
||||
|
||||
@@ -50,8 +50,8 @@ class ResetDemoSettings extends Command
|
||||
$settings->alert_email = 'service@snipe-it.io';
|
||||
$settings->login_note = 'Use `admin` / `password` to login to the demo.';
|
||||
$settings->header_color = '#3c8dbc';
|
||||
$settings->link_dark_color = '#86cbf2';
|
||||
$settings->link_light_color = '#084d73;';
|
||||
$settings->link_dark_color = '#5fa4cc';
|
||||
$settings->link_light_color = '#296282;';
|
||||
$settings->label2_2d_type = 'QRCODE';
|
||||
$settings->default_currency = 'USD';
|
||||
$settings->brand = 2;
|
||||
|
||||
24
app/Events/CheckoutablesCheckedOutInBulk.php
Normal file
24
app/Events/CheckoutablesCheckedOutInBulk.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class CheckoutablesCheckedOutInBulk
|
||||
{
|
||||
use Dispatchable, SerializesModels;
|
||||
|
||||
public function __construct(
|
||||
public Collection $assets,
|
||||
public Model $target,
|
||||
public User $admin,
|
||||
public string $checkout_at,
|
||||
public string $expected_checkin,
|
||||
public string $note,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -775,11 +775,11 @@ class Helper
|
||||
public static function checkLowInventory()
|
||||
{
|
||||
$alert_threshold = \App\Models\Setting::getSettings()->alert_threshold;
|
||||
$consumables = Consumable::withCount('consumableAssignments as consumable_assignments_count')->whereNotNull('min_amt')->get();
|
||||
$consumables = Consumable::withCount('consumableAssignments as consumables_users_count')->whereNotNull('min_amt')->get();
|
||||
$accessories = Accessory::withCount('checkouts as checkouts_count')->whereNotNull('min_amt')->get();
|
||||
$components = Component::whereNotNull('min_amt')->get();
|
||||
$asset_models = AssetModel::where('min_amt', '>', 0)->get();
|
||||
$licenses = License::where('min_amt', '>', 0)->get();
|
||||
$components = Component::withCount('assets as sum_unconstrained_assets')->whereNotNull('min_amt')->get();
|
||||
$asset_models = AssetModel::where('min_amt', '>', 0)->withCount(['availableAssets', 'assets'])->get();
|
||||
$licenses = License::withCount('availCount as licenses_available')->where('min_amt', '>', 0)->get();
|
||||
|
||||
$items_array = [];
|
||||
$all_count = 0;
|
||||
@@ -844,8 +844,8 @@ class Helper
|
||||
foreach ($asset_models as $asset_model){
|
||||
|
||||
$asset = new Asset();
|
||||
$total_owned = $asset->where('model_id', '=', $asset_model->id)->count();
|
||||
$avail = $asset->where('model_id', '=', $asset_model->id)->whereNull('assigned_to')->count();
|
||||
$total_owned = $asset_model->assets_count; //requires the withCount() clause in the initial query!
|
||||
$avail = $asset_model->available_assets_count; //requires the withCount() clause in the initial query!
|
||||
|
||||
if ($avail <= ($asset_model->min_amt) + $alert_threshold) {
|
||||
if ($avail > 0) {
|
||||
@@ -1404,43 +1404,6 @@ class Helper
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generic helper (largely used by livewire right now) that returns the font-awesome icon
|
||||
* for the object type.
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since 6.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function iconTypeByItem($item) {
|
||||
|
||||
switch ($item) {
|
||||
case 'asset':
|
||||
return 'fas fa-barcode';
|
||||
case 'accessory':
|
||||
return 'fas fa-keyboard';
|
||||
case 'component':
|
||||
return 'fas fa-hdd';
|
||||
case 'consumable':
|
||||
return 'fas fa-tint';
|
||||
case 'license':
|
||||
return 'far fa-save';
|
||||
case 'location':
|
||||
return 'fas fa-map-marker-alt';
|
||||
case 'user':
|
||||
return 'fas fa-user';
|
||||
case 'supplier':
|
||||
return 'fa-solid fa-store';
|
||||
case 'manufacturer':
|
||||
return 'fa-solid fa-building';
|
||||
case 'category':
|
||||
return 'fa-solid fa-table-columns';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This is a shorter way to see if the app is in demo mode.
|
||||
*
|
||||
|
||||
@@ -36,6 +36,8 @@ class IconHelper
|
||||
return 'fa-solid fa-user';
|
||||
case 'users':
|
||||
return 'fas fa-users';
|
||||
case 'supplier':
|
||||
return 'fa-solid fa-store';
|
||||
case 'restore':
|
||||
return 'fa-solid fa-trash-arrow-up';
|
||||
case 'external-link':
|
||||
@@ -55,7 +57,7 @@ class IconHelper
|
||||
case 'download':
|
||||
return 'fas fa-download';
|
||||
case 'checkmark':
|
||||
return 'fas fa-check icon-white';
|
||||
return 'fas fa-check';
|
||||
case 'x':
|
||||
return 'fas fa-times';
|
||||
case 'logout':
|
||||
@@ -87,8 +89,11 @@ class IconHelper
|
||||
case 'licenses':
|
||||
case 'license':
|
||||
return 'far fa-save';
|
||||
case 'requests':
|
||||
case 'requestable':
|
||||
return 'fas fa-laptop';
|
||||
case 'request':
|
||||
case 'requested':
|
||||
return 'fa-solid fa-bell-concierge';
|
||||
case 'reports':
|
||||
return 'fas fa-chart-bar';
|
||||
case 'heart':
|
||||
@@ -212,6 +217,50 @@ class IconHelper
|
||||
return 'fa-regular fa-building';
|
||||
case 'parent':
|
||||
return 'fa-solid fa-building-flag';
|
||||
case 'number':
|
||||
return 'fa-solid fa-hashtag';
|
||||
case 'depreciation':
|
||||
return 'fa-solid fa-arrows-down-to-line';
|
||||
case 'depreciation-calendar':
|
||||
case 'expiration':
|
||||
case 'terminates':
|
||||
return 'fa-regular fa-calendar-xmark';
|
||||
case 'manufacturer':
|
||||
return 'fa-solid fa-industry';
|
||||
case 'fieldset' :
|
||||
return 'fa-regular fa-rectangle-list';
|
||||
case 'deleted-date':
|
||||
return 'fa-solid fa-calendar-xmark';
|
||||
case 'eol':
|
||||
return 'fa-regular fa-calendar-days';
|
||||
case 'category':
|
||||
return 'fa-solid fa-icons';
|
||||
case 'cost':
|
||||
return 'fa-solid fa-money-bills';
|
||||
case 'available':
|
||||
return 'fa-solid fa-box';
|
||||
case 'checkedout':
|
||||
return 'fa-solid fa-box-open';
|
||||
case 'purchase_order':
|
||||
return 'fa-solid fa-file-invoice-dollar';
|
||||
case 'order':
|
||||
return 'fa-solid fa-file-invoice';
|
||||
case 'checkout-all':
|
||||
return 'fa-solid fa-arrows-down-to-people';
|
||||
case 'square-right':
|
||||
return 'fa-regular fa-square-caret-right';
|
||||
case 'square-left':
|
||||
return 'fa-regular fa-square-caret-left';
|
||||
case 'square':
|
||||
return 'fa-solid fa-square';
|
||||
case 'models':
|
||||
case 'model':
|
||||
return 'fa-solid fa-boxes-stacked';
|
||||
case 'min-qty':
|
||||
return 'fa-solid fa-chart-pie';
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,16 +74,16 @@ class AcceptanceController extends Controller
|
||||
*/
|
||||
public function store(Request $request, $id) : RedirectResponse
|
||||
{
|
||||
$acceptance = CheckoutAcceptance::find($id);
|
||||
|
||||
if (!$acceptance = CheckoutAcceptance::find($id)) {
|
||||
return redirect()->route('account.accept')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$assigned_user = User::find($acceptance->assigned_to_id);
|
||||
$settings = Setting::getSettings();
|
||||
$sig_filename='';
|
||||
|
||||
|
||||
if (is_null($acceptance)) {
|
||||
return redirect()->route('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'));
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ class AccessoriesController extends Controller
|
||||
'company_id',
|
||||
'notes',
|
||||
'checkouts_count',
|
||||
'order_number',
|
||||
'qty',
|
||||
// These are *relationships* so we wouldn't normally include them in this array,
|
||||
// since they would normally create a `column not found` error,
|
||||
@@ -91,6 +92,10 @@ class AccessoriesController extends Controller
|
||||
$accessories->where('accessories.company_id', '=', $request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('order_number')) {
|
||||
$accessories->where('accessories.order_number', '=', $request->input('order_number'));
|
||||
}
|
||||
|
||||
if ($request->filled('category_id')) {
|
||||
$accessories->where('category_id', '=', $request->input('category_id'));
|
||||
}
|
||||
|
||||
@@ -154,15 +154,15 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
$assets = Asset::select('assets.*')
|
||||
->addSelect([
|
||||
'first_checkout_at' => Actionlog::query()
|
||||
->select('created_at')
|
||||
->whereColumn('item_id', 'assets.id')
|
||||
->where('item_type', Asset::class)
|
||||
->where('action_type', 'checkout')
|
||||
->orderBy('created_at')
|
||||
->limit(1),
|
||||
])
|
||||
// ->addSelect([
|
||||
// 'first_checkout_at' => Actionlog::query()
|
||||
// ->select('created_at')
|
||||
// ->whereColumn('item_id', 'assets.id')
|
||||
// ->where('item_type', Asset::class)
|
||||
// ->where('action_type', 'checkout')
|
||||
// ->orderBy('created_at')
|
||||
// ->limit(1),
|
||||
// ])
|
||||
->with(
|
||||
'model',
|
||||
'location',
|
||||
@@ -820,7 +820,10 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
if (isset($target)) {
|
||||
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->input('name')), $location);
|
||||
// Using `->has` preserves the asset name if the name parameter was not included in request.
|
||||
$asset_name = request()->has('name') ? request('name') : $asset->name;
|
||||
|
||||
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', $asset_name, $location);
|
||||
}
|
||||
|
||||
if ($asset->image) {
|
||||
@@ -1145,7 +1148,9 @@ class AssetsController extends Controller
|
||||
$payload = [
|
||||
'id' => $asset->id,
|
||||
'asset_tag' => $asset->asset_tag,
|
||||
'note' => $request->input('note'),
|
||||
'note' => e($request->input('note')),
|
||||
'status_label' => e($asset->assetstatus->display_name),
|
||||
'status_type' => $asset->assetstatus->getStatuslabelType(),
|
||||
'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date),
|
||||
];
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class ComponentsController extends Controller
|
||||
|
||||
$components = Component::select('components.*')
|
||||
->with('company', 'location', 'category', 'supplier', 'adminuser', 'manufacturer')
|
||||
->withSum('uncontrainedAssets as sum_unconstrained_assets', 'components_assets.assigned_qty');
|
||||
->withSum('unconstrainedAssets as sum_unconstrained_assets', 'components_assets.assigned_qty');
|
||||
|
||||
$filter = [];
|
||||
|
||||
@@ -87,6 +87,10 @@ class ComponentsController extends Controller
|
||||
$components->where('components.company_id', '=', $request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('order_number')) {
|
||||
$components->where('components.order_number', '=', $request->input('order_number'));
|
||||
}
|
||||
|
||||
if ($request->filled('category_id')) {
|
||||
$components->where('category_id', '=', $request->input('category_id'));
|
||||
}
|
||||
|
||||
@@ -86,6 +86,10 @@ class ConsumablesController extends Controller
|
||||
$consumables->where('consumables.company_id', '=', $request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('order_number')) {
|
||||
$consumables->where('consumables.order_number', '=', $request->input('order_number'));
|
||||
}
|
||||
|
||||
if ($request->filled('category_id')) {
|
||||
$consumables->where('category_id', '=', $request->input('category_id'));
|
||||
}
|
||||
|
||||
@@ -98,24 +98,52 @@ class LicenseSeatsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $licenseId, $seatId) : JsonResponse | array
|
||||
{
|
||||
$validated = $this->validate($request, [
|
||||
'assigned_to' => [
|
||||
'sometimes',
|
||||
'int',
|
||||
'nullable',
|
||||
'prohibits:asset_id',
|
||||
// must be a valid user or null to unassign
|
||||
function ($attribute, $value, $fail) {
|
||||
if (!is_null($value) && !User::where('id', $value)->whereNull('deleted_at')->exists()) {
|
||||
$fail('The selected assigned_to is invalid.');
|
||||
}
|
||||
},
|
||||
],
|
||||
'asset_id' => [
|
||||
'sometimes',
|
||||
'int',
|
||||
'nullable',
|
||||
'prohibits:assigned_to',
|
||||
// must be a valid asset or null to unassign
|
||||
function ($attribute, $value, $fail) {
|
||||
if (!is_null($value) && !Asset::where('id', $value)->whereNull('deleted_at')->exists()) {
|
||||
$fail('The selected asset_id is invalid.');
|
||||
}
|
||||
},
|
||||
],
|
||||
'notes' => 'sometimes|string|nullable',
|
||||
]);
|
||||
|
||||
$this->authorize('checkout', License::class);
|
||||
|
||||
$licenseSeat = LicenseSeat::with(['license', 'asset', 'user'])->find($seatId);
|
||||
|
||||
if (! $licenseSeat = LicenseSeat::find($seatId)) {
|
||||
if (!$licenseSeat) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat not found'));
|
||||
}
|
||||
|
||||
$license = $licenseSeat->license()->first();
|
||||
$license = $licenseSeat->license;
|
||||
if (!$license || $license->id != intval($licenseId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat does not belong to the specified license'));
|
||||
}
|
||||
|
||||
$oldUser = $licenseSeat->user()->first();
|
||||
$oldAsset = $licenseSeat->asset()->first();
|
||||
$oldUser = $licenseSeat->user;
|
||||
$oldAsset = $licenseSeat->asset;
|
||||
|
||||
// attempt to update the license seat
|
||||
$licenseSeat->fill($request->all());
|
||||
$licenseSeat->created_by = auth()->id();
|
||||
$licenseSeat->fill($validated);
|
||||
|
||||
// check if this update is a checkin operation
|
||||
// 1. are relevant fields touched at all?
|
||||
@@ -140,12 +168,17 @@ class LicenseSeatsController extends Controller
|
||||
if ($licenseSeat->isDirty('assigned_to')) {
|
||||
$target = $is_checkin ? $oldUser : User::find($licenseSeat->assigned_to);
|
||||
}
|
||||
|
||||
if ($licenseSeat->isDirty('asset_id')) {
|
||||
$target = $is_checkin ? $oldAsset : Asset::find($licenseSeat->asset_id);
|
||||
}
|
||||
|
||||
if ($assignmentTouched && is_null($target)){
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Target not found'));
|
||||
// if both asset_id and assigned_to are null then we are "checking-in"
|
||||
// a related model that does not exist (possible purged or bad data).
|
||||
if (!is_null($request->input('asset_id')) || !is_null($request->input('assigned_to'))) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Target not found'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($licenseSeat->save()) {
|
||||
@@ -155,6 +188,7 @@ class LicenseSeatsController extends Controller
|
||||
$licenseSeat->unreassignable_seat = true;
|
||||
$licenseSeat->save();
|
||||
}
|
||||
// todo: skip if target is null?
|
||||
$licenseSeat->logCheckin($target, $licenseSeat->notes);
|
||||
} else {
|
||||
// in this case, relevant fields are touched but it's not a checkin operation. so it must be a checkout operation.
|
||||
|
||||
@@ -32,7 +32,7 @@ class LicensesController extends Controller
|
||||
$licenses->ExpiredLicenses();
|
||||
} elseif ($request->input('status')=='expiring') {
|
||||
$licenses->ExpiringLicenses($settings->alert_interval);
|
||||
} else {
|
||||
} elseif ($request->input('status')=='active') {
|
||||
$licenses->ActiveLicenses();
|
||||
}
|
||||
|
||||
|
||||
@@ -82,6 +82,8 @@ class MaintenancesController extends Controller
|
||||
'location',
|
||||
'is_warranty',
|
||||
'status_label',
|
||||
'model',
|
||||
'model_number',
|
||||
];
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
@@ -100,6 +102,12 @@ class MaintenancesController extends Controller
|
||||
case 'asset_name':
|
||||
$maintenances = $maintenances->OrderByAssetName($order);
|
||||
break;
|
||||
case 'model':
|
||||
$maintenances = $maintenances->OrderByAssetModelName($order);
|
||||
break;
|
||||
case 'model_number':
|
||||
$maintenances = $maintenances->OrderByAssetModelNumber($order);
|
||||
break;
|
||||
case 'serial':
|
||||
$maintenances = $maintenances->OrderByAssetSerial($order);
|
||||
break;
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\ProfileTransformer;
|
||||
use App\Models\CheckoutRequest;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -182,19 +183,22 @@ class ProfileController extends Controller
|
||||
*/
|
||||
public function eulas(ProfileTransformer $transformer, Request $request)
|
||||
{
|
||||
if($request->filled('user_id') && $request->input('user_id') != 0) {
|
||||
// Return selected user's EULAs
|
||||
$eulas = User::find($request->input('user_id'))->eulas;
|
||||
}
|
||||
else {
|
||||
// Only return this user's EULAs
|
||||
|
||||
if (($request->filled('user_id')) && ($request->input( 'user_id') != 0)) {
|
||||
|
||||
$eula_user = User::find($request->input('user_id'));
|
||||
|
||||
if (($eula_user) && (Setting::getSettings()->manager_view_enabled) && (auth()->user()->isManagerOf($eula_user))) {
|
||||
$eulas = $eula_user->eulas;
|
||||
} else {
|
||||
return response()->json(Helper:: formatStandardApiResponse('error', null, trans('admin/users/message.user_not_found')));
|
||||
}
|
||||
} else {
|
||||
$eulas = auth()->user()->eulas;
|
||||
}
|
||||
|
||||
return response()->json(
|
||||
$transformer->transformFiles($eulas, $eulas->count())
|
||||
);
|
||||
return response()->json($transformer->transformFiles($eulas, $eulas->count()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -478,12 +478,22 @@ class UsersController extends Controller
|
||||
|
||||
}
|
||||
|
||||
if ($request->filled('groups')) {
|
||||
|
||||
if (($request->has('groups')) && (auth()->user()->isSuperUser())) {
|
||||
|
||||
$validator = Validator::make($request->only('groups'), [
|
||||
'groups.*' => 'integer|exists:permission_groups,id',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()));
|
||||
}
|
||||
|
||||
// Sync the groups since the user is a superuser and the groups pass validation
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
} else {
|
||||
$user->groups()->sync([]);
|
||||
}
|
||||
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.create')));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers\Assets;
|
||||
|
||||
use App\Events\CheckoutablesCheckedOutInBulk;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\CheckInOutRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
@@ -12,6 +13,7 @@ use App\Models\Setting;
|
||||
use App\View\Label;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Context;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
@@ -644,6 +646,8 @@ class BulkAssetsController extends Controller
|
||||
*/
|
||||
public function storeCheckout(AssetCheckoutRequest $request) : RedirectResponse | ModelNotFoundException
|
||||
{
|
||||
Context::add('action', 'bulk_asset_checkout');
|
||||
|
||||
$this->authorize('checkout', Asset::class);
|
||||
|
||||
try {
|
||||
@@ -730,6 +734,15 @@ class BulkAssetsController extends Controller
|
||||
});
|
||||
|
||||
if (! $errors) {
|
||||
CheckoutablesCheckedOutInBulk::dispatch(
|
||||
$assets,
|
||||
$target,
|
||||
$admin,
|
||||
$checkout_at,
|
||||
$expected_checkin,
|
||||
e($request->get('note')),
|
||||
);
|
||||
|
||||
// Redirect to the new asset page
|
||||
return redirect()->to('hardware')->with('success', trans_choice('admin/hardware/message.multi-checkout.success', $asset_ids));
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Company;
|
||||
use App\Models\Component;
|
||||
use App\Helpers\Helper;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@@ -226,6 +226,20 @@ class ComponentsController extends Controller
|
||||
public function show(Component $component)
|
||||
{
|
||||
$this->authorize('view', $component);
|
||||
return view('components/view', compact('component'));
|
||||
return view('components/view', compact('component'))->with('snipe_component', $component);
|
||||
}
|
||||
|
||||
public function getClone(Component $component) : View | RedirectResponse
|
||||
{
|
||||
$this->authorize('create', Component::class);
|
||||
|
||||
$cloned_component = clone $component;
|
||||
$cloned_component->id = null;
|
||||
$cloned_component->deleted_at = null;
|
||||
|
||||
// Show the page
|
||||
return view('components/edit')
|
||||
->with('item', $cloned_component)
|
||||
->with('component', $cloned_component);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,15 +32,16 @@ use App\Models\Location;
|
||||
use App\Models\Maintenance;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Traits\DisablesDebugbar;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
abstract class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||
use AuthorizesRequests, DisablesDebugbar, DispatchesJobs, ValidatesRequests;
|
||||
|
||||
static $map_object_type = [
|
||||
'accessories' => Accessory::class,
|
||||
|
||||
@@ -74,7 +74,7 @@ class CustomFieldsetsController extends Controller
|
||||
{
|
||||
$this->authorize('create', CustomField::class);
|
||||
|
||||
return view('custom_fields.fieldsets.edit')->with('item', new CustomFieldset());
|
||||
return view('custom_fields.fieldsets.view')->with('custom_fieldset', new CustomFieldset());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,7 +127,7 @@ class CustomFieldsetsController extends Controller
|
||||
public function edit(CustomFieldset $fieldset) : View | RedirectResponse
|
||||
{
|
||||
$this->authorize('create', CustomField::class);
|
||||
return view('custom_fields.fieldsets.edit')->with('item', $fieldset);
|
||||
return view('custom_fields.fieldsets.view')->with('custom_fieldset', $fieldset);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,23 +28,41 @@ class HealthController extends BaseController
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
|
||||
try {
|
||||
|
||||
if (DB::select('select 2 + 2')) {
|
||||
return response()->json([
|
||||
'status' => 'ok',
|
||||
]);
|
||||
$db_status = 'ok';
|
||||
} else {
|
||||
$db_status = 'Could not connect to database';
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
\Log::error('Could not connect to database');
|
||||
return response()->json([
|
||||
'status' => 'database connection failed',
|
||||
], 500);
|
||||
$db_status = 'Could not connect to database';
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (is_writable(storage_path('logs'))) {
|
||||
$filesystem_status = 'ok';
|
||||
} else {
|
||||
$filesystem_status = 'Could not write to storage/logs';
|
||||
}
|
||||
|
||||
if (($filesystem_status!='ok') || ($db_status!='ok')) {
|
||||
return response()->json([
|
||||
'status' =>
|
||||
[
|
||||
'database' => $db_status,
|
||||
'filesystem' => $filesystem_status,
|
||||
]
|
||||
], 500);
|
||||
}
|
||||
|
||||
|
||||
return response()->json([
|
||||
'status' => 'ok',
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,7 +315,8 @@ class LicensesController extends Controller
|
||||
public function getExportLicensesCsv()
|
||||
{
|
||||
$this->authorize('view', License::class);
|
||||
\Debugbar::disable();
|
||||
|
||||
$this->disableDebugbar();
|
||||
|
||||
$response = new StreamedResponse(function () {
|
||||
// Open output stream
|
||||
|
||||
@@ -38,7 +38,7 @@ class ModalController extends Controller
|
||||
|
||||
|
||||
if (in_array($type, $allowed_types)) {
|
||||
$view = view("modals.${type}");
|
||||
$view = view("modals.{$type}");
|
||||
|
||||
if ($type == "statuslabel") {
|
||||
$view->with('statuslabel_types', Helper::statusTypeList());
|
||||
|
||||
@@ -242,7 +242,8 @@ class ReportsController extends Controller
|
||||
ini_set('max_execution_time', 12000);
|
||||
$this->authorize('reports.view');
|
||||
|
||||
\Debugbar::disable();
|
||||
$this->disableDebugbar();
|
||||
|
||||
$response = new StreamedResponse(function () {
|
||||
Log::debug('Starting streamed response');
|
||||
|
||||
@@ -437,8 +438,8 @@ class ReportsController extends Controller
|
||||
ini_set('max_execution_time', env('REPORT_TIME_LIMIT', 12000)); //12000 seconds = 200 minutes
|
||||
$this->authorize('reports.view');
|
||||
|
||||
$this->disableDebugbar();
|
||||
|
||||
\Debugbar::disable();
|
||||
$customfields = CustomField::get();
|
||||
$response = new StreamedResponse(function () use ($customfields, $request) {
|
||||
Log::debug('Starting streamed response');
|
||||
@@ -549,6 +550,10 @@ class ReportsController extends Controller
|
||||
$header[] = 'Username';
|
||||
}
|
||||
|
||||
if ($request->filled('email')) {
|
||||
$header[] = 'Email';
|
||||
}
|
||||
|
||||
if ($request->filled('employee_num')) {
|
||||
$header[] = 'Employee No.';
|
||||
}
|
||||
@@ -882,6 +887,15 @@ class ReportsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('email')) {
|
||||
// Only works if we're checked out to a user, not anything else.
|
||||
if ($asset->checkedOutToUser()) {
|
||||
$row[] = ($asset->assignedto) ? $asset->assignedto->email : '';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('employee_num')) {
|
||||
// Only works if we're checked out to a user, not anything else.
|
||||
if ($asset->checkedOutToUser()) {
|
||||
@@ -1228,12 +1242,14 @@ class ReportsController extends Controller
|
||||
];
|
||||
$mailable= $lookup[get_class($acceptance->checkoutable)];
|
||||
|
||||
return new $mailable($acceptance->checkoutable,
|
||||
return new $mailable(
|
||||
$acceptance->checkoutable,
|
||||
$acceptance->checkedOutTo ?? $acceptance->assignedTo,
|
||||
$logItem->adminuser,
|
||||
$acceptance,
|
||||
$acceptance->note);
|
||||
|
||||
$acceptance->note,
|
||||
firstTimeSending: false,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* sentAssetAcceptanceReminder
|
||||
|
||||
@@ -403,24 +403,23 @@ class SettingsController extends Controller
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
}
|
||||
|
||||
// Check if the audit interval has changed - if it has, we want to update ALL of the assets audit dates
|
||||
if ($request->input('audit_interval') != $setting->audit_interval) {
|
||||
|
||||
// This could be a negative number if the user is trying to set the audit interval to a lower number than it was before
|
||||
$audit_diff_months = ((int)$request->input('audit_interval') - (int)($setting->audit_interval));
|
||||
// Check if the audit interval has changed - if it has, check if we should update all of the assets audit dates
|
||||
if ((($request->input('audit_interval') != $setting->audit_interval)) && ($request->input('update_existing_dates') == 1)) {
|
||||
|
||||
// Batch update the dates. We have to use this method to avoid time limit exceeded errors on very large datasets,
|
||||
// but it DOES mean this change doesn't get logged in the action logs, since it skips the observer.
|
||||
// @see https://stackoverflow.com/questions/54879160/laravel-observer-not-working-on-bulk-insert
|
||||
$affected = Asset::whereNotNull('next_audit_date')
|
||||
->whereNull('deleted_at')
|
||||
->update(
|
||||
['next_audit_date' => DB::raw('DATE_ADD(next_audit_date, INTERVAL '.$audit_diff_months.' MONTH)')]
|
||||
);
|
||||
|
||||
Log::debug($affected .' assets affected by audit interval update');
|
||||
// This could be a negative number if the user is trying to set the audit interval to a lower number than it was before
|
||||
$audit_diff_months = ((int)$request->input('audit_interval') - (int)($setting->audit_interval));
|
||||
|
||||
// Batch update the dates. We have to use this method to avoid time limit exceeded errors on very large datasets,
|
||||
// but it DOES mean this change doesn't get logged in the action logs, since it skips the observer.
|
||||
// @see https://stackoverflow.com/questions/54879160/laravel-observer-not-working-on-bulk-insert
|
||||
$affected = Asset::whereNotNull('next_audit_date')
|
||||
->whereNull('deleted_at')
|
||||
->update(
|
||||
['next_audit_date' => DB::raw('DATE_ADD(next_audit_date, INTERVAL ' . $audit_diff_months . ' MONTH)')]
|
||||
);
|
||||
|
||||
Log::debug($affected . ' assets affected by audit interval update');
|
||||
}
|
||||
|
||||
$alert_email = rtrim($request->input('alert_email'), ',');
|
||||
|
||||
@@ -510,7 +510,8 @@ class UsersController extends Controller
|
||||
public function getExportUserCsv()
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
\Debugbar::disable();
|
||||
|
||||
$this->disableDebugbar();
|
||||
|
||||
$response = new StreamedResponse(function () {
|
||||
// Open output stream
|
||||
|
||||
@@ -206,7 +206,7 @@ class ViewAssetsController extends Controller
|
||||
if ($fullItemType == Asset::class) {
|
||||
$data['item_url'] = route('hardware.show', $item->id);
|
||||
} else {
|
||||
$data['item_url'] = route("view/${itemType}", $item->id);
|
||||
$data['item_url'] = route("view/{$itemType}", $item->id);
|
||||
}
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
@@ -34,7 +34,7 @@ class SaveUserRequest extends FormRequest
|
||||
{
|
||||
$rules = [
|
||||
'department_id' => 'nullable|integer|exists:departments,id',
|
||||
'manager_id' => 'nullable|exists:users,id',
|
||||
'manager_id' => 'nullable|integer|exists:users,id',
|
||||
'company_id' => ['nullable', 'integer', 'exists:companies,id']
|
||||
];
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ class AssetsTransformer
|
||||
'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date, 'date'),
|
||||
'deleted_at' => Helper::getFormattedDateObject($asset->deleted_at, 'datetime'),
|
||||
'purchase_date' => Helper::getFormattedDateObject($asset->purchase_date, 'date'),
|
||||
'first_checkout' => Helper::getFormattedDateObject($asset->first_checkout_at, 'datetime'),
|
||||
// 'first_checkout' => Helper::getFormattedDateObject($asset->first_checkout_at, 'datetime'),
|
||||
'age' => $asset->purchase_date ? $asset->purchase_date->locale(app()->getLocale())->diffForHumans() : '',
|
||||
'last_checkout' => Helper::getFormattedDateObject($asset->last_checkout, 'datetime'),
|
||||
'last_checkin' => Helper::getFormattedDateObject($asset->last_checkin, 'datetime'),
|
||||
@@ -211,7 +211,7 @@ class AssetsTransformer
|
||||
return $asset->assigned ? [
|
||||
'id' => (int) $asset->assigned->id,
|
||||
'username' => e($asset->assigned->username),
|
||||
'name' => e($asset->assigned->getFullNameAttribute()),
|
||||
'name' => e($asset->assigned->display_name),
|
||||
'first_name'=> e($asset->assigned->first_name),
|
||||
'last_name'=> ($asset->assigned->last_name) ? e($asset->assigned->last_name) : null,
|
||||
'email'=> ($asset->assigned->email) ? e($asset->assigned->email) : null,
|
||||
|
||||
@@ -74,6 +74,7 @@ class ComponentsTransformer
|
||||
'checkout' => Gate::allows('checkout', Component::class),
|
||||
'checkin' => Gate::allows('checkin', Component::class),
|
||||
'update' => Gate::allows('update', Component::class),
|
||||
'clone' => Gate::allows('create', Component::class),
|
||||
'delete' => $component->isDeletable(),
|
||||
];
|
||||
$array += $permissions_array;
|
||||
|
||||
@@ -44,6 +44,7 @@ class CustomFieldsTransformer
|
||||
'db_column_name' => e($field->db_column_name()),
|
||||
'format' => e($field->format),
|
||||
'field_values' => ($field->field_values) ? e($field->field_values) : null,
|
||||
'field_encrypted' => $field->field_encrypted,
|
||||
'field_values_array' => ($field->field_values) ? explode("\r\n", e($field->field_values)) : null,
|
||||
'type' => e($field->element),
|
||||
'required' => (($field->pivot) && ($field->pivot->required=='1')) ? true : false,
|
||||
|
||||
@@ -37,7 +37,8 @@ class MaintenancesTransformer
|
||||
'image' => ($assetmaintenance->image != '') ? Storage::disk('public')->url('maintenances/'.e($assetmaintenance->image)) : null,
|
||||
'model' => (($assetmaintenance->asset) && ($assetmaintenance->asset->model)) ? [
|
||||
'id' => (int) $assetmaintenance->asset->model->id,
|
||||
'name'=> ($assetmaintenance->asset->model->name) ? e($assetmaintenance->asset->model->name).' '.e($assetmaintenance->asset->model->model_number) : null,
|
||||
'name'=> ($assetmaintenance->asset->model->name) ? e($assetmaintenance->asset->model->name) : null,
|
||||
'model_number'=> ($assetmaintenance->asset->model->model_number) ? e($assetmaintenance->asset->model->model_number) : null,
|
||||
] : null,
|
||||
'status_label' => (($assetmaintenance->asset) && ($assetmaintenance->asset->assetstatus)) ? [
|
||||
'id' => (int) $assetmaintenance->asset->assetstatus->id,
|
||||
|
||||
@@ -40,7 +40,7 @@ class StatuslabelsTransformer
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'update' => Gate::allows('update', Statuslabel::class) ? true : false,
|
||||
'delete' => (Gate::allows('delete', Statuslabel::class) && ($statuslabel->assets_count == 0)) ? true : false,
|
||||
'delete' => (Gate::allows('delete', Statuslabel::class) && ($statuslabel->isDeletable())) ? true : false,
|
||||
];
|
||||
$array += $permissions_array;
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ class SuppliersTransformer
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'update' => Gate::allows('update', Supplier::class),
|
||||
'delete' => (Gate::allows('delete', Supplier::class) && ($supplier->assets_count == 0) && ($supplier->licenses_count == 0) && ($supplier->accessories_count == 0)),
|
||||
'delete' => (Gate::allows('delete', Supplier::class) && ($supplier->isDeletable())),
|
||||
];
|
||||
|
||||
$array += $permissions_array;
|
||||
|
||||
@@ -141,7 +141,7 @@ class UsersTransformer
|
||||
'id' => (int) $user->id,
|
||||
'image' => e($user->present()->gravatar) ?? null,
|
||||
'type' => 'user',
|
||||
'name' => e($user->getFullNameAttribute()),
|
||||
'name' => e($user->display_name),
|
||||
'first_name' => e($user->first_name),
|
||||
'last_name' => e($user->last_name),
|
||||
'username' => e($user->username),
|
||||
|
||||
@@ -70,6 +70,7 @@ class CategoryImporter extends ItemImporter
|
||||
$this->item['use_default_eula'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'use_default_eula'))) == 1) ? 1 : 0;
|
||||
$this->item['require_acceptance'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'require_acceptance'))) == 1) ? 1 : 0;
|
||||
$this->item['checkin_email'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'checkin_email'))) == 1) ? 1 : 0;
|
||||
$this->item['tag_color'] = trim($this->findCsvMatch($row, 'tag_color'));
|
||||
|
||||
|
||||
Log::debug('Item array is: ');
|
||||
|
||||
@@ -72,6 +72,7 @@ class ManufacturerImporter extends ItemImporter
|
||||
$this->item['support_url'] = trim($this->findCsvMatch($row, 'support_url'));
|
||||
$this->item['warranty_lookup_url'] = trim($this->findCsvMatch($row, 'warranty_lookup_url'));
|
||||
$this->item['notes'] = trim($this->findCsvMatch($row, 'notes'));
|
||||
$this->item['tag_color'] = trim($this->findCsvMatch($row, 'tag_color'));
|
||||
|
||||
|
||||
Log::debug('Item array is: ');
|
||||
|
||||
@@ -76,6 +76,7 @@ class SupplierImporter extends ItemImporter
|
||||
$this->item['contact'] = trim($this->findCsvMatch($row, 'contact'));
|
||||
$this->item['url'] = trim($this->findCsvMatch($row, 'url'));
|
||||
$this->item['notes'] = trim($this->findCsvMatch($row, 'notes'));
|
||||
$this->item['tag_color'] = trim($this->findCsvMatch($row, 'tag_color'));
|
||||
|
||||
|
||||
Log::debug('Item array is: ');
|
||||
|
||||
@@ -33,6 +33,7 @@ use App\Notifications\CheckoutConsumableNotification;
|
||||
use App\Notifications\CheckoutLicenseSeatNotification;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Context;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Exception;
|
||||
@@ -128,7 +129,7 @@ class CheckoutableListener
|
||||
->notify($this->getCheckoutNotification($event, $acceptance));
|
||||
}
|
||||
} catch (ClientException $e) {
|
||||
$status = optional($e->getResponse()->getStatusCode());
|
||||
$status = $e->getResponse()->getStatusCode();
|
||||
|
||||
if (strpos($e->getMessage(), 'channel_not_found') !== false) {
|
||||
Log::warning(Setting::getSettings()->webhook_selected . " notification failed: " . $e->getMessage());
|
||||
@@ -231,7 +232,7 @@ class CheckoutableListener
|
||||
->notify($this->getCheckinNotification($event));
|
||||
}
|
||||
} catch (ClientException $e) {
|
||||
$status = optional($e->getResponse()->getStatusCode());
|
||||
$status = $e->getResponse()->getStatusCode();
|
||||
|
||||
if (strpos($e->getMessage(), 'channel_not_found') !== false) {
|
||||
Log::warning(Setting::getSettings()->webhook_selected . " notification failed: " . $e->getMessage());
|
||||
@@ -441,12 +442,17 @@ class CheckoutableListener
|
||||
private function shouldSendCheckoutEmailToUser(Model $checkoutable): bool
|
||||
{
|
||||
/**
|
||||
* Send an email if any of the following conditions are met:
|
||||
* Send an email if we didn't get here from a bulk checkout
|
||||
* and any of the following conditions are met:
|
||||
* 1. The asset requires acceptance
|
||||
* 2. The item has a EULA
|
||||
* 3. The item should send an email at check-in/check-out
|
||||
*/
|
||||
|
||||
if (Context::get('action') === 'bulk_asset_checkout') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($checkoutable->requireAcceptance()) {
|
||||
return true;
|
||||
}
|
||||
@@ -464,6 +470,10 @@ class CheckoutableListener
|
||||
|
||||
private function shouldSendEmailToAlertAddress($acceptance = null): bool
|
||||
{
|
||||
if (Context::get('action') === 'bulk_asset_checkout') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$setting = Setting::getSettings();
|
||||
|
||||
if (!$setting) {
|
||||
|
||||
154
app/Listeners/CheckoutablesCheckedOutInBulkListener.php
Normal file
154
app/Listeners/CheckoutablesCheckedOutInBulkListener.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Events\CheckoutablesCheckedOutInBulk;
|
||||
use App\Mail\BulkAssetCheckoutMail;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Location;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class CheckoutablesCheckedOutInBulkListener
|
||||
{
|
||||
|
||||
public function subscribe($events)
|
||||
{
|
||||
$events->listen(
|
||||
CheckoutablesCheckedOutInBulk::class,
|
||||
CheckoutablesCheckedOutInBulkListener::class
|
||||
);
|
||||
}
|
||||
|
||||
public function handle(CheckoutablesCheckedOutInBulk $event): void
|
||||
{
|
||||
$notifiableUser = $this->getNotifiableUser($event);
|
||||
|
||||
$shouldSendEmailToUser = $this->shouldSendCheckoutEmailToUser($notifiableUser, $event->assets);
|
||||
$shouldSendEmailToAlertAddress = $this->shouldSendEmailToAlertAddress($event->assets);
|
||||
|
||||
if ($shouldSendEmailToUser && $notifiableUser) {
|
||||
try {
|
||||
Mail::to($notifiableUser)->send(new BulkAssetCheckoutMail(
|
||||
$event->assets,
|
||||
$event->target,
|
||||
$event->admin,
|
||||
$event->checkout_at,
|
||||
$event->expected_checkin,
|
||||
$event->note,
|
||||
));
|
||||
|
||||
Log::info('BulkAssetCheckoutMail sent to checkout target');
|
||||
} catch (Exception $e) {
|
||||
Log::debug("Exception caught during BulkAssetCheckoutMail to target: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if ($shouldSendEmailToAlertAddress && Setting::getSettings()->admin_cc_email) {
|
||||
try {
|
||||
Mail::to(Setting::getSettings()->admin_cc_email)->send(new BulkAssetCheckoutMail(
|
||||
$event->assets,
|
||||
$event->target,
|
||||
$event->admin,
|
||||
$event->checkout_at,
|
||||
$event->expected_checkin,
|
||||
$event->note,
|
||||
));
|
||||
|
||||
Log::info('BulkAssetCheckoutMail sent to admin_cc_email');
|
||||
} catch (Exception $e) {
|
||||
Log::debug("Exception caught during BulkAssetCheckoutMail to admin_cc_email: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function shouldSendCheckoutEmailToUser(?User $user, Collection $assets): bool
|
||||
{
|
||||
if (!$user?->email) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->hasAssetWithEula($assets)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->hasAssetWithCategorySettingToSendEmail($assets)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->hasAssetThatRequiresAcceptance($assets);
|
||||
}
|
||||
|
||||
private function shouldSendEmailToAlertAddress(Collection $assets): bool
|
||||
{
|
||||
$setting = Setting::getSettings();
|
||||
|
||||
if (!$setting) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($setting->admin_cc_always) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$this->hasAssetThatRequiresAcceptance($assets)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) $setting->admin_cc_email;
|
||||
}
|
||||
|
||||
private function hasAssetWithEula(Collection $assets): bool
|
||||
{
|
||||
foreach ($assets as $asset) {
|
||||
if ($asset->getEula()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function hasAssetWithCategorySettingToSendEmail(Collection $assets): bool
|
||||
{
|
||||
foreach ($assets as $asset) {
|
||||
if ($asset->checkin_email()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function hasAssetThatRequiresAcceptance(Collection $assets): bool
|
||||
{
|
||||
foreach ($assets as $asset) {
|
||||
if ($asset->requireAcceptance()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getNotifiableUser(CheckoutablesCheckedOutInBulk $event): ?Model
|
||||
{
|
||||
$target = $event->target;
|
||||
|
||||
if ($target instanceof Asset) {
|
||||
$target->load('assignedTo');
|
||||
return $target->assignedto;
|
||||
}
|
||||
|
||||
if ($target instanceof Location) {
|
||||
return $target->manager;
|
||||
}
|
||||
|
||||
return $target;
|
||||
}
|
||||
}
|
||||
@@ -150,10 +150,15 @@ class Importer extends Component
|
||||
// if you got here, we didn't find a match. Try the $aliases_fields
|
||||
foreach ($this->aliases_fields as $key => $alias_values) {
|
||||
foreach ($alias_values as $alias_value) {
|
||||
|
||||
// Trim off any trailing spaces
|
||||
$key = trim($key);
|
||||
$header = trim($header);
|
||||
if (strcasecmp($alias_value, $header) === 0) { // aLsO CaSe-INSENSitiVE!
|
||||
// Make *absolutely* sure that this key actually _exists_ in this import type -
|
||||
// you can trigger this by importing accessories with a 'Warranty' column (which don't exist
|
||||
// in "Accessories"!)
|
||||
|
||||
if (array_key_exists($key, $this->columnOptions[$type])) {
|
||||
$this->field_map[$i] = $key;
|
||||
continue 3; // bust out of both of these loops and the surrounding one - e.g. move on to the next header
|
||||
@@ -262,7 +267,7 @@ class Importer extends Component
|
||||
'order_number' => trans('general.order_number'),
|
||||
'purchase_cost' => trans('general.purchase_cost'),
|
||||
'purchase_date' => trans('general.purchase_date'),
|
||||
'quantity' => trans('general.qty'),
|
||||
'qty' => trans('general.qty'),
|
||||
'supplier' => trans('general.supplier'),
|
||||
];
|
||||
|
||||
@@ -278,7 +283,7 @@ class Importer extends Component
|
||||
'order_number' => trans('general.order_number'),
|
||||
'purchase_cost' => trans('general.purchase_cost'),
|
||||
'purchase_date' => trans('general.purchase_date'),
|
||||
'quantity' => trans('general.qty'),
|
||||
'qty' => trans('general.qty'),
|
||||
'serial' => trans('general.serial_number'),
|
||||
'supplier' => trans('general.supplier'),
|
||||
];
|
||||
@@ -372,12 +377,14 @@ class Importer extends Component
|
||||
'city' => trans('general.city'),
|
||||
'notes' => trans('general.notes'),
|
||||
'state' => trans('general.state'),
|
||||
'country' => trans('general.country'),
|
||||
'zip' => trans('general.zip'),
|
||||
'phone' => trans('general.phone'),
|
||||
'fax' => trans('general.fax'),
|
||||
'url' => trans('general.url'),
|
||||
'contact' => trans('general.contact'),
|
||||
'email' => trans('general.email'),
|
||||
'tag_color' => trans('general.tag_color'),
|
||||
];
|
||||
|
||||
$this->manufacturers_fields = [
|
||||
@@ -389,6 +396,7 @@ class Importer extends Component
|
||||
'support_email' => trans('admin/manufacturers/table.support_email'),
|
||||
'warranty_lookup_url' => trans('admin/manufacturers/table.warranty_lookup_url'),
|
||||
'url' => trans('general.url'),
|
||||
'tag_color' => trans('general.tag_color'),
|
||||
];
|
||||
|
||||
$this->categories_fields = [
|
||||
@@ -400,6 +408,8 @@ class Importer extends Component
|
||||
'use_default_eula' => trans('admin/categories/general.use_default_eula_column'),
|
||||
'require_acceptance' => trans('admin/categories/general.import_require_acceptance'),
|
||||
'checkin_email' => trans('admin/categories/general.import_checkin_email'),
|
||||
'alert_on_response' => trans('admin/categories/general.import_alert_on_response'),
|
||||
'tag_color' => trans('general.tag_color'),
|
||||
];
|
||||
|
||||
|
||||
@@ -409,22 +419,32 @@ class Importer extends Component
|
||||
'category' => trans('general.category'),
|
||||
'eol' => trans('general.eol'),
|
||||
'fieldset' => trans('admin/models/general.fieldset'),
|
||||
'item_name' => trans('general.item_name_var', ['item' => trans('general.asset_model')]),
|
||||
'name' => trans('general.name'),
|
||||
'manufacturer' => trans('general.manufacturer'),
|
||||
'min_amt' => trans('mail.min_QTY'),
|
||||
'model_number' => trans('general.model_no'),
|
||||
'notes' => trans('general.item_notes', ['item' => trans('admin/hardware/form.model')]),
|
||||
'requestable' => trans('admin/models/general.requestable'),
|
||||
'notes' => trans('general.notes'),
|
||||
'requestable' => trans('general.requestable'),
|
||||
'require_serial' => trans('admin/hardware/general.require_serial'),
|
||||
|
||||
'tag_color' => trans('general.tag_color'),
|
||||
'depreciation' => trans('general.depreciation'),
|
||||
];
|
||||
|
||||
// "real fieldnames" to a list of aliases for that field
|
||||
/**
|
||||
* These are the "real fieldnames" with a list of possible aliases,
|
||||
* like misspellings, slight mis-phrasings, user-specific language, etc. that
|
||||
* could be in the imported file header.
|
||||
* This just makes the user's experience a little better when they're using
|
||||
* their own CSV template.
|
||||
*/
|
||||
|
||||
$this->aliases_fields = [
|
||||
'item_name' =>
|
||||
[
|
||||
'item name',
|
||||
'asset name',
|
||||
'model name',
|
||||
'asset model name',
|
||||
'accessory name',
|
||||
'user name',
|
||||
'consumable name',
|
||||
@@ -438,6 +458,20 @@ class Importer extends Component
|
||||
'item no.',
|
||||
'item #',
|
||||
],
|
||||
'order_number' => [
|
||||
'order #',
|
||||
'order no.',
|
||||
'order num',
|
||||
'order number',
|
||||
'order',
|
||||
],
|
||||
'eula_text' => [
|
||||
'eula',
|
||||
],
|
||||
|
||||
'checkin_email' => [
|
||||
'checkin email',
|
||||
],
|
||||
'asset_model' =>
|
||||
[
|
||||
'model name',
|
||||
@@ -455,16 +489,6 @@ class Importer extends Component
|
||||
'EOL',
|
||||
'eol months',
|
||||
],
|
||||
'depreciation' =>
|
||||
[
|
||||
'Depreciation',
|
||||
'depreciation',
|
||||
],
|
||||
'requestable' =>
|
||||
[
|
||||
'requestable',
|
||||
'Requestable',
|
||||
],
|
||||
'gravatar' =>
|
||||
[
|
||||
'gravatar',
|
||||
@@ -541,7 +565,8 @@ class Importer extends Component
|
||||
],
|
||||
'require_serial' =>
|
||||
[
|
||||
'serial required',
|
||||
trans('admin/models/general.importer.require_serial'),
|
||||
trans('admin/models/general.importer.serial_reqiured'),
|
||||
],
|
||||
'model_number' =>
|
||||
[
|
||||
|
||||
165
app/Mail/BulkAssetCheckoutMail.php
Normal file
165
app/Mail/BulkAssetCheckoutMail.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class BulkAssetCheckoutMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public bool $requires_acceptance;
|
||||
|
||||
public Collection $assetsByCategory;
|
||||
|
||||
public function __construct(
|
||||
public Collection $assets,
|
||||
public Model $target,
|
||||
public User $admin,
|
||||
public string $checkout_at,
|
||||
public string $expected_checkin,
|
||||
public string $note,
|
||||
) {
|
||||
$this->requires_acceptance = $this->requiresAcceptance();
|
||||
|
||||
$this->loadCustomFieldsOnAssets();
|
||||
$this->loadEulasOnAssets();
|
||||
|
||||
$this->assetsByCategory = $this->groupAssetsByCategory();
|
||||
}
|
||||
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: $this->getSubject(),
|
||||
);
|
||||
}
|
||||
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'mail.markdown.bulk-asset-checkout-mail',
|
||||
with: [
|
||||
'introduction' => $this->getIntroduction(),
|
||||
'requires_acceptance' => $this->requires_acceptance,
|
||||
'requires_acceptance_info' => $this->getRequiresAcceptanceInfo(),
|
||||
'requires_acceptance_prompt' => $this->getRequiresAcceptancePrompt(),
|
||||
'singular_eula' => $this->getSingularEula(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
private function getSubject(): string
|
||||
{
|
||||
if ($this->assets->count() > 1) {
|
||||
return ucfirst(trans('general.assets_checked_out_count'));
|
||||
}
|
||||
|
||||
return trans('mail.Asset_Checkout_Notification', ['tag' => $this->assets->first()->asset_tag]);
|
||||
}
|
||||
|
||||
private function loadCustomFieldsOnAssets(): void
|
||||
{
|
||||
$this->assets = $this->assets->map(function (Asset $asset) {
|
||||
$fields = $asset->model?->fieldset?->fields->filter(function (CustomField $field) {
|
||||
return $field->show_in_email && !$field->field_encrypted;
|
||||
});
|
||||
|
||||
$asset->setRelation('fields', $fields);
|
||||
|
||||
return $asset;
|
||||
});
|
||||
}
|
||||
|
||||
private function loadEulasOnAssets(): void
|
||||
{
|
||||
$this->assets = $this->assets->map(function (Asset $asset) {
|
||||
$asset->eula = $asset->getEula();
|
||||
|
||||
return $asset;
|
||||
});
|
||||
}
|
||||
|
||||
private function groupAssetsByCategory(): Collection
|
||||
{
|
||||
return $this->assets->groupBy(fn($asset) => $asset->model->category->id);
|
||||
}
|
||||
|
||||
private function getIntroduction(): string
|
||||
{
|
||||
if ($this->target instanceof Location) {
|
||||
return trans_choice('mail.new_item_checked_location', $this->assets->count(), ['location' => $this->target->name]);
|
||||
}
|
||||
|
||||
return trans_choice('mail.new_item_checked', $this->assets->count());
|
||||
}
|
||||
|
||||
private function getRequiresAcceptanceInfo(): ?string
|
||||
{
|
||||
if (!$this->requires_acceptance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return trans_choice('mail.items_checked_out_require_acceptance', $this->assets->count());
|
||||
}
|
||||
|
||||
private function getRequiresAcceptancePrompt(): ?string
|
||||
{
|
||||
if (!$this->requires_acceptance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$acceptanceUrl = $this->assets->count() === 1
|
||||
? route('account.accept.item', $this->assets->first())
|
||||
: route('account.accept');
|
||||
|
||||
return
|
||||
sprintf(
|
||||
'**[✔ %s](%s)**',
|
||||
trans_choice('mail.click_here_to_review_terms_and_accept_item', $this->assets->count()),
|
||||
$acceptanceUrl,
|
||||
);
|
||||
}
|
||||
|
||||
private function getSingularEula()
|
||||
{
|
||||
// get unique categories from all assets
|
||||
$categories = $this->assets->pluck('model.category.id')->unique();
|
||||
|
||||
// if assets do not have the same category then return early...
|
||||
if ($categories->count() > 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// if assets do have the same category then return the shared EULA
|
||||
if ($categories->count() === 1) {
|
||||
return $this->assets->first()->getEula();
|
||||
}
|
||||
}
|
||||
|
||||
private function requiresAcceptance(): bool
|
||||
{
|
||||
foreach ($this->assets as $asset) {
|
||||
if ($asset->requireAcceptance()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,12 @@ class CheckoutAccessoryMail extends BaseMailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
private bool $firstTimeSending;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
|
||||
public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $acceptance, $note, bool $firstTimeSending = true)
|
||||
{
|
||||
$this->item = $accessory;
|
||||
$this->admin = $checkedOutBy;
|
||||
@@ -29,6 +31,7 @@ class CheckoutAccessoryMail extends BaseMailable
|
||||
$this->checkout_qty = $accessory->checkout_qty;
|
||||
$this->target = $checkedOutTo;
|
||||
$this->acceptance = $acceptance;
|
||||
$this->firstTimeSending = $firstTimeSending;
|
||||
$this->settings = Setting::getSettings();
|
||||
}
|
||||
|
||||
@@ -41,7 +44,7 @@ class CheckoutAccessoryMail extends BaseMailable
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
subject: trans_choice('mail.Accessory_Checkout_Notification', $this->checkout_qty),
|
||||
subject: $this->getSubject(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -88,14 +91,18 @@ class CheckoutAccessoryMail extends BaseMailable
|
||||
return trans_choice('mail.new_item_checked_location', $this->checkout_qty, ['location' => $this->target->name]);
|
||||
}
|
||||
|
||||
if ($this->requiresAcceptance()) {
|
||||
if ($this->firstTimeSending && $this->requiresAcceptance()) {
|
||||
return trans_choice('mail.new_item_checked_with_acceptance', $this->checkout_qty);
|
||||
}
|
||||
|
||||
if (!$this->requiresAcceptance()) {
|
||||
if ($this->firstTimeSending && !$this->requiresAcceptance()) {
|
||||
return trans_choice('mail.new_item_checked', $this->checkout_qty);
|
||||
}
|
||||
|
||||
if (!$this->firstTimeSending && $this->requiresAcceptance()) {
|
||||
return trans('mail.recent_item_checked');
|
||||
}
|
||||
|
||||
// we shouldn't get here but let's send a default message just in case
|
||||
return trans('new_item_checked');
|
||||
}
|
||||
@@ -113,4 +120,13 @@ class CheckoutAccessoryMail extends BaseMailable
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
private function getSubject(): string
|
||||
{
|
||||
if ($this->firstTimeSending) {
|
||||
return trans_choice('mail.Accessory_Checkout_Notification', $this->checkout_qty);
|
||||
}
|
||||
|
||||
return trans('mail.unaccepted_asset_reminder');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ use Illuminate\Mail\Mailables\Address;
|
||||
use Illuminate\Mail\Mailables\Attachment;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class CheckoutAssetMail extends BaseMailable
|
||||
@@ -25,7 +24,7 @@ class CheckoutAssetMail extends BaseMailable
|
||||
* Create a new message instance.
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $acceptance, $note, bool $firstTimeSending = true)
|
||||
public function __construct(Asset $asset, $checkedOutTo, ?User $checkedOutBy, $acceptance, $note, bool $firstTimeSending = true)
|
||||
{
|
||||
$this->item = $asset;
|
||||
$this->admin = $checkedOutBy;
|
||||
|
||||
@@ -15,10 +15,12 @@ class CheckoutConsumableMail extends BaseMailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
private bool $firstTimeSending;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct(Consumable $consumable, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
|
||||
public function __construct(Consumable $consumable, $checkedOutTo, User $checkedOutBy, $acceptance, $note, bool $firstTimeSending = true)
|
||||
{
|
||||
$this->item = $consumable;
|
||||
$this->admin = $checkedOutBy;
|
||||
@@ -26,6 +28,7 @@ class CheckoutConsumableMail extends BaseMailable
|
||||
$this->target = $checkedOutTo;
|
||||
$this->acceptance = $acceptance;
|
||||
$this->qty = $consumable->checkout_qty;
|
||||
$this->firstTimeSending = $firstTimeSending;
|
||||
|
||||
$this->settings = Setting::getSettings();
|
||||
}
|
||||
@@ -39,7 +42,7 @@ class CheckoutConsumableMail extends BaseMailable
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
subject: trans('mail.Confirm_consumable_delivery'),
|
||||
subject: $this->getSubject(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -65,6 +68,7 @@ class CheckoutConsumableMail extends BaseMailable
|
||||
'req_accept' => $req_accept,
|
||||
'accept_url' => $accept_url,
|
||||
'qty' => $this->qty,
|
||||
'introduction_line' => $this->introductionLine(),
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -78,4 +82,36 @@ class CheckoutConsumableMail extends BaseMailable
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
private function getSubject(): string
|
||||
{
|
||||
if ($this->firstTimeSending) {
|
||||
return trans('mail.Confirm_consumable_delivery');
|
||||
}
|
||||
|
||||
return trans('mail.unaccepted_asset_reminder');
|
||||
}
|
||||
|
||||
private function introductionLine()
|
||||
{
|
||||
if ($this->firstTimeSending && $this->requiresAcceptance()) {
|
||||
return trans_choice('mail.new_item_checked_with_acceptance', $this->qty);
|
||||
}
|
||||
|
||||
if ($this->firstTimeSending && !$this->requiresAcceptance()) {
|
||||
return trans_choice('mail.new_item_checked', $this->qty);
|
||||
}
|
||||
|
||||
if (!$this->firstTimeSending && $this->requiresAcceptance()) {
|
||||
return trans('mail.recent_item_checked');
|
||||
}
|
||||
|
||||
// we shouldn't get here but let's send a default message just in case
|
||||
return trans('new_item_checked');
|
||||
}
|
||||
|
||||
private function requiresAcceptance(): int|bool
|
||||
{
|
||||
return method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,12 @@ class CheckoutLicenseMail extends BaseMailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
private bool $firstTimeSending;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct(LicenseSeat $licenseSeat, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
|
||||
public function __construct(LicenseSeat $licenseSeat, $checkedOutTo, User $checkedOutBy, $acceptance, $note, bool $firstTimeSending = true)
|
||||
{
|
||||
$this->item = $licenseSeat;
|
||||
$this->admin = $checkedOutBy;
|
||||
@@ -27,6 +29,7 @@ class CheckoutLicenseMail extends BaseMailable
|
||||
$this->acceptance = $acceptance;
|
||||
$this->settings = Setting::getSettings();
|
||||
$this->target = $checkedOutTo;
|
||||
$this->firstTimeSending = $firstTimeSending;
|
||||
|
||||
if($this->target instanceof User){
|
||||
$this->target = $this->target->display_name;
|
||||
@@ -45,7 +48,7 @@ class CheckoutLicenseMail extends BaseMailable
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
subject: trans('mail.Confirm_license_delivery'),
|
||||
subject: $this->getSubject(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -69,6 +72,7 @@ class CheckoutLicenseMail extends BaseMailable
|
||||
'eula' => $eula,
|
||||
'req_accept' => $req_accept,
|
||||
'accept_url' => $accept_url,
|
||||
'introduction_line' => $this->introductionLine(),
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -82,4 +86,36 @@ class CheckoutLicenseMail extends BaseMailable
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
private function getSubject(): string
|
||||
{
|
||||
if ($this->firstTimeSending) {
|
||||
return trans('mail.Confirm_license_delivery');
|
||||
}
|
||||
|
||||
return trans('mail.unaccepted_asset_reminder');
|
||||
}
|
||||
|
||||
private function introductionLine(): string
|
||||
{
|
||||
if ($this->firstTimeSending && $this->requiresAcceptance()) {
|
||||
return trans_choice('mail.new_item_checked_with_acceptance', 1);
|
||||
}
|
||||
|
||||
if ($this->firstTimeSending && !$this->requiresAcceptance()) {
|
||||
return trans_choice('mail.new_item_checked', 1);
|
||||
}
|
||||
|
||||
if (!$this->firstTimeSending && $this->requiresAcceptance()) {
|
||||
return trans('mail.recent_item_checked');
|
||||
}
|
||||
|
||||
// we shouldn't get here but let's send a default message just in case
|
||||
return trans('new_item_checked');
|
||||
}
|
||||
|
||||
private function requiresAcceptance(): int|bool
|
||||
{
|
||||
return method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +119,9 @@ class Accessory extends SnipeModel
|
||||
}
|
||||
|
||||
|
||||
public function isDeletable() {
|
||||
return $this->checkouts_count === 0;
|
||||
}
|
||||
/**
|
||||
* Sets the requestable attribute on the accessory
|
||||
*
|
||||
@@ -268,7 +271,7 @@ class Accessory extends SnipeModel
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,6 +20,7 @@ use Watson\Validating\ValidatingTrait;
|
||||
*/
|
||||
class AccessoryCheckout extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use Searchable;
|
||||
|
||||
protected $fillable = [
|
||||
@@ -41,7 +42,7 @@ class AccessoryCheckout extends Model
|
||||
*/
|
||||
public function accessory()
|
||||
{
|
||||
return $this->hasOne(Accessory::class, 'id', 'accessory_id');
|
||||
return $this->belongsTo(Accessory::class);
|
||||
}
|
||||
|
||||
public function accessories()
|
||||
@@ -57,7 +58,7 @@ class AccessoryCheckout extends Model
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->hasOne(\App\Models\User::class, 'id', 'created_by');
|
||||
return $this->belongsTo(User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -907,7 +907,7 @@ class Asset extends Depreciable
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
|
||||
@@ -1271,6 +1271,7 @@ class Asset extends Depreciable
|
||||
$query = $query
|
||||
->orWhere('assets_users.first_name', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('assets_users.last_name', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('assets_users.display_name', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('assets_users.jobtitle', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('assets_users.username', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('assets_users.employee_num', 'LIKE', '%'.$term.'%')
|
||||
@@ -1915,6 +1916,7 @@ class Asset extends Depreciable
|
||||
function ($query) use ($search_val) {
|
||||
$query->where('users.first_name', 'LIKE', '%'.$search_val.'%')
|
||||
->orWhere('users.last_name', 'LIKE', '%'.$search_val.'%')
|
||||
->orWhere('users.display_name', 'LIKE', '%'.$search_val.'%')
|
||||
->orWhere('users.username', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
|
||||
@@ -234,7 +234,7 @@ class AssetModel extends SnipeModel
|
||||
public function isDeletable()
|
||||
{
|
||||
return Gate::allows('delete', $this)
|
||||
&& ($this->assets_count == 0)
|
||||
&& (($this->assets_count ?? $this->assets()->count()) === 0)
|
||||
&& ($this->deleted_at == '');
|
||||
}
|
||||
|
||||
|
||||
@@ -242,7 +242,7 @@ class Category extends SnipeModel
|
||||
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,7 +46,7 @@ class Checkoutable
|
||||
$category = optional($unaccepted_row->license?->category?->present())->nameUrl() ?? '';
|
||||
$company = optional($unaccepted_row->license?->company?->present())?->nameUrl() ?? '';
|
||||
$model = '';
|
||||
$name = $unaccepted_row->license->present()->nameUrl() ?? '';
|
||||
$name = $unaccepted_row->license?->present()->nameUrl() ?? '';
|
||||
}
|
||||
if($unaccepted_row instanceof Consumable){
|
||||
$category = optional($unaccepted_row->category?->present())->nameUrl() ?? '';
|
||||
|
||||
@@ -320,7 +320,7 @@ final class Company extends SnipeModel
|
||||
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ class Component extends SnipeModel
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,14 +264,21 @@ class Component extends SnipeModel
|
||||
// In case there are elements checked out to assets that belong to a different company
|
||||
// than this asset and full multiple company support is on we'll remove the global scope,
|
||||
// so they are included in the count.
|
||||
if (is_null($this->sum_unconstrained_assets) || $recalculate) {
|
||||
// This, in a components-listing context, is mostly important for when it sets a 'zero' which
|
||||
// is *not* null - so we don't have to keep recalculating for un-checked-out components
|
||||
|
||||
// the 'sum' query returns NULL when there are zero checkouts - which can inadvertently re-trigger the following query
|
||||
// for un-checked-out components. So we have to do this very careful process of fetching the 'attributes'
|
||||
// of the component, then see if sum_unconstrained_assets exists as an attribute. If it doesn't, we run the
|
||||
// query. But if it *does* exist as an attribute - even a null - we skip the query, because that means that this
|
||||
// component was fetched using withCount() - and that count *is* accurate, even if null. We just do a quick
|
||||
// null-coalesce at the end to zero for the null case.
|
||||
$raw_attributes = $this->getAttributes();
|
||||
if (!array_key_exists('sum_unconstrained_assets', $raw_attributes) || $recalculate) {
|
||||
// This part should *only* run if the component was fetched *without* withCount() (or you've asked to recalculate)
|
||||
// NOTE: doing this will add a 'pseudo-attribute' to the component in question, so we need to _remove_ this
|
||||
// before we save - so that gets handled in the 'saving' callback defined in the 'booted' method, above.
|
||||
$this->sum_unconstrained_assets = $this->uncontrainedAssets()->sum('assigned_qty') ?? 0;
|
||||
$this->sum_unconstrained_assets = $this->unconstrainedAssets()->sum('assigned_qty') ?? 0;
|
||||
}
|
||||
return $this->sum_unconstrained_assets;
|
||||
return $this->sum_unconstrained_assets ?? 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -280,7 +287,7 @@ class Component extends SnipeModel
|
||||
*
|
||||
* This allows us to get the assets with assigned components without the company restriction
|
||||
*/
|
||||
public function uncontrainedAssets()
|
||||
public function unconstrainedAssets()
|
||||
{
|
||||
|
||||
return $this->belongsToMany(\App\Models\Asset::class, 'components_assets')
|
||||
|
||||
@@ -13,6 +13,7 @@ use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
|
||||
@@ -131,6 +132,13 @@ class Consumable extends SnipeModel
|
||||
$this->attributes['requestable'] = filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
|
||||
public function isDeletable()
|
||||
{
|
||||
return Gate::allows('delete', $this)
|
||||
&& ($this->numCheckedOut() === 0)
|
||||
&& ($this->deleted_at == '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the consumable -> admin user relationship
|
||||
*
|
||||
@@ -140,7 +148,7 @@ class Consumable extends SnipeModel
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
return $this->belongsTo(User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,6 @@ class ConsumableAssignment extends Model
|
||||
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Presenters\Presentable;
|
||||
use App\Rules\AlphaEncrypted;
|
||||
use App\Rules\BooleanEncrypted;
|
||||
use App\Rules\DateEncrypted;
|
||||
@@ -18,10 +20,13 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
|
||||
class CustomFieldset extends Model
|
||||
class CustomFieldset extends SnipeModel
|
||||
{
|
||||
use HasFactory;
|
||||
use ValidatingTrait;
|
||||
use Presentable;
|
||||
|
||||
protected $presenter = \App\Presenters\CustomFieldsetPresenter::class;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Models\Traits\CompanyableTrait;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
|
||||
class Department extends SnipeModel
|
||||
@@ -77,6 +78,13 @@ class Department extends SnipeModel
|
||||
*/
|
||||
protected $searchableRelations = [];
|
||||
|
||||
|
||||
public function isDeletable()
|
||||
{
|
||||
return Gate::allows('delete', $this) && (($this->users_count ?? $this->users()->count()) === 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Establishes the department -> company relationship
|
||||
*
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Models;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
|
||||
class Depreciation extends SnipeModel
|
||||
@@ -52,6 +53,16 @@ class Depreciation extends SnipeModel
|
||||
*/
|
||||
protected $searchableRelations = [];
|
||||
|
||||
|
||||
public function isDeletable()
|
||||
{
|
||||
return Gate::allows('delete', $this)
|
||||
&& (($this->assets_count ?? $this->assets()->count()) === 0)
|
||||
&& (($this->licenses_count ?? $this->licenses()->count()) === 0)
|
||||
&& (($this->models_count ?? $this->models()->count()) === 0)
|
||||
&& ($this->deleted_at == '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the depreciation -> models relationship
|
||||
*
|
||||
@@ -97,7 +108,7 @@ class Depreciation extends SnipeModel
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class Group extends SnipeModel
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,6 @@ class Import extends Model
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,18 @@
|
||||
namespace App\Models\Labels\Sheets\Avery;
|
||||
|
||||
|
||||
use App\Helpers\Helper;
|
||||
|
||||
class L7163_A extends L7163
|
||||
{
|
||||
private const BARCODE_MARGIN = 1.80;
|
||||
private const TAG_SIZE = 4.80;
|
||||
private const TITLE_SIZE = 5.00;
|
||||
private const TITLE_MARGIN = 1.80;
|
||||
private const LABEL_SIZE = 2.35;
|
||||
private const TITLE_MARGIN = .75;
|
||||
private const LABEL_SIZE = 3.35;
|
||||
private const LABEL_MARGIN = - 0.30;
|
||||
private const FIELD_SIZE = 4.80;
|
||||
private const FIELD_MARGIN = 0.30;
|
||||
private const FIELD_MARGIN = 0.20;
|
||||
|
||||
public function getUnit()
|
||||
{
|
||||
@@ -74,17 +76,7 @@ class L7163_A extends L7163
|
||||
$currentX = $pa->x1;
|
||||
$currentY = $pa->y1;
|
||||
|
||||
if ($record->has('title')) {
|
||||
static::writeText(
|
||||
$pdf, $record->get('title'),
|
||||
$currentX, $currentY,
|
||||
'freesans', '', self::TITLE_SIZE, 'C',
|
||||
$usableWidth, self::TITLE_SIZE, true, 0
|
||||
);
|
||||
$currentY += self::TITLE_SIZE + self::TITLE_MARGIN;
|
||||
}
|
||||
|
||||
$barcodeSize = $pa->h - self::TITLE_SIZE - self::TITLE_MARGIN - self::TAG_SIZE;
|
||||
$barcodeSize = $pa->h - self::TAG_SIZE;
|
||||
|
||||
if ($record->has('barcode2d')) {
|
||||
static::writeText(
|
||||
@@ -108,25 +100,71 @@ class L7163_A extends L7163
|
||||
$usableWidth, self::TAG_SIZE, true, 0
|
||||
);
|
||||
}
|
||||
$title = $record->has('title') ? $record->get('title') : null;
|
||||
$fields = $record->get('fields');
|
||||
|
||||
foreach ($record->get('fields') as $field) {
|
||||
$field_layout = Helper::labelFieldLayoutScaling(
|
||||
pdf: $pdf,
|
||||
fields: $fields,
|
||||
currentX: $currentX,
|
||||
usableWidth: $usableWidth,
|
||||
usableHeight: $usableHeight,
|
||||
baseLabelSize: self::LABEL_SIZE,
|
||||
baseFieldSize: self::FIELD_SIZE,
|
||||
baseFieldMargin: self::FIELD_MARGIN,
|
||||
title: $title,
|
||||
baseTitleSize: self::TITLE_SIZE,
|
||||
baseTitleMargin: self::TITLE_MARGIN,
|
||||
baseLabelPadding: 1.5,
|
||||
baseGap: 1.5,
|
||||
maxScale: 1.8,
|
||||
labelFont: 'freesans',
|
||||
);
|
||||
|
||||
if ($field_layout['hasTitle']) {
|
||||
static::writeText(
|
||||
$pdf, $field['label'],
|
||||
$pdf, $title,
|
||||
$currentX, $currentY,
|
||||
'freesans', '', self::LABEL_SIZE, 'L',
|
||||
$usableWidth, self::LABEL_SIZE, true, 0
|
||||
'freesans', 'b', $field_layout['titleSize'], 'L',
|
||||
$usableWidth, $field_layout['titleSize'], true, 0
|
||||
);
|
||||
$currentY += $field_layout['titleAdvance'];
|
||||
}
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$rawLabel = $field['label'] ?? null;
|
||||
$value = (string)($field['value'] ?? '');
|
||||
|
||||
// No label: value takes the whole row
|
||||
if (!is_string($rawLabel) || trim($rawLabel) === '') {
|
||||
static::writeText(
|
||||
$pdf, $value,
|
||||
$currentX, $currentY,
|
||||
'freemono', 'B', $field_layout['fieldSize'], 'L',
|
||||
$usableWidth, $field_layout['rowAdvance'], true, 0, 0.01
|
||||
);
|
||||
|
||||
$currentY += $field_layout['rowAdvance'];
|
||||
continue;
|
||||
}
|
||||
|
||||
$labelText = rtrim($field['label'], ':') . ':';
|
||||
|
||||
static::writeText(
|
||||
$pdf, $labelText,
|
||||
$currentX, $currentY,
|
||||
'freesans', '', $field_layout['labelSize'], 'L',
|
||||
$field_layout['labelWidth'], $field_layout['rowAdvance'], true,
|
||||
);
|
||||
$currentY += self::LABEL_SIZE + self::LABEL_MARGIN;
|
||||
|
||||
static::writeText(
|
||||
$pdf, $field['value'],
|
||||
$currentX, $currentY,
|
||||
'freemono', 'B', self::FIELD_SIZE, 'L',
|
||||
$usableWidth, self::FIELD_SIZE, true, 0, 0.5
|
||||
$field_layout['valueX'], $currentY,
|
||||
'freemono', 'B', $field_layout['fieldSize'], 'L',
|
||||
$field_layout['valueWidth'], $field_layout['rowAdvance'], true, 0, 0.01
|
||||
);
|
||||
$currentY += self::FIELD_SIZE + self::FIELD_MARGIN;
|
||||
$currentY += $field_layout['rowAdvance'];;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ class Ldap extends Model
|
||||
$connection = self::connectToLdap();
|
||||
$ldap_username_field = $settings->ldap_username_field;
|
||||
$baseDn = $settings->ldap_basedn;
|
||||
$userDn = $ldap_username_field.'='.$username.','.$settings->ldap_basedn;
|
||||
$userDn = $ldap_username_field . '=' . ldap_escape($username, '', LDAP_ESCAPE_DN) . ',' . $settings->ldap_basedn;
|
||||
|
||||
if ($settings->is_ad == '1') {
|
||||
// Check if they are using the userprincipalname for the username field.
|
||||
@@ -213,7 +213,7 @@ class Ldap extends Model
|
||||
}
|
||||
}
|
||||
|
||||
$filterQuery = $settings->ldap_auth_filter_query.$username;
|
||||
$filterQuery = $settings->ldap_auth_filter_query . ldap_escape($username, '', LDAP_ESCAPE_FILTER);
|
||||
$filter = Setting::getSettings()->ldap_filter; //FIXME - this *does* respect the ldap filter, but I believe that AdLdap2 did *not*.
|
||||
$filterQuery = "({$filter}({$filterQuery}))";
|
||||
|
||||
|
||||
@@ -389,7 +389,7 @@ class License extends Depreciable
|
||||
*/
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\Category::class, 'category_id');
|
||||
return $this->belongsTo(\App\Models\Category::class, 'category_id')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -401,7 +401,7 @@ class License extends Depreciable
|
||||
*/
|
||||
public function manufacturer()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\Manufacturer::class, 'manufacturer_id');
|
||||
return $this->belongsTo(\App\Models\Manufacturer::class, 'manufacturer_id')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -478,7 +478,7 @@ class License extends Depreciable
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -69,7 +69,7 @@ class LicenseSeat extends SnipeModel implements ICompanyableChild
|
||||
protected function name(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value) => $this->license->name,
|
||||
get: fn(mixed $value) => $this->license?->name,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ class Location extends SnipeModel
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,7 +10,9 @@ use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
use App\Presenters\MaintenancesPresenter;
|
||||
|
||||
/**
|
||||
* Model for Asset Maintenances.
|
||||
@@ -27,7 +29,7 @@ class Maintenance extends SnipeModel implements ICompanyableChild
|
||||
use Loggable, Presentable;
|
||||
|
||||
|
||||
|
||||
protected $presenter = MaintenancesPresenter::class;
|
||||
protected $table = 'maintenances';
|
||||
protected $rules = [
|
||||
'asset_id' => 'required|integer',
|
||||
@@ -118,6 +120,12 @@ class Maintenance extends SnipeModel implements ICompanyableChild
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function isDeletable()
|
||||
{
|
||||
return Gate::allows('delete', $this);
|
||||
}
|
||||
|
||||
public function setIsWarrantyAttribute($value)
|
||||
{
|
||||
if ($value == '') {
|
||||
@@ -315,4 +323,18 @@ class Maintenance extends SnipeModel implements ICompanyableChild
|
||||
{
|
||||
return $query->leftJoin('users as admin_sort', 'maintenances.created_by', '=', 'admin_sort.id')->select('maintenances.*')->orderBy('admin_sort.first_name', $order)->orderBy('admin_sort.last_name', $order);
|
||||
}
|
||||
|
||||
public function scopeOrderByAssetModelName($query, $order)
|
||||
{
|
||||
return $query->join('assets as maintained_asset', 'maintenances.asset_id', '=', 'maintained_asset.id')
|
||||
->leftjoin('models as maintained_asset_model', 'maintained_asset_model.id', '=', 'maintained_asset.model_id')
|
||||
->orderBy('maintained_asset_model.name', $order);
|
||||
}
|
||||
|
||||
public function scopeOrderByAssetModelNumber($query, $order)
|
||||
{
|
||||
return $query->join('assets as maintained_asset', 'maintenances.asset_id', '=', 'maintained_asset.id')
|
||||
->leftjoin('models as maintained_asset_model', 'maintained_asset_model.id', '=', 'maintained_asset.model_id')
|
||||
->orderBy('maintained_asset_model.model_number', $order);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ class Manufacturer extends SnipeModel
|
||||
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,21 +4,27 @@ namespace App\Models;
|
||||
|
||||
use App\Http\Traits\UniqueUndeletedTrait;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
|
||||
|
||||
class Statuslabel extends SnipeModel
|
||||
{
|
||||
use HasFactory;
|
||||
use SoftDeletes;
|
||||
use ValidatingTrait;
|
||||
use UniqueUndeletedTrait;
|
||||
use Presentable;
|
||||
|
||||
protected $injectUniqueIdentifier = true;
|
||||
|
||||
protected $table = 'status_labels';
|
||||
protected $hidden = ['user_id', 'deleted_at'];
|
||||
protected $presenter = \App\Presenters\StatusLabelPresenter::class;
|
||||
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'required|max:255|string|unique_undeleted',
|
||||
@@ -52,6 +58,13 @@ class Statuslabel extends SnipeModel
|
||||
*/
|
||||
protected $searchableRelations = [];
|
||||
|
||||
public function isDeletable()
|
||||
{
|
||||
return Gate::allows('delete', $this)
|
||||
&& (($this->assets_count ?? $this->assets()->count()) === 0)
|
||||
&& ($this->deleted_at == '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the status label -> assets relationship
|
||||
*
|
||||
@@ -66,7 +79,7 @@ class Statuslabel extends SnipeModel
|
||||
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,6 +9,7 @@ use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
use \Illuminate\Database\Eloquent\Relations\Relation;
|
||||
class Supplier extends SnipeModel
|
||||
@@ -72,6 +73,18 @@ class Supplier extends SnipeModel
|
||||
*/
|
||||
protected $fillable = ['name', 'address', 'address2', 'city', 'state', 'country', 'zip', 'phone', 'fax', 'email', 'contact', 'url', 'tag_color', 'notes'];
|
||||
|
||||
|
||||
public function isDeletable()
|
||||
{
|
||||
return Gate::allows('delete', $this)
|
||||
&& (($this->assets_count ?? $this->assets()->count()) === 0)
|
||||
&& (($this->licenses_count ?? $this->licenses()->count()) === 0)
|
||||
&& (($this->consumables_count ?? $this->consumables()->count()) === 0)
|
||||
&& (($this->accessories_count ?? $this->accessories()->count()) === 0)
|
||||
&& (($this->components_count ?? $this->components()->count()) === 0)
|
||||
&& (($this->maintenances_count ?? $this->maintenances()->count()) === 0)
|
||||
&& ($this->deleted_at == '');
|
||||
}
|
||||
/**
|
||||
* Eager load counts
|
||||
*
|
||||
@@ -145,7 +158,7 @@ class Supplier extends SnipeModel
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -262,7 +262,11 @@ class AccessoryPresenter extends Presenter
|
||||
*/
|
||||
public function nameUrl()
|
||||
{
|
||||
return (string) link_to_route('accessories.show', $this->name, $this->id);
|
||||
if (auth()->user()->can('view', ['\App\Models\Accessory', $this])) {
|
||||
return (string)link_to_route('accessories.show', e($this->display_name), $this->id);
|
||||
} else {
|
||||
return e($this->display_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -304,10 +304,10 @@ class AssetModelPresenter extends Presenter
|
||||
|
||||
public function formattedNameLink() {
|
||||
|
||||
if (auth()->user()->can('models.view', $this)) {
|
||||
return '<a href="'.route('models.show', e($this->id)).'">'.e($this->name).'</a>';
|
||||
if (auth()->user()->can('view', ['\App\Models\AssetModel', $this])) {
|
||||
return '<a href="'.route('models.show', e($this->id)).'" class="'. (($this->deleted_at!='') ? 'deleted' : '').'">'.e($this->display_name).'</a>';
|
||||
}
|
||||
|
||||
return $this->name;
|
||||
return '<span class="'. (($this->deleted_at!='') ? 'deleted' : '').'">'.e($this->display_name).'</span>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,14 +151,15 @@ class AssetPresenter extends Presenter
|
||||
'visible' => false,
|
||||
'title' => trans('general.purchase_date'),
|
||||
'formatter' => 'dateDisplayFormatter',
|
||||
], [
|
||||
'field' => 'first_checkout',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'visible' => false,
|
||||
'title' => trans('general.first_checkout'),
|
||||
'formatter' => 'dateDisplayFormatter',
|
||||
],
|
||||
// [
|
||||
// 'field' => 'first_checkout',
|
||||
// 'searchable' => true,
|
||||
// 'sortable' => true,
|
||||
// 'visible' => false,
|
||||
// 'title' => trans('general.first_checkout'),
|
||||
// 'formatter' => 'dateDisplayFormatter',
|
||||
// ],
|
||||
[
|
||||
'field' => 'age',
|
||||
'searchable' => false,
|
||||
@@ -447,7 +448,11 @@ class AssetPresenter extends Presenter
|
||||
*/
|
||||
public function nameUrl()
|
||||
{
|
||||
return (string) link_to_route('hardware.show', e($this->name), $this->id);
|
||||
if (auth()->user()->can('view', ['\App\Models\Asset', $this])) {
|
||||
return (string)link_to_route('hardware.show', e($this->display_name), $this->id);
|
||||
} else {
|
||||
return e($this->display_name);
|
||||
}
|
||||
}
|
||||
|
||||
public function modelUrl()
|
||||
|
||||
@@ -139,7 +139,11 @@ class CategoryPresenter extends Presenter
|
||||
*/
|
||||
public function nameUrl()
|
||||
{
|
||||
return (string) link_to_route('categories.show', $this->name, $this->id);
|
||||
if (auth()->user()->can('view', ['\App\Models\Category', $this])) {
|
||||
return (string)link_to_route('categories.show', e($this->display_name), $this->id);
|
||||
} else {
|
||||
return e($this->display_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,10 +157,16 @@ class CategoryPresenter extends Presenter
|
||||
|
||||
public function formattedNameLink() {
|
||||
|
||||
if (auth()->user()->can('view', ['\App\Models\Category', $this])) {
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').'<a href="'.route('categories.show', e($this->id)).'">'.e($this->name).'</a>';
|
||||
// We use soft-deletes for categories, but we don't give you a way to restore them right now. This would be the method we'd use when that happens
|
||||
// if (auth()->user()->can('view', ['\App\Models\Category', $this])) {
|
||||
// return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').'<a href="'.route('models.show', e($this->id)).'" class="'. (($this->deleted_at!='') ? 'deleted' : '').'">'.e($this->display_name).'</a>';
|
||||
// }
|
||||
|
||||
if ((auth()->user()->can('view', ['\App\Models\Category', $this])) && ($this->deleted_at=='')) {
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').'<a href="'.route('categories.show', e($this->id)).'">'.e($this->name).'</a>';
|
||||
}
|
||||
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i> " : '').$this->name;
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').'<span class="'. (($this->deleted_at!='') ? 'deleted' : '').'">'.e($this->display_name).'</span>';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,11 @@ class CompanyPresenter extends Presenter
|
||||
*/
|
||||
public function nameUrl()
|
||||
{
|
||||
return (string) link_to_route('companies.show', $this->name, $this->id);
|
||||
if (auth()->user()->can('view', ['\App\Models\Company', $this])) {
|
||||
return (string)link_to_route('companies.show', e($this->display_name), $this->id);
|
||||
} else {
|
||||
return e($this->display_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,9 +183,9 @@ class CompanyPresenter extends Presenter
|
||||
public function formattedNameLink() {
|
||||
|
||||
if (auth()->user()->can('view', ['\App\Models\Company', $this])) {
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').'<a href="'.route('companies.show', e($this->id)).'">'.e($this->name).'</a>';
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').'<a href="'.route('companies.show', e($this->id)).'">'.e($this->display_name).'</a>';
|
||||
}
|
||||
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').$this->name;
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').e($this->display_name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,13 +191,74 @@ class ComponentPresenter extends Presenter
|
||||
return json_encode($layout);
|
||||
}
|
||||
|
||||
public static function checkedOut() {
|
||||
$layout = [
|
||||
[
|
||||
'field' => 'id',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.id'),
|
||||
'visible' => false,
|
||||
],
|
||||
[
|
||||
'field' => 'name',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.name'),
|
||||
'visible' => true,
|
||||
'formatter' => 'hardwareLinkFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'qty',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.qty'),
|
||||
'visible' => true,
|
||||
],
|
||||
[
|
||||
'field' => 'note',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'visible' => true,
|
||||
'title' => trans('general.notes'),
|
||||
'formatter' => 'notesFormatter',
|
||||
],[
|
||||
'field' => 'created_at',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'visible' => false,
|
||||
'title' => trans('general.created_at'),
|
||||
'formatter' => 'dateDisplayFormatter',
|
||||
],
|
||||
$layout[] = [
|
||||
'field' => 'available_actions',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'switchable' => false,
|
||||
'title' => trans('general.checkin').'/'.trans('general.checkout'),
|
||||
'visible' => true,
|
||||
'formatter' => 'componentsInOutFormatter',
|
||||
'printIgnore' => true,
|
||||
|
||||
],
|
||||
];
|
||||
|
||||
return json_encode($layout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate html link to this items name.
|
||||
* @return string
|
||||
*/
|
||||
public function nameUrl()
|
||||
{
|
||||
return (string) link_to_route('consumables.show', e($this->name), $this->id);
|
||||
if (auth()->user()->can('view', ['\App\Models\Component', $this])) {
|
||||
return (string)link_to_route('components.show', e($this->display_name), $this->id);
|
||||
} else {
|
||||
return e($this->display_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -187,6 +187,58 @@ class ConsumablePresenter extends Presenter
|
||||
return json_encode($layout);
|
||||
}
|
||||
|
||||
public static function checkedOut()
|
||||
{
|
||||
$layout = [
|
||||
|
||||
[
|
||||
'field' => 'avatar',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'title' => trans('general.image'),
|
||||
'visible' => true,
|
||||
'formatter' => 'imageFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'user',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'title' => trans('general.name'),
|
||||
'visible' => true,
|
||||
'formatter' => 'usersLinkObjFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'created_at',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'title' => trans('general.date'),
|
||||
'visible' => true,
|
||||
'formatter' => 'dateDisplayFormatter',
|
||||
],
|
||||
|
||||
[
|
||||
'field' => 'note',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'title' => trans('general.notes'),
|
||||
'visible' => true,
|
||||
],
|
||||
|
||||
[
|
||||
'field' => 'created_by',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'title' => trans('general.created_by'),
|
||||
'visible' => true,
|
||||
'formatter' => 'usersLinkObjFormatter',
|
||||
],
|
||||
|
||||
|
||||
];
|
||||
|
||||
return json_encode($layout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Url to view this item.
|
||||
* @return string
|
||||
|
||||
22
app/Presenters/CustomFieldsetPresenter.php
Normal file
22
app/Presenters/CustomFieldsetPresenter.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Presenters;
|
||||
|
||||
/**
|
||||
* Class CustomFieldsetPresenter
|
||||
*/
|
||||
class CustomFieldsetPresenter extends Presenter
|
||||
{
|
||||
|
||||
|
||||
public function nameUrl()
|
||||
{
|
||||
if (auth()->user()->can('view', ['\App\Models\CustomFieldset', $this])) {
|
||||
return (string)link_to_route('fieldsets.show', e($this->display_name), $this->id);
|
||||
} else {
|
||||
return e($this->display_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -118,6 +118,19 @@ class DepartmentPresenter extends Presenter
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Url to view this item.
|
||||
* @return string
|
||||
*/
|
||||
public function viewUrl()
|
||||
{
|
||||
if (auth()->user()->can('view', ['\App\Models\Location', $this])) {
|
||||
return (string)link_to_route('locations.show', $this->display_name, $this->id);
|
||||
} else {
|
||||
return $this->display_name;
|
||||
}
|
||||
}
|
||||
|
||||
public function formattedNameLink() {
|
||||
|
||||
if (auth()->user()->can('view', ['\App\Models\Department', $this])) {
|
||||
|
||||
@@ -102,4 +102,17 @@ class DepreciationPresenter extends Presenter
|
||||
|
||||
return json_encode($layout);
|
||||
}
|
||||
|
||||
public function formattedNameLink() {
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i> " : '').e($this->display_name);
|
||||
}
|
||||
|
||||
public function nameUrl()
|
||||
{
|
||||
if (auth()->user()->can('view', ['\App\Models\Depreciation', $this])) {
|
||||
return (string)link_to_route('depreciations.show', e($this->display_name), $this->id);
|
||||
} else {
|
||||
return e($this->display_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +180,11 @@ class DepreciationReportPresenter extends Presenter
|
||||
*/
|
||||
public function nameUrl()
|
||||
{
|
||||
return (string) link_to_route('hardware.show', e($this->name), $this->id);
|
||||
if (auth()->user()->can('view', ['\App\Models\Depreciation', $this])) {
|
||||
return (string)link_to_route('depreciations.show', e($this->display_name), $this->id);
|
||||
} else {
|
||||
return e($this->display_name);
|
||||
}
|
||||
}
|
||||
|
||||
public function modelUrl()
|
||||
|
||||
@@ -319,7 +319,12 @@ class LicensePresenter extends Presenter
|
||||
*/
|
||||
public function nameUrl()
|
||||
{
|
||||
return (string) link_to_route('licenses.show', $this->name, $this->id);
|
||||
if (auth()->user()->can('view', ['\App\Models\License', $this])) {
|
||||
return (string)link_to_route('licenses.show', e($this->display_name), $this->id);
|
||||
} else {
|
||||
return e($this->display_name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -346,7 +346,11 @@ class LocationPresenter extends Presenter
|
||||
*/
|
||||
public function nameUrl()
|
||||
{
|
||||
return (string) link_to_route('locations.show', $this->name, $this->id);
|
||||
if (auth()->user()->can('view', ['\App\Models\Location', $this])) {
|
||||
return (string)link_to_route('locations.show', e($this->display_name), $this->id);
|
||||
} else {
|
||||
return e($this->display_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -367,6 +371,7 @@ class LocationPresenter extends Presenter
|
||||
return route('locations.show', $this->id);
|
||||
}
|
||||
|
||||
|
||||
public function glyph()
|
||||
{
|
||||
return '<x-icon type="locations" />';
|
||||
@@ -380,9 +385,9 @@ class LocationPresenter extends Presenter
|
||||
public function formattedNameLink() {
|
||||
|
||||
if (auth()->user()->can('view', ['\App\Models\Location', $this])) {
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').'<a href="'.route('locations.show', e($this->id)).'">'.e($this->name).'</a>';
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').'<a href="'.route('locations.show', e($this->id)).'">'.e($this->display_name).'</a>';
|
||||
}
|
||||
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i> " : '').$this->name;
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i> " : '').e($this->display_name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,14 @@ class MaintenancesPresenter extends Presenter
|
||||
'visible' => false,
|
||||
'formatter' => 'modelsLinkObjFormatter',
|
||||
], [
|
||||
'field' => 'model.model_number',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.model_no'),
|
||||
'visible' => true,
|
||||
],
|
||||
[
|
||||
'field' => 'supplier',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
|
||||
@@ -180,7 +180,11 @@ class ManufacturerPresenter extends Presenter
|
||||
*/
|
||||
public function nameUrl()
|
||||
{
|
||||
return (string) link_to_route('manufacturers.show', $this->name, $this->id);
|
||||
if (auth()->user()->can('view', ['\App\Models\Manufacturer', $this])) {
|
||||
return (string)link_to_route('manufacturers.show', e($this->display_name), $this->id);
|
||||
} else {
|
||||
return e($this->display_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,10 +199,10 @@ class ManufacturerPresenter extends Presenter
|
||||
public function formattedNameLink() {
|
||||
|
||||
if (auth()->user()->can('view', ['\App\Models\Manufacturer', $this])) {
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').'<a href="'.route('manufacturers.show', e($this->id)).'">'.e($this->name).'</a>';
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').'<a href="'.route('manufacturers.show', e($this->id)).'">'.e($this->display_name).'</a>';
|
||||
}
|
||||
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i> " : '').$this->name;
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').e($this->display_name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -295,7 +295,12 @@ class PredefinedKitPresenter extends Presenter
|
||||
*/
|
||||
public function nameUrl()
|
||||
{
|
||||
return (string) link_to_route('kits.show', $this->name, $this->id);
|
||||
if (auth()->user()->can('view', ['\App\Models\PredefinedKit', $this])) {
|
||||
return (string)link_to_route('kits.show', e($this->display_name), $this->id);
|
||||
} else {
|
||||
return e($this->display_name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -207,7 +207,11 @@ class SupplierPresenter extends Presenter
|
||||
*/
|
||||
public function nameUrl()
|
||||
{
|
||||
return (string) link_to_route('suppliers.show', $this->name, $this->id);
|
||||
if (auth()->user()->can('view', ['\App\Models\Supplier', $this])) {
|
||||
return (string)link_to_route('suppliers.show', e($this->display_name), $this->id);
|
||||
} else {
|
||||
return e($this->display_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,7 +229,11 @@ class SupplierPresenter extends Presenter
|
||||
*/
|
||||
public function viewUrl()
|
||||
{
|
||||
return route('suppliers.show', $this->id);
|
||||
if (auth()->user()->can('view', ['\App\Models\Supplier', $this])) {
|
||||
return (string)link_to_route('suppliers.show', $this->display_name, $this->id);
|
||||
} else {
|
||||
return e($this->display_name);
|
||||
}
|
||||
}
|
||||
|
||||
public function glyph()
|
||||
@@ -241,9 +249,9 @@ class SupplierPresenter extends Presenter
|
||||
public function formattedNameLink() {
|
||||
|
||||
if (auth()->user()->can('view', ['\App\Models\Supplier', $this])) {
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').'<a href="'.route('suppliers.show', e($this->id)).'">'.e($this->name).'</a>';
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-square fa-fw' style='color: ".e($this->tag_color)."' aria-hidden='true'></i> " : '').'<a href="'.route('suppliers.show', e($this->id)).'">'.e($this->name).'</a>';
|
||||
}
|
||||
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i> " : '').$this->name;
|
||||
return ($this->tag_color ? "<i class='fa-solid fa-square fa-fw' style='color: ".e($this->tag_color)."' aria-hidden='true'></i> " : '').e($this->name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,7 +526,12 @@ class UserPresenter extends Presenter
|
||||
*/
|
||||
public function nameUrl()
|
||||
{
|
||||
return (string) link_to_route('users.show', $this->display_name, $this->id);
|
||||
if (auth()->user()->can('view', ['\App\Models\User', $this])) {
|
||||
return (string)link_to_route('users.show', $this->display_name, $this->id);
|
||||
} else {
|
||||
return e($this->display_name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -542,4 +547,13 @@ class UserPresenter extends Presenter
|
||||
{
|
||||
return '<x-icon type="user"/>';
|
||||
}
|
||||
|
||||
public function formattedNameLink() {
|
||||
|
||||
if (auth()->user()->can('view', ['\App\Models\User', $this])) {
|
||||
return '<a href="'.route('users.show', e($this->id)).'" class="'. (($this->deleted_at!='') ? 'deleted' : '').'">'.e($this->display_name).'</a>';
|
||||
}
|
||||
|
||||
return '<span class="'. (($this->deleted_at!='') ? 'deleted' : '').'">'.e($this->display_name).'</span>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,6 +214,12 @@ class BreadcrumbsServiceProvider extends ServiceProvider
|
||||
->push(trans('general.breadcrumb_button_actions.edit_item', ['name' => $component->name]), route('components.edit', $component))
|
||||
);
|
||||
|
||||
Breadcrumbs::for('components.clone.create', fn (Trail $trail, Component $component) =>
|
||||
$trail->parent('components.index', route('components.index'))
|
||||
->push($component->display_name, route('components.show', $component))
|
||||
->push(trans('general.clone'), route('components.create'))
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Consumables Breadcrumbs
|
||||
@@ -262,9 +268,10 @@ class BreadcrumbsServiceProvider extends ServiceProvider
|
||||
|
||||
Breadcrumbs::for('fieldsets.create', fn (Trail $trail) =>
|
||||
$trail->parent('fields.index', route('fields.index'))
|
||||
->push(trans('general.create'), route('fieldsets.create'))
|
||||
->push(trans('admin/custom_fields/general.create_fieldset'), route('fieldsets.create'))
|
||||
);
|
||||
|
||||
|
||||
Breadcrumbs::for('fieldsets.show', fn (Trail $trail, CustomFieldset $fieldset) =>
|
||||
$trail->parent('fields.index', route('fields.index'))
|
||||
->push($fieldset->name, route('fields.index'))
|
||||
@@ -434,7 +441,8 @@ class BreadcrumbsServiceProvider extends ServiceProvider
|
||||
|
||||
Breadcrumbs::for('maintenances.edit', fn (Trail $trail, Maintenance $maintenance) =>
|
||||
$trail->parent('maintenances.index', route('maintenances.index'))
|
||||
->push(trans('general.breadcrumb_button_actions.edit_item', ['name' => $maintenance->name]), route('maintenances.edit', $maintenance))
|
||||
->push($maintenance->name, route('maintenances.show', $maintenance))
|
||||
->push(trans('general.update', ['name' => $maintenance->name]), route('maintenances.edit', $maintenance))
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Listeners\CheckoutableListener;
|
||||
use App\Listeners\CheckoutablesCheckedOutInBulkListener;
|
||||
use App\Listeners\LogListener;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
|
||||
@@ -31,5 +32,6 @@ class EventServiceProvider extends ServiceProvider
|
||||
protected $subscribe = [
|
||||
LogListener::class,
|
||||
CheckoutableListener::class,
|
||||
CheckoutablesCheckedOutInBulkListener::class,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -21,12 +21,18 @@ class LivewireServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
Livewire::setUpdateRoute(function ($handle) {
|
||||
return Route::post('/' . config('livewire.url_prefix') . '/livewire/update', $handle);
|
||||
$prefix = trim((string) config('livewire.url_prefix', ''), '/');
|
||||
if ($prefix === '') {
|
||||
$prefix = trim((string) parse_url(config('app.url'), PHP_URL_PATH), '/');
|
||||
}
|
||||
$prefix = $prefix === '' ? '' : '/' . $prefix;
|
||||
|
||||
Livewire::setUpdateRoute(function ($handle) use ($prefix) {
|
||||
return Route::post($prefix . '/livewire/update', $handle);
|
||||
});
|
||||
|
||||
Livewire::setScriptRoute(function ($handle) {
|
||||
return Route::get('/' . config('livewire.url_prefix') . '/livewire/livewire.js', $handle);
|
||||
Livewire::setScriptRoute(function ($handle) use ($prefix) {
|
||||
return Route::get($prefix . '/livewire/livewire.js', $handle);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,10 @@ class SettingsServiceProvider extends ServiceProvider
|
||||
return 'models/';
|
||||
});
|
||||
|
||||
app()->singleton('licenses_upload_url', function () {
|
||||
return 'licenses/';
|
||||
});
|
||||
|
||||
// Categories
|
||||
app()->singleton('categories_upload_path', function () {
|
||||
return 'categories/';
|
||||
@@ -175,6 +179,10 @@ class SettingsServiceProvider extends ServiceProvider
|
||||
return 'components/';
|
||||
});
|
||||
|
||||
app()->singleton('maintenances_upload_url', function () {
|
||||
return 'maintenances/';
|
||||
});
|
||||
|
||||
// Set the monetary locale to the configured locale to make helper::parseFloat work.
|
||||
setlocale(LC_MONETARY, config('app.locale'));
|
||||
setlocale(LC_NUMERIC, config('app.locale'));
|
||||
|
||||
13
app/Traits/DisablesDebugbar.php
Normal file
13
app/Traits/DisablesDebugbar.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
trait DisablesDebugbar
|
||||
{
|
||||
public function disableDebugbar()
|
||||
{
|
||||
if (class_exists(\Fruitcake\LaravelDebugbar\Facades\Debugbar::class)) {
|
||||
\Fruitcake\LaravelDebugbar\Facades\Debugbar::disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,6 @@
|
||||
"alek13/slack": "^2.0",
|
||||
"arietimmerman/laravel-scim-server": "dev-master",
|
||||
"bacon/bacon-qr-code": "^2.0",
|
||||
"barryvdh/laravel-debugbar": "^3.13",
|
||||
"doctrine/cache": "^1.10",
|
||||
"doctrine/dbal": "^3.1",
|
||||
"doctrine/instantiator": "^1.3",
|
||||
@@ -84,6 +83,7 @@
|
||||
"ext-exif": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"fruitcake/laravel-debugbar": "^4.0",
|
||||
"larastan/larastan": "^2.9",
|
||||
"laravel/telescope": "^5.11",
|
||||
"mockery/mockery": "^1.4",
|
||||
|
||||
530
composer.lock
generated
530
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "00deb2a452371da9a1a522e5e9919f03",
|
||||
"content-hash": "895ab3cf22a77918f3239ef4774d628b",
|
||||
"packages": [
|
||||
{
|
||||
"name": "alek13/slack",
|
||||
@@ -340,91 +340,6 @@
|
||||
},
|
||||
"time": "2022-12-07T17:46:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "barryvdh/laravel-debugbar",
|
||||
"version": "v3.16.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/barryvdh/laravel-debugbar.git",
|
||||
"reference": "f265cf5e38577d42311f1a90d619bcd3740bea23"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/f265cf5e38577d42311f1a90d619bcd3740bea23",
|
||||
"reference": "f265cf5e38577d42311f1a90d619bcd3740bea23",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/routing": "^9|^10|^11|^12",
|
||||
"illuminate/session": "^9|^10|^11|^12",
|
||||
"illuminate/support": "^9|^10|^11|^12",
|
||||
"php": "^8.1",
|
||||
"php-debugbar/php-debugbar": "~2.2.0",
|
||||
"symfony/finder": "^6|^7"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.3",
|
||||
"orchestra/testbench-dusk": "^7|^8|^9|^10",
|
||||
"phpunit/phpunit": "^9.5.10|^10|^11",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"aliases": {
|
||||
"Debugbar": "Barryvdh\\Debugbar\\Facades\\Debugbar"
|
||||
},
|
||||
"providers": [
|
||||
"Barryvdh\\Debugbar\\ServiceProvider"
|
||||
]
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-master": "3.16-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/helpers.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Barryvdh\\Debugbar\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Barry vd. Heuvel",
|
||||
"email": "barryvdh@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP Debugbar integration for Laravel",
|
||||
"keywords": [
|
||||
"debug",
|
||||
"debugbar",
|
||||
"dev",
|
||||
"laravel",
|
||||
"profiler",
|
||||
"webprofiler"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/barryvdh/laravel-debugbar/issues",
|
||||
"source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.16.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://fruitcake.nl",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/barryvdh",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-14T11:56:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.14.0",
|
||||
@@ -4458,16 +4373,16 @@
|
||||
},
|
||||
{
|
||||
"name": "livewire/livewire",
|
||||
"version": "v4.1.2",
|
||||
"version": "v3.6.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/livewire/livewire.git",
|
||||
"reference": "8adef21f35f4ffa87fd2f3655b350236df0c39a8"
|
||||
"reference": "ef04be759da41b14d2d129e670533180a44987dc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/livewire/livewire/zipball/8adef21f35f4ffa87fd2f3655b350236df0c39a8",
|
||||
"reference": "8adef21f35f4ffa87fd2f3655b350236df0c39a8",
|
||||
"url": "https://api.github.com/repos/livewire/livewire/zipball/ef04be759da41b14d2d129e670533180a44987dc",
|
||||
"reference": "ef04be759da41b14d2d129e670533180a44987dc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4522,7 +4437,7 @@
|
||||
"description": "A front-end framework for Laravel.",
|
||||
"support": {
|
||||
"issues": "https://github.com/livewire/livewire/issues",
|
||||
"source": "https://github.com/livewire/livewire/tree/v4.1.2"
|
||||
"source": "https://github.com/livewire/livewire/tree/v3.6.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4530,7 +4445,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-02-03T03:01:29+00:00"
|
||||
"time": "2025-07-17T05:12:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "masterminds/html5",
|
||||
@@ -5840,79 +5755,6 @@
|
||||
},
|
||||
"time": "2025-12-30T16:16:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-debugbar/php-debugbar",
|
||||
"version": "v2.2.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-debugbar/php-debugbar.git",
|
||||
"reference": "3146d04671f51f69ffec2a4207ac3bdcf13a9f35"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/3146d04671f51f69ffec2a4207ac3bdcf13a9f35",
|
||||
"reference": "3146d04671f51f69ffec2a4207ac3bdcf13a9f35",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8",
|
||||
"psr/log": "^1|^2|^3",
|
||||
"symfony/var-dumper": "^4|^5|^6|^7"
|
||||
},
|
||||
"replace": {
|
||||
"maximebf/debugbar": "self.version"
|
||||
},
|
||||
"require-dev": {
|
||||
"dbrekelmans/bdi": "^1",
|
||||
"phpunit/phpunit": "^8|^9",
|
||||
"symfony/panther": "^1|^2.1",
|
||||
"twig/twig": "^1.38|^2.7|^3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"kriswallsmith/assetic": "The best way to manage assets",
|
||||
"monolog/monolog": "Log using Monolog",
|
||||
"predis/predis": "Redis storage"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"DebugBar\\": "src/DebugBar/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Maxime Bouroumeau-Fuseau",
|
||||
"email": "maxime.bouroumeau@gmail.com",
|
||||
"homepage": "http://maximebf.com"
|
||||
},
|
||||
{
|
||||
"name": "Barry vd. Heuvel",
|
||||
"email": "barryvdh@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Debug bar in the browser for php application",
|
||||
"homepage": "https://github.com/php-debugbar/php-debugbar",
|
||||
"keywords": [
|
||||
"debug",
|
||||
"debug bar",
|
||||
"debugbar",
|
||||
"dev"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-debugbar/php-debugbar/issues",
|
||||
"source": "https://github.com/php-debugbar/php-debugbar/tree/v2.2.4"
|
||||
},
|
||||
"time": "2025-07-22T14:01:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-common",
|
||||
"version": "2.2.0",
|
||||
@@ -9103,23 +8945,23 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v7.3.2",
|
||||
"version": "v7.4.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "2a6614966ba1074fa93dae0bc804227422df4dfe"
|
||||
"reference": "01b24a145bbeaa7141e75887ec904c34a6728a5f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe",
|
||||
"reference": "2a6614966ba1074fa93dae0bc804227422df4dfe",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/01b24a145bbeaa7141e75887ec904c34a6728a5f",
|
||||
"reference": "01b24a145bbeaa7141e75887ec904c34a6728a5f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/filesystem": "^6.4|^7.0"
|
||||
"symfony/filesystem": "^6.4|^7.0|^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@@ -9147,7 +8989,7 @@
|
||||
"description": "Finds files and directories via an intuitive fluent interface",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/finder/tree/v7.3.2"
|
||||
"source": "https://github.com/symfony/finder/tree/v7.4.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -9167,27 +9009,26 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-15T13:41:35+00:00"
|
||||
"time": "2026-01-12T12:19:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-foundation",
|
||||
"version": "v7.3.7",
|
||||
"version": "v7.4.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-foundation.git",
|
||||
"reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4"
|
||||
"reference": "977a554a34cf8edc95ca351fbecb1bb1ad05cc94"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/db488a62f98f7a81d5746f05eea63a74e55bb7c4",
|
||||
"reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/977a554a34cf8edc95ca351fbecb1bb1ad05cc94",
|
||||
"reference": "977a554a34cf8edc95ca351fbecb1bb1ad05cc94",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3.0",
|
||||
"symfony/polyfill-mbstring": "~1.1",
|
||||
"symfony/polyfill-php83": "^1.27"
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/polyfill-mbstring": "^1.1"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/dbal": "<3.6",
|
||||
@@ -9196,13 +9037,13 @@
|
||||
"require-dev": {
|
||||
"doctrine/dbal": "^3.6|^4",
|
||||
"predis/predis": "^1.1|^2.0",
|
||||
"symfony/cache": "^6.4.12|^7.1.5",
|
||||
"symfony/clock": "^6.4|^7.0",
|
||||
"symfony/dependency-injection": "^6.4|^7.0",
|
||||
"symfony/expression-language": "^6.4|^7.0",
|
||||
"symfony/http-kernel": "^6.4|^7.0",
|
||||
"symfony/mime": "^6.4|^7.0",
|
||||
"symfony/rate-limiter": "^6.4|^7.0"
|
||||
"symfony/cache": "^6.4.12|^7.1.5|^8.0",
|
||||
"symfony/clock": "^6.4|^7.0|^8.0",
|
||||
"symfony/dependency-injection": "^6.4|^7.0|^8.0",
|
||||
"symfony/expression-language": "^6.4|^7.0|^8.0",
|
||||
"symfony/http-kernel": "^6.4|^7.0|^8.0",
|
||||
"symfony/mime": "^6.4|^7.0|^8.0",
|
||||
"symfony/rate-limiter": "^6.4|^7.0|^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@@ -9230,7 +9071,7 @@
|
||||
"description": "Defines an object-oriented layer for the HTTP specification",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-foundation/tree/v7.3.7"
|
||||
"source": "https://github.com/symfony/http-foundation/tree/v7.4.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -9250,7 +9091,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-11-08T16:41:12+00:00"
|
||||
"time": "2026-01-09T12:14:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
@@ -9456,20 +9297,21 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/mime",
|
||||
"version": "v7.3.2",
|
||||
"version": "v7.4.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/mime.git",
|
||||
"reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1"
|
||||
"reference": "40945014c0a9471ccfe19673c54738fa19367a3c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/mime/zipball/e0a0f859148daf1edf6c60b398eb40bfc96697d1",
|
||||
"reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1",
|
||||
"url": "https://api.github.com/repos/symfony/mime/zipball/40945014c0a9471ccfe19673c54738fa19367a3c",
|
||||
"reference": "40945014c0a9471ccfe19673c54738fa19367a3c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/polyfill-intl-idn": "^1.10",
|
||||
"symfony/polyfill-mbstring": "^1.0"
|
||||
},
|
||||
@@ -9484,11 +9326,11 @@
|
||||
"egulias/email-validator": "^2.1.10|^3.1|^4",
|
||||
"league/html-to-markdown": "^5.0",
|
||||
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
|
||||
"symfony/dependency-injection": "^6.4|^7.0",
|
||||
"symfony/process": "^6.4|^7.0",
|
||||
"symfony/property-access": "^6.4|^7.0",
|
||||
"symfony/property-info": "^6.4|^7.0",
|
||||
"symfony/serializer": "^6.4.3|^7.0.3"
|
||||
"symfony/dependency-injection": "^6.4|^7.0|^8.0",
|
||||
"symfony/process": "^6.4|^7.0|^8.0",
|
||||
"symfony/property-access": "^6.4|^7.0|^8.0",
|
||||
"symfony/property-info": "^6.4|^7.0|^8.0",
|
||||
"symfony/serializer": "^6.4.3|^7.0.3|^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@@ -9520,7 +9362,7 @@
|
||||
"mime-type"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/mime/tree/v7.3.2"
|
||||
"source": "https://github.com/symfony/mime/tree/v7.4.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -9540,7 +9382,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-15T13:41:35+00:00"
|
||||
"time": "2026-01-08T16:12:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
@@ -10872,16 +10714,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
"version": "v7.3.3",
|
||||
"version": "v7.4.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-dumper.git",
|
||||
"reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f"
|
||||
"reference": "0e4769b46a0c3c62390d124635ce59f66874b282"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/34d8d4c4b9597347306d1ec8eb4e1319b1e6986f",
|
||||
"reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/0e4769b46a0c3c62390d124635ce59f66874b282",
|
||||
"reference": "0e4769b46a0c3c62390d124635ce59f66874b282",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -10893,10 +10735,10 @@
|
||||
"symfony/console": "<6.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/console": "^6.4|^7.0",
|
||||
"symfony/http-kernel": "^6.4|^7.0",
|
||||
"symfony/process": "^6.4|^7.0",
|
||||
"symfony/uid": "^6.4|^7.0",
|
||||
"symfony/console": "^6.4|^7.0|^8.0",
|
||||
"symfony/http-kernel": "^6.4|^7.0|^8.0",
|
||||
"symfony/process": "^6.4|^7.0|^8.0",
|
||||
"symfony/uid": "^6.4|^7.0|^8.0",
|
||||
"twig/twig": "^3.12"
|
||||
},
|
||||
"bin": [
|
||||
@@ -10935,7 +10777,7 @@
|
||||
"dump"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/var-dumper/tree/v7.3.3"
|
||||
"source": "https://github.com/symfony/var-dumper/tree/v7.4.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -10955,7 +10797,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-08-13T11:49:31+00:00"
|
||||
"time": "2026-01-01T22:13:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "tabuna/breadcrumbs",
|
||||
@@ -12771,6 +12613,108 @@
|
||||
],
|
||||
"time": "2025-09-10T09:51:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "fruitcake/laravel-debugbar",
|
||||
"version": "v4.0.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fruitcake/laravel-debugbar.git",
|
||||
"reference": "a9cc62c81cd0bda4ca7410229487638d7df786be"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/a9cc62c81cd0bda4ca7410229487638d7df786be",
|
||||
"reference": "a9cc62c81cd0bda4ca7410229487638d7df786be",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/routing": "^11|^12",
|
||||
"illuminate/session": "^11|^12",
|
||||
"illuminate/support": "^11|^12",
|
||||
"php": "^8.2",
|
||||
"php-debugbar/php-debugbar": "^3.1",
|
||||
"php-debugbar/symfony-bridge": "^1.1"
|
||||
},
|
||||
"replace": {
|
||||
"barryvdh/laravel-debugbar": "self.version"
|
||||
},
|
||||
"require-dev": {
|
||||
"larastan/larastan": "^3",
|
||||
"laravel/octane": "^2",
|
||||
"laravel/pennant": "^1",
|
||||
"laravel/pint": "^1",
|
||||
"laravel/telescope": "^5.16",
|
||||
"livewire/livewire": "^3.7|^4",
|
||||
"mockery/mockery": "^1.3.3",
|
||||
"orchestra/testbench-dusk": "^9|^10",
|
||||
"php-debugbar/twig-bridge": "^2.0",
|
||||
"phpstan/phpstan-phpunit": "^2",
|
||||
"phpstan/phpstan-strict-rules": "^2.0",
|
||||
"phpunit/phpunit": "^11",
|
||||
"shipmonk/phpstan-rules": "^4.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"aliases": {
|
||||
"Debugbar": "Fruitcake\\LaravelDebugbar\\Facades\\Debugbar"
|
||||
},
|
||||
"providers": [
|
||||
"Fruitcake\\LaravelDebugbar\\ServiceProvider"
|
||||
]
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-master": "4.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/helpers.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Fruitcake\\LaravelDebugbar\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fruitcake",
|
||||
"homepage": "https://fruitcake.nl"
|
||||
},
|
||||
{
|
||||
"name": "Barry vd. Heuvel",
|
||||
"email": "barryvdh@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP Debugbar integration for Laravel",
|
||||
"keywords": [
|
||||
"barryvdh",
|
||||
"debug",
|
||||
"debugbar",
|
||||
"dev",
|
||||
"laravel",
|
||||
"profiler",
|
||||
"webprofiler"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/fruitcake/laravel-debugbar/issues",
|
||||
"source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.0.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://fruitcake.nl",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/barryvdh",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-02-06T20:53:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "hamcrest/hamcrest-php",
|
||||
"version": "v2.1.1",
|
||||
@@ -13608,6 +13552,174 @@
|
||||
},
|
||||
"time": "2022-02-21T01:04:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-debugbar/php-debugbar",
|
||||
"version": "v3.2.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-debugbar/php-debugbar.git",
|
||||
"reference": "a1f4e156811f000efb6e118178832581fab94f11"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/a1f4e156811f000efb6e118178832581fab94f11",
|
||||
"reference": "a1f4e156811f000efb6e118178832581fab94f11",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"psr/log": "^1|^2|^3",
|
||||
"symfony/var-dumper": "^5.4|^6|^7|^8"
|
||||
},
|
||||
"replace": {
|
||||
"maximebf/debugbar": "self.version"
|
||||
},
|
||||
"require-dev": {
|
||||
"dbrekelmans/bdi": "^1.4",
|
||||
"friendsofphp/php-cs-fixer": "^3.92",
|
||||
"monolog/monolog": "^3.9",
|
||||
"php-debugbar/doctrine-bridge": "^3@dev",
|
||||
"php-debugbar/monolog-bridge": "^1@dev",
|
||||
"php-debugbar/symfony-bridge": "^1@dev",
|
||||
"php-debugbar/twig-bridge": "^2@dev",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpstan/phpstan-phpunit": "^2.0",
|
||||
"phpstan/phpstan-strict-rules": "^2.0",
|
||||
"phpunit/phpunit": "^10",
|
||||
"predis/predis": "^3.3",
|
||||
"shipmonk/phpstan-rules": "^4.3",
|
||||
"symfony/browser-kit": "^6.4|7.0",
|
||||
"symfony/dom-crawler": "^6.4|^7",
|
||||
"symfony/event-dispatcher": "^5.4|^6.4|^7.3|^8.0",
|
||||
"symfony/http-foundation": "^5.4|^6.4|^7.3|^8.0",
|
||||
"symfony/mailer": "^5.4|^6.4|^7.3|^8.0",
|
||||
"symfony/panther": "^1|^2.1",
|
||||
"twig/twig": "^3.11.2"
|
||||
},
|
||||
"suggest": {
|
||||
"php-debugbar/doctrine-bridge": "To integrate Doctrine with php-debugbar.",
|
||||
"php-debugbar/monolog-bridge": "To integrate Monolog with php-debugbar.",
|
||||
"php-debugbar/symfony-bridge": "To integrate Symfony with php-debugbar.",
|
||||
"php-debugbar/twig-bridge": "To integrate Twig with php-debugbar."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"DebugBar\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Maxime Bouroumeau-Fuseau",
|
||||
"email": "maxime.bouroumeau@gmail.com",
|
||||
"homepage": "http://maximebf.com"
|
||||
},
|
||||
{
|
||||
"name": "Barry vd. Heuvel",
|
||||
"email": "barryvdh@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Debug bar in the browser for php application",
|
||||
"homepage": "https://github.com/php-debugbar/php-debugbar",
|
||||
"keywords": [
|
||||
"debug",
|
||||
"debug bar",
|
||||
"debugbar",
|
||||
"dev",
|
||||
"profiler",
|
||||
"toolbar"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-debugbar/php-debugbar/issues",
|
||||
"source": "https://github.com/php-debugbar/php-debugbar/tree/v3.2.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://fruitcake.nl",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/barryvdh",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-01-25T20:59:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-debugbar/symfony-bridge",
|
||||
"version": "v1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-debugbar/symfony-bridge.git",
|
||||
"reference": "e37d2debe5d316408b00d0ab2688d9c2cf59b5ad"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-debugbar/symfony-bridge/zipball/e37d2debe5d316408b00d0ab2688d9c2cf59b5ad",
|
||||
"reference": "e37d2debe5d316408b00d0ab2688d9c2cf59b5ad",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"php-debugbar/php-debugbar": "^3.1",
|
||||
"symfony/http-foundation": "^5.4|^6.4|^7.3|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dbrekelmans/bdi": "^1.4",
|
||||
"phpunit/phpunit": "^10",
|
||||
"symfony/browser-kit": "^6|^7",
|
||||
"symfony/dom-crawler": "^6|^7",
|
||||
"symfony/mailer": "^5.4|^6.4|^7.3|^8.0",
|
||||
"symfony/panther": "^1|^2.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"DebugBar\\Bridge\\Symfony\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Maxime Bouroumeau-Fuseau",
|
||||
"email": "maxime.bouroumeau@gmail.com",
|
||||
"homepage": "http://maximebf.com"
|
||||
},
|
||||
{
|
||||
"name": "Barry vd. Heuvel",
|
||||
"email": "barryvdh@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Symfony bridge for PHP Debugbar",
|
||||
"homepage": "https://github.com/php-debugbar/php-debugbar",
|
||||
"keywords": [
|
||||
"debugbar",
|
||||
"dev",
|
||||
"symfony"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-debugbar/symfony-bridge/issues",
|
||||
"source": "https://github.com/php-debugbar/symfony-bridge/tree/v1.1.0"
|
||||
},
|
||||
"time": "2026-01-15T14:47:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-mock/php-mock",
|
||||
"version": "2.6.2",
|
||||
|
||||
@@ -86,7 +86,7 @@ return [
|
||||
'mysql' => [
|
||||
'driver' => 'mysql',
|
||||
'host' => env('DB_HOST', 'localhost'),
|
||||
'port' => env('DB_PORT', 3306),
|
||||
'port' => (int) env('DB_PORT', 3306),
|
||||
'database' => env('DB_DATABASE', 'forge'),
|
||||
'username' => env('DB_USERNAME', 'forge'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
@@ -10,98 +12,53 @@ return [
|
||||
| Debugbar is enabled by default, when debug is set to true in app.php.
|
||||
| You can override the value by setting enable to true or false instead of null.
|
||||
|
|
||||
*/
|
||||
|
||||
'enabled' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Storage settings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| DebugBar stores data for session/ajax requests.
|
||||
| You can disable this, so the debugbar stores data in headers/session,
|
||||
| but this can cause problems with large data collectors.
|
||||
| By default, file storage (in the storage folder) is used. Redis and PDO
|
||||
| can also be used. For PDO, run the package migrations first.
|
||||
| You can provide an array of URI's that must be ignored (eg. 'api/*')
|
||||
|
|
||||
*/
|
||||
'storage' => [
|
||||
'enabled' => true,
|
||||
'driver' => 'file', // redis, file, pdo
|
||||
'path' => storage_path().'/debugbar', // For file driver
|
||||
'connection' => null, // Leave null for default connection (Redis/PDO)
|
||||
|
||||
'enabled' => env('DEBUGBAR_ENABLED'),
|
||||
'collect_jobs' => env('DEBUGBAR_COLLECT_JOBS', false),
|
||||
'except' => [
|
||||
'telescope*',
|
||||
'horizon*',
|
||||
'_boost/browser-logs',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Vendors
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Vendor files are included by default, but can be set to false.
|
||||
| This can also be set to 'js' or 'css', to only include javascript or css vendor files.
|
||||
| Vendor files are for css: font-awesome (including fonts) and highlight.js (css files)
|
||||
| and for js: jquery and and highlight.js
|
||||
| So if you want syntax highlighting, set it to true.
|
||||
| jQuery is set to not conflict with existing jQuery scripts.
|
||||
|
|
||||
*/
|
||||
|
||||
'include_vendors' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Capture Ajax Requests
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors),
|
||||
| you can use this option to disable sending the data through the headers.
|
||||
|
|
||||
*/
|
||||
|
||||
'capture_ajax' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Clockwork integration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The Debugbar can emulate the Clockwork headers, so you can use the Chrome
|
||||
| Extension, without the server-side code. It uses Debugbar collectors instead.
|
||||
|
|
||||
*/
|
||||
'clockwork' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DataCollectors
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Enable/disable DataCollectors
|
||||
|
|
||||
*/
|
||||
|--------------------------------------------------------------------------
|
||||
| DataCollectors
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Enable/disable DataCollectors
|
||||
|
|
||||
*/
|
||||
|
||||
'collectors' => [
|
||||
'phpinfo' => true, // Php version
|
||||
'messages' => true, // Messages
|
||||
'time' => true, // Time Datalogger
|
||||
'memory' => true, // Memory usage
|
||||
'exceptions' => true, // Exception displayer
|
||||
'log' => true, // Logs from Monolog (merged in messages if enabled)
|
||||
'db' => true, // Show database (PDO) queries and bindings
|
||||
'views' => true, // Views with their data
|
||||
'route' => true, // Current route information
|
||||
'laravel' => true, // Laravel version and environment
|
||||
'events' => true, // All events fired
|
||||
'default_request' => false, // Regular or special Symfony request logger
|
||||
'symfony_request' => true, // Only one can be enabled..
|
||||
'mail' => true, // Catch mail messages
|
||||
'logs' => true, // Add the latest log messages
|
||||
'files' => true, // Show the included files
|
||||
'config' => false, // Display config settings
|
||||
'auth' => true, // Display Laravel authentication status
|
||||
'gate' => true, // Display Laravel Gate checks
|
||||
'session' => true, // Display session data
|
||||
'phpinfo' => env('DEBUGBAR_COLLECTORS_PHPINFO', true), // Php version
|
||||
'messages' => env('DEBUGBAR_COLLECTORS_MESSAGES', true), // Messages
|
||||
'time' => env('DEBUGBAR_COLLECTORS_TIME', true), // Time Datalogger
|
||||
'memory' => env('DEBUGBAR_COLLECTORS_MEMORY', true), // Memory usage
|
||||
'exceptions' => env('DEBUGBAR_COLLECTORS_EXCEPTIONS', true), // Exception displayer
|
||||
'log' => env('DEBUGBAR_COLLECTORS_LOG', true), // Logs from Monolog (merged in messages if enabled)
|
||||
'db' => env('DEBUGBAR_COLLECTORS_DB', true), // Show database (PDO) queries and bindings
|
||||
'views' => env('DEBUGBAR_COLLECTORS_VIEWS', true), // Views with their data
|
||||
'route' => env('DEBUGBAR_COLLECTORS_ROUTE', true), // Current route information
|
||||
'auth' => env('DEBUGBAR_COLLECTORS_AUTH', true), // Display Laravel authentication status
|
||||
'gate' => env('DEBUGBAR_COLLECTORS_GATE', true), // Display Laravel Gate checks
|
||||
'session' => env('DEBUGBAR_COLLECTORS_SESSION', true), // Display session data
|
||||
'symfony_request' => env('DEBUGBAR_COLLECTORS_SYMFONY_REQUEST', false), // Default Request Data
|
||||
'mail' => env('DEBUGBAR_COLLECTORS_MAIL', true), // Catch mail messages
|
||||
'laravel' => env('DEBUGBAR_COLLECTORS_LARAVEL', true), // Laravel version and environment
|
||||
'events' => env('DEBUGBAR_COLLECTORS_EVENTS', true), // All events fired
|
||||
'logs' => env('DEBUGBAR_COLLECTORS_LOGS', true), // Add the latest log messages
|
||||
'config' => env('DEBUGBAR_COLLECTORS_CONFIG', false), // Display config settings
|
||||
'cache' => env('DEBUGBAR_COLLECTORS_CACHE', true), // Display cache events
|
||||
'models' => env('DEBUGBAR_COLLECTORS_MODELS', true), // Display models
|
||||
'livewire' => env('DEBUGBAR_COLLECTORS_LIVEWIRE', true), // Display Livewire (when available)
|
||||
'inertia' => env('DEBUGBAR_COLLECTORS_INERTIA', true), // Display Inertia (when available)
|
||||
'jobs' => env('DEBUGBAR_COLLECTORS_JOBS', true), // Display dispatched jobs
|
||||
'pennant' => env('DEBUGBAR_COLLECTORS_PENNANT', true), // Display Pennant feature flags
|
||||
'http_client' => env('DEBUGBAR_COLLECTORS_HTTP_CLIENT', true), // Display HTTP Client requests
|
||||
],
|
||||
|
||||
/*
|
||||
@@ -114,56 +71,298 @@ return [
|
||||
*/
|
||||
|
||||
'options' => [
|
||||
'time' => [
|
||||
'memory_usage' => env('DEBUGBAR_OPTIONS_TIME_MEMORY_USAGE', false), // Calculated by subtracting memory start and end, it may be inaccurate
|
||||
],
|
||||
'messages' => [
|
||||
'trace' => env('DEBUGBAR_OPTIONS_MESSAGES_TRACE', true), // Trace the origin of the debug message
|
||||
'backtrace_exclude_paths' => [], // Paths to exclude from backtrace. (in addition to defaults)
|
||||
'capture_dumps' => env('DEBUGBAR_OPTIONS_MESSAGES_CAPTURE_DUMPS', false), // Capture laravel `dump();` as message
|
||||
'timeline' => env('DEBUGBAR_OPTIONS_MESSAGES_TIMELINE', true), // Add messages to the timeline
|
||||
],
|
||||
'memory' => [
|
||||
'reset_peak' => env('DEBUGBAR_OPTIONS_MEMORY_RESET_PEAK', false), // run memory_reset_peak_usage before collecting
|
||||
'with_baseline' => env('DEBUGBAR_OPTIONS_MEMORY_WITH_BASELINE', false), // Set boot memory usage as memory peak baseline
|
||||
'precision' => (int) env('DEBUGBAR_OPTIONS_MEMORY_PRECISION', 0), // Memory rounding precision
|
||||
],
|
||||
'auth' => [
|
||||
'show_name' => false, // Also show the users name/email in the debugbar
|
||||
'show_name' => env('DEBUGBAR_OPTIONS_AUTH_SHOW_NAME', false), // Also show the users name/email in the debugbar
|
||||
'show_guards' => env('DEBUGBAR_OPTIONS_AUTH_SHOW_GUARDS', true), // Show the guards that are used
|
||||
],
|
||||
'gate' => [
|
||||
'trace' => false, // Trace the origin of the Gate checks
|
||||
'timeline' => env('DEBUGBAR_OPTIONS_GATE_TIMELINE', false), // Add mails to the timeline
|
||||
],
|
||||
'db' => [
|
||||
'with_params' => true, // Render SQL with the parameters substituted
|
||||
'timeline' => true, // Add the queries to the timeline
|
||||
'backtrace' => true, // EXPERIMENTAL: Use a backtrace to find the origin of the query in your files.
|
||||
'explain' => [ // EXPERIMENTAL: Show EXPLAIN output on queries
|
||||
'enabled' => false,
|
||||
'types' => ['SELECT'], // array('SELECT', 'INSERT', 'UPDATE', 'DELETE'); for MySQL 5.6.3+
|
||||
'with_params' => env('DEBUGBAR_OPTIONS_WITH_PARAMS', true), // Render SQL with the parameters substituted
|
||||
'exclude_paths' => [ // Paths to exclude entirely from the collector
|
||||
//'vendor/laravel/framework/src/Illuminate/Session', // Exclude sessions queries
|
||||
],
|
||||
'hints' => true, // Show hints for common mistakes
|
||||
'backtrace' => env('DEBUGBAR_OPTIONS_DB_BACKTRACE', true), // Use a backtrace to find the origin of the query in your files.
|
||||
'backtrace_exclude_paths' => [], // Paths to exclude from backtrace. (in addition to defaults)
|
||||
'timeline' => env('DEBUGBAR_OPTIONS_DB_TIMELINE', true), // Add the queries to the timeline
|
||||
'duration_background' => env('DEBUGBAR_OPTIONS_DB_DURATION_BACKGROUND', true), // Show shaded background on each query relative to how long it took to execute.
|
||||
'explain' => [ // Show EXPLAIN output on queries
|
||||
'enabled' => env('DEBUGBAR_OPTIONS_DB_EXPLAIN_ENABLED', false),
|
||||
],
|
||||
'only_slow_queries' => env('DEBUGBAR_OPTIONS_DB_ONLY_SLOW_QUERIES', true), // Only track queries that last longer than `slow_threshold`
|
||||
'slow_threshold' => env('DEBUGBAR_OPTIONS_DB_SLOW_THRESHOLD', false), // Max query execution time (ms). Exceeding queries will be highlighted
|
||||
'memory_usage' => env('DEBUGBAR_OPTIONS_DB_MEMORY_USAGE', false), // Show queries memory usage
|
||||
'soft_limit' => (int) env('DEBUGBAR_OPTIONS_DB_SOFT_LIMIT', 100), // After the soft limit, no parameters/backtrace are captured
|
||||
'hard_limit' => (int) env('DEBUGBAR_OPTIONS_DB_HARD_LIMIT', 500), // After the hard limit, queries are ignored
|
||||
],
|
||||
'mail' => [
|
||||
'full_log' => false,
|
||||
'timeline' => env('DEBUGBAR_OPTIONS_MAIL_TIMELINE', true), // Add mails to the timeline
|
||||
'show_body' => env('DEBUGBAR_OPTIONS_MAIL_SHOW_BODY', true),
|
||||
],
|
||||
'views' => [
|
||||
'data' => false, //Note: Can slow down the application, because the data can be quite large..
|
||||
'timeline' => env('DEBUGBAR_OPTIONS_VIEWS_TIMELINE', true), // Add the views to the timeline
|
||||
'data' => env('DEBUGBAR_OPTIONS_VIEWS_DATA', false), // True for all data, 'keys' for only names, false for no parameters.
|
||||
'group' => (int) env('DEBUGBAR_OPTIONS_VIEWS_GROUP', 50), // Group duplicate views. Pass value to auto-group, or true/false to force
|
||||
'exclude_paths' => [ // Add the paths which you don't want to appear in the views
|
||||
'vendor/filament', // Exclude Filament components by default
|
||||
],
|
||||
],
|
||||
'inertia' => [
|
||||
'pages' => env('DEBUGBAR_OPTIONS_VIEWS_INERTIA_PAGES', 'js/Pages'), // Path for Inertia views
|
||||
],
|
||||
'route' => [
|
||||
'label' => true, // show complete route on bar
|
||||
'label' => env('DEBUGBAR_OPTIONS_ROUTE_LABEL', true), // Show complete route on bar
|
||||
],
|
||||
'session' => [
|
||||
'masked' => [], // List of keys that are masked
|
||||
],
|
||||
'symfony_request' => [
|
||||
'label' => env('DEBUGBAR_OPTIONS_SYMFONY_REQUEST_LABEL', true), // Show route on bar
|
||||
'masked' => [], // List of keys that are masked
|
||||
],
|
||||
'events' => [
|
||||
'data' => env('DEBUGBAR_OPTIONS_EVENTS_DATA', false), // Collect events data
|
||||
'listeners' => env('DEBUGBAR_OPTIONS_EVENTS_LISTENERS', false), // Add listeners to the events data
|
||||
'excluded' => [], // Example: ['eloquent.*', 'composing', Illuminate\Cache\Events\CacheHit::class]
|
||||
],
|
||||
'logs' => [
|
||||
'file' => null,
|
||||
'file' => env('DEBUGBAR_OPTIONS_LOGS_FILE'),
|
||||
],
|
||||
'cache' => [
|
||||
'values' => env('DEBUGBAR_OPTIONS_CACHE_VALUES', true), // Collect cache values
|
||||
'timeline' => env('DEBUGBAR_OPTIONS_CACHE_TIMELINE', false), // Add mails to the timeline
|
||||
],
|
||||
'http_client' => [
|
||||
'masked' => [],
|
||||
'timeline' => env('DEBUGBAR_OPTIONS_HTTP_CLIENT_TIMELINE', true), // Add requests to the timeline
|
||||
],
|
||||
],
|
||||
|
||||
/**
|
||||
* Add any additional DataCollectors by adding the class name of a DataCollector or invokable class.
|
||||
*/
|
||||
'custom_collectors' => [
|
||||
// MyCollector::class => env('DEBUGBAR_COLLECTORS_MYCOLLECTOR', true),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Editor
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Choose your preferred editor to use when clicking file name.
|
||||
|
|
||||
| Supported: "sublime", "textmate", "emacs", "macvim", "codelite",
|
||||
| "phpstorm", "phpstorm-remote", "idea", "idea-remote",
|
||||
| "vscode", "vscode-insiders", "vscode-remote", "vscode-insiders-remote",
|
||||
| "vscodium", "nova", "xdebug", "atom", "espresso",
|
||||
| "netbeans", "cursor", "windsurf", "zed", "antigravity"
|
||||
|
|
||||
*/
|
||||
|
||||
'editor' => env('DEBUGBAR_EDITOR') ?: env('IGNITION_EDITOR', 'phpstorm'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Capture Ajax Requests
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors),
|
||||
| you can use this option to disable sending the data through the headers.
|
||||
|
|
||||
| Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools.
|
||||
|
|
||||
| Note for your request to be identified as ajax requests they must either send the header
|
||||
| X-Requested-With with the value XMLHttpRequest (most JS libraries send this), or have application/json as a Accept header.
|
||||
|
|
||||
| By default `ajax_handler_auto_show` is set to true allowing ajax requests to be shown automatically in the Debugbar.
|
||||
| Changing `ajax_handler_auto_show` to false will prevent the Debugbar from reloading.
|
||||
|
|
||||
| You can defer loading the dataset, so it will be loaded with ajax after the request is done. (Experimental)
|
||||
*/
|
||||
|
||||
'capture_ajax' => env('DEBUGBAR_CAPTURE_AJAX', true),
|
||||
'add_ajax_timing' => env('DEBUGBAR_ADD_AJAX_TIMING', false),
|
||||
'ajax_handler_auto_show' => env('DEBUGBAR_AJAX_HANDLER_AUTO_SHOW', true),
|
||||
'ajax_handler_enable_tab' => env('DEBUGBAR_AJAX_HANDLER_ENABLE_TAB', true),
|
||||
'defer_datasets' => env('DEBUGBAR_DEFER_DATASETS', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Remote Path Mapping
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If you are using a remote dev server, like Laravel Homestead, Docker, or
|
||||
| even a remote VPS, it will be necessary to specify your path mapping.
|
||||
|
|
||||
| Leaving one, or both of these, empty or null will not trigger the remote
|
||||
| URL changes and Debugbar will treat your editor links as local files.
|
||||
|
|
||||
| "remote_sites_path" is an absolute base path for your sites or projects
|
||||
| in Homestead, Vagrant, Docker, or another remote development server.
|
||||
|
|
||||
| Example value: "/home/vagrant/Code"
|
||||
|
|
||||
| "local_sites_path" is an absolute base path for your sites or projects
|
||||
| on your local computer where your IDE or code editor is running on.
|
||||
|
|
||||
| Example values: "/Users/<name>/Code", "C:\Users\<name>\Documents\Code"
|
||||
|
|
||||
*/
|
||||
|
||||
'remote_sites_path' => env('DEBUGBAR_REMOTE_SITES_PATH'),
|
||||
'local_sites_path' => env('DEBUGBAR_LOCAL_SITES_PATH', env('IGNITION_LOCAL_SITES_PATH')),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Storage settings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Debugbar stores data for session/ajax requests.
|
||||
| You can disable this, so the debugbar stores data in headers/session,
|
||||
| but this can cause problems with large data collectors.
|
||||
| By default, file storage (in the storage folder) is used. Sqlite will
|
||||
| create a database file in the storage folder.
|
||||
| Redis and PDO can also be used. For PDO, run the package migrations first.
|
||||
|
|
||||
| Warning: Enabling storage.open will allow everyone to access previous
|
||||
| request, do not enable open storage in publicly available environments!
|
||||
| Specify a callback if you want to limit based on IP or authentication.
|
||||
| Leaving it to null will allow localhost only.
|
||||
*/
|
||||
'storage' => [
|
||||
'enabled' => env('DEBUGBAR_STORAGE_ENABLED', true),
|
||||
'open' => env('DEBUGBAR_OPEN_STORAGE'), // bool/callback.
|
||||
'driver' => env('DEBUGBAR_STORAGE_DRIVER', 'file'), // redis, file, sqlite, pdo, custom
|
||||
'path' => env('DEBUGBAR_STORAGE_PATH', storage_path('debugbar')), // For file driver
|
||||
'connection' => env('DEBUGBAR_STORAGE_CONNECTION'), // Leave null for default connection (Redis/PDO)
|
||||
'provider' => env('DEBUGBAR_STORAGE_PROVIDER', ''), // Instance of StorageInterface for custom driver
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Assets
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Vendor files are included by default, but can be set to false.
|
||||
| This can also be set to 'js' or 'css', to only include javascript or css vendor files.
|
||||
| Vendor files are for css: (none)
|
||||
| and for js: highlight.js
|
||||
| So if you want syntax highlighting, set it to true.
|
||||
|
|
||||
*/
|
||||
'use_dist_files' => env('DEBUGBAR_USE_DIST_FILES', true),
|
||||
'include_vendors' => env('DEBUGBAR_INCLUDE_VENDORS', true),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Custom Error Handler for Deprecated warnings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When enabled, the Debugbar shows deprecated warnings for Symfony components
|
||||
| in the Messages tab.
|
||||
|
|
||||
| You can set a custom error reporting level to filter which errors are
|
||||
| handled. For example, to exclude deprecation warnings:
|
||||
| E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED
|
||||
|
|
||||
| To exclude notices, strict warnings, and deprecations:
|
||||
| E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED & ~E_USER_DEPRECATED
|
||||
|
|
||||
| Defaults to E_ALL (all errors).
|
||||
|
|
||||
*/
|
||||
'error_handler' => env('DEBUGBAR_ERROR_HANDLER', false),
|
||||
'error_level' => env('DEBUGBAR_ERROR_LEVEL', E_ALL),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Clockwork integration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The Debugbar can emulate the Clockwork headers, so you can use the Chrome
|
||||
| Extension, without the server-side code. It uses Debugbar collectors instead.
|
||||
|
|
||||
*/
|
||||
'clockwork' => env('DEBUGBAR_CLOCKWORK', true),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Inject Debugbar in Response
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Usually, the debugbar is added just before <body>, by listening to the
|
||||
| Usually, the debugbar is added just before </body>, by listening to the
|
||||
| Response after the App is done. If you disable this, you have to add them
|
||||
| in your template yourself. See http://phpdebugbar.com/docs/rendering.html
|
||||
|
|
||||
*/
|
||||
|
||||
'inject' => true,
|
||||
'inject' => env('DEBUGBAR_INJECT', true),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DebugBar route prefix
|
||||
| Debugbar route prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Sometimes you want to set route prefix to be used by DebugBar to load
|
||||
| Sometimes you want to set route prefix to be used by Debugbar to load
|
||||
| its resources from. Usually the need comes from misconfigured web server or
|
||||
| from trying to overcome bugs like this: http://trac.nginx.org/nginx/ticket/97
|
||||
|
|
||||
*/
|
||||
'route_prefix' => '_debugbar',
|
||||
'route_prefix' => env('DEBUGBAR_ROUTE_PREFIX', '_debugbar'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Debugbar route middleware
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Additional middleware to run on the Debugbar routes
|
||||
*/
|
||||
'route_middleware' => [],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Debugbar route domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default Debugbar route served from the same domain that request served.
|
||||
| To override default domain, specify it as a non-empty value.
|
||||
*/
|
||||
'route_domain' => env('DEBUGBAR_ROUTE_DOMAIN'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Debugbar theme
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Switches between light and dark theme. If set to auto it will respect system preferences
|
||||
| Possible values: auto, light, dark
|
||||
*/
|
||||
'theme' => env('DEBUGBAR_THEME', 'auto'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Backtrace stack limit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default, the Debugbar limits the number of frames returned by the 'debug_backtrace()' function.
|
||||
| If you need larger stacktraces, you can increase this number. Setting it to 0 will result in no limit.
|
||||
*/
|
||||
'debug_backtrace_limit' => (int) env('DEBUGBAR_DEBUG_BACKTRACE_LIMIT', 50),
|
||||
];
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
return array (
|
||||
'app_version' => 'v8.3.7',
|
||||
'full_app_version' => 'v8.3.7 - build 21025-ga8c268760',
|
||||
'build_version' => '21025',
|
||||
'app_version' => 'v8.4.0',
|
||||
'full_app_version' => 'v8.4.0 - build 21280-g91a95dbc6',
|
||||
'build_version' => '21280',
|
||||
'prerelease_version' => '',
|
||||
'hash_version' => 'ga8c268760',
|
||||
'full_hash' => 'v8.3.7-220-ga8c268760',
|
||||
'hash_version' => 'g91a95dbc6',
|
||||
'full_hash' => 'v8.4.0-475-g91a95dbc6',
|
||||
'branch' => 'develop',
|
||||
);
|
||||
23
database/factories/AccessoryCheckoutFactory.php
Normal file
23
database/factories/AccessoryCheckoutFactory.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class AccessoryCheckoutFactory extends Factory
|
||||
{
|
||||
protected $model = AccessoryCheckout::class;
|
||||
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'created_by' => User::factory(),
|
||||
'accessory_id' => Accessory::factory(),
|
||||
'assigned_to' => User::factory(),
|
||||
'assigned_type' => User::class,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -28,10 +28,10 @@ class AssetModelFactory extends Factory
|
||||
public function definition()
|
||||
{
|
||||
return [
|
||||
'created_by' => User::factory()->superuser(),
|
||||
'name' => $this->faker->catchPhrase(),
|
||||
'category_id' => Category::factory(),
|
||||
'created_by' => User::factory()->superuser(),
|
||||
'model_number' => $this->faker->creditCardNumber(),
|
||||
'name' => $this->faker->catchPhrase(),
|
||||
'notes' => 'Created by demo seeder',
|
||||
'require_serial' => 0,
|
||||
|
||||
|
||||
@@ -23,15 +23,15 @@ class CategoryFactory extends Factory
|
||||
public function definition()
|
||||
{
|
||||
return [
|
||||
'name' => $this->faker->catchPhrase(),
|
||||
'category_type' => 'asset',
|
||||
'checkin_email' => true,
|
||||
'eula_text' => $this->faker->paragraph(),
|
||||
'require_acceptance' => false,
|
||||
'use_default_eula' => false,
|
||||
'created_by' => User::factory()->superuser(),
|
||||
'eula_text' => $this->faker->paragraph(),
|
||||
'name' => $this->faker->catchPhrase(),
|
||||
'notes' => 'Created by DB seeder',
|
||||
'require_acceptance' => false,
|
||||
'tag_color' => $this->faker->hexColor(),
|
||||
'use_default_eula' => false,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -215,4 +215,34 @@ class CategoryFactory extends Factory
|
||||
'require_acceptance' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function sendsCheckinEmail()
|
||||
{
|
||||
return $this->state([
|
||||
'checkin_email' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function doesNotSendCheckinEmail()
|
||||
{
|
||||
return $this->state([
|
||||
'checkin_email' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function hasLocalEula()
|
||||
{
|
||||
return $this->state([
|
||||
'use_default_eula' => false,
|
||||
'eula_text' => 'Some EULA text here',
|
||||
]);
|
||||
}
|
||||
|
||||
public function withNoLocalOrGlobalEula()
|
||||
{
|
||||
return $this->state([
|
||||
'use_default_eula' => false,
|
||||
'eula_text' => '',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user