diff --git a/.github/workflows/docker-alpine.yml b/.github/workflows/docker-alpine.yml index 3139763cbd..f274fc2c40 100644 --- a/.github/workflows/docker-alpine.yml +++ b/.github/workflows/docker-alpine.yml @@ -72,7 +72,7 @@ jobs: # https://github.com/docker/build-push-action - name: Build and push 'snipe-it' image id: docker_build - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: . file: ./Dockerfile.alpine diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index b261dd828b..323d90e41c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -72,7 +72,7 @@ jobs: # https://github.com/docker/build-push-action - name: Build and push 'snipe-it' image id: docker_build - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: . file: ./Dockerfile diff --git a/app/Console/Commands/NormalizeUserNames.php b/app/Console/Commands/NormalizeUserNames.php new file mode 100644 index 0000000000..c3ea5e8ace --- /dev/null +++ b/app/Console/Commands/NormalizeUserNames.php @@ -0,0 +1,52 @@ +info($users->count() . ' users'); + + foreach ($users as $user) { + $user->first_name = ucwords(strtolower($user->first_name)); + $user->last_name = ucwords(strtolower($user->last_name)); + $user->email = strtolower($user->email); + $user->save(); + } + } +} diff --git a/app/Events/UserMerged.php b/app/Events/UserMerged.php new file mode 100644 index 0000000000..b045fdef03 --- /dev/null +++ b/app/Events/UserMerged.php @@ -0,0 +1,24 @@ +merged_from = $from_user; + $this->merged_to = $to_user; + $this->admin = $admin; + } +} diff --git a/app/Http/Controllers/Api/GroupsController.php b/app/Http/Controllers/Api/GroupsController.php index 2c37760629..0e5d391e57 100644 --- a/app/Http/Controllers/Api/GroupsController.php +++ b/app/Http/Controllers/Api/GroupsController.php @@ -8,6 +8,7 @@ use App\Http\Transformers\GroupsTransformer; use App\Models\Group; use Illuminate\Http\Request; + class GroupsController extends Controller { /** @@ -19,6 +20,8 @@ class GroupsController extends Controller */ public function index(Request $request) { + $this->authorize('superadmin'); + $this->authorize('view', Group::class); $allowed_columns = ['id', 'name', 'created_at', 'users_count']; @@ -59,9 +62,11 @@ class GroupsController extends Controller */ public function store(Request $request) { - $this->authorize('create', Group::class); + $this->authorize('superadmin'); $group = new Group; - $group->fill($request->all()); + + $group->name = $request->input('name'); + $group->permissions = $request->input('permissions'); // Todo - some JSON validation stuff here if ($group->save()) { return response()->json(Helper::formatStandardApiResponse('success', $group, trans('admin/groups/message.create.success'))); @@ -80,7 +85,7 @@ class GroupsController extends Controller */ public function show($id) { - $this->authorize('view', Group::class); + $this->authorize('superadmin'); $group = Group::findOrFail($id); return (new GroupsTransformer)->transformGroup($group); @@ -97,9 +102,11 @@ class GroupsController extends Controller */ public function update(Request $request, $id) { - $this->authorize('update', Group::class); + $this->authorize('superadmin'); $group = Group::findOrFail($id); - $group->fill($request->all()); + + $group->name = $request->input('name'); + $group->permissions = $request->input('permissions'); // Todo - some JSON validation stuff here if ($group->save()) { return response()->json(Helper::formatStandardApiResponse('success', $group, trans('admin/groups/message.update.success'))); @@ -118,9 +125,8 @@ class GroupsController extends Controller */ public function destroy($id) { - $this->authorize('delete', Group::class); + $this->authorize('superadmin'); $group = Group::findOrFail($id); - $this->authorize('delete', $group); $group->delete(); return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/groups/message.delete.success'))); diff --git a/app/Http/Controllers/Api/ProfileController.php b/app/Http/Controllers/Api/ProfileController.php index 691efda981..4f5e3b1bdf 100644 --- a/app/Http/Controllers/Api/ProfileController.php +++ b/app/Http/Controllers/Api/ProfileController.php @@ -10,7 +10,7 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Http\Request; use Laravel\Passport\TokenRepository; use Illuminate\Contracts\Validation\Factory as ValidationFactory; -use Gate; +use Illuminate\Support\Facades\Gate; use DB; class ProfileController extends Controller diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index 6bd6d1d090..ff18b87910 100644 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -192,7 +192,6 @@ class UsersController extends Controller } $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; - $offset = (($users) && (request('offset') > $users->count())) ? 0 : request('offset', 0); // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which // case we override with the actual count, so we should return 0 items. @@ -218,6 +217,14 @@ class UsersController extends Controller case 'company': $users = $users->OrderCompany($order); break; + case 'first_name': + $users->orderBy('first_name', $order); + $users->orderBy('last_name', $order); + break; + case 'last_name': + $users->orderBy('last_name', $order); + $users->orderBy('first_name', $order); + break; default: $allowed_columns = [ diff --git a/app/Http/Controllers/Assets/AssetsController.php b/app/Http/Controllers/Assets/AssetsController.php index 323d238ec1..2e45646b9c 100755 --- a/app/Http/Controllers/Assets/AssetsController.php +++ b/app/Http/Controllers/Assets/AssetsController.php @@ -16,7 +16,7 @@ use App\Models\User; use Auth; use Carbon\Carbon; use DB; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Http\Request; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Storage; diff --git a/app/Http/Controllers/Licenses/LicenseFilesController.php b/app/Http/Controllers/Licenses/LicenseFilesController.php index db414edebf..d457d4983a 100644 --- a/app/Http/Controllers/Licenses/LicenseFilesController.php +++ b/app/Http/Controllers/Licenses/LicenseFilesController.php @@ -91,29 +91,30 @@ class LicenseFilesController extends Controller */ public function destroy($licenseId = null, $fileId = null) { - $license = License::find($licenseId); + if ($license = License::find($licenseId)) { - // the asset is valid - if (isset($license->id)) { $this->authorize('update', $license); - $log = Actionlog::find($fileId); - // Remove the file if one exists - if (Storage::exists('licenses/'.$log->filename)) { - try { - Storage::delete('licenses/'.$log->filename); - } catch (\Exception $e) { - \Log::debug($e); + if ($log = Actionlog::find($fileId)) { + + // Remove the file if one exists + if (Storage::exists('licenses/'.$log->filename)) { + try { + Storage::delete('licenses/'.$log->filename); + } catch (\Exception $e) { + \Log::debug($e); + } } + + $log->delete(); + + return redirect()->back() + ->with('success', trans('admin/hardware/message.deletefile.success')); } - $log->delete(); - - return redirect()->back() - ->with('success', trans('admin/hardware/message.deletefile.success')); + return redirect()->route('licenses.index')->with('error', trans('general.log_does_not_exist')); } - // Redirect to the licence management page return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist')); } @@ -129,7 +130,6 @@ class LicenseFilesController extends Controller */ public function show($licenseId = null, $fileId = null, $download = true) { - \Log::info('Private filesystem is: '.config('filesystems.default')); $license = License::find($licenseId); // the license is valid diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index cf5f49feb0..d67d673a21 100755 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -8,7 +8,7 @@ use App\Models\Setting; use App\Models\User; use App\Notifications\CurrentInventory; use Illuminate\Support\Facades\Auth; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Storage; diff --git a/app/Http/Controllers/ReportsController.php b/app/Http/Controllers/ReportsController.php index c0c621ff68..9764df923e 100644 --- a/app/Http/Controllers/ReportsController.php +++ b/app/Http/Controllers/ReportsController.php @@ -1024,7 +1024,11 @@ class ReportsController extends Controller if (is_null($acceptance->created_at)){ return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data')); } else { - $logItem = $assetItem->checkouts()->where('created_at', '=', $acceptance->created_at)->get()[0]; + $logItem_res = $assetItem->checkouts()->where('created_at', '=', $acceptance->created_at)->get(); + if ($logItem_res->isEmpty()){ + return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data')); + } + $logItem = $logItem_res[0]; } if(!$assetItem->assignedTo->locale){ diff --git a/app/Http/Controllers/Users/BulkUsersController.php b/app/Http/Controllers/Users/BulkUsersController.php index 3550b43708..b12683efaf 100644 --- a/app/Http/Controllers/Users/BulkUsersController.php +++ b/app/Http/Controllers/Users/BulkUsersController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Users; +use App\Events\UserMerged; use App\Helpers\Helper; use App\Http\Controllers\Controller; use App\Models\Accessory; @@ -13,6 +14,7 @@ use App\Models\LicenseSeat; use App\Models\ConsumableAssignment; use App\Models\Consumable; use App\Models\User; +use Carbon\Carbon; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; @@ -21,7 +23,7 @@ use Illuminate\Support\Facades\Password; class BulkUsersController extends Controller { /** - * Returns a view that confirms the user's a bulk delete will be applied to. + * Returns a view that confirms the user's a bulk action will be applied to. * * @author [A. Gianotto] [] * @since [v1.7] @@ -35,16 +37,32 @@ class BulkUsersController extends Controller // Make sure there were users selected if (($request->filled('ids')) && (count($request->input('ids')) > 0)) { + // Get the list of affected users $user_raw_array = request('ids'); $users = User::whereIn('id', $user_raw_array) ->with('groups', 'assets', 'licenses', 'accessories')->get(); + // bulk edit, display the bulk edit form if ($request->input('bulk_actions') == 'edit') { return view('users/bulk-edit', compact('users')) ->with('groups', Group::pluck('name', 'id')); + + // bulk delete, display the bulk delete confirmation form } elseif ($request->input('bulk_actions') == 'delete') { return view('users/confirm-bulk-delete')->with('users', $users)->with('statuslabel_list', Helper::statusLabelList()); + + // merge, confirm they have at least 2 users selected and display the merge screen + } elseif ($request->input('bulk_actions') == 'merge') { + + if (($request->filled('ids')) && (count($request->input('ids')) > 1)) { + return view('users/confirm-merge')->with('users', $users); + // Not enough users selected, send them back + } else { + return redirect()->back()->with('error', trans('general.not_enough_users_selected', ['count' => 2])); + } + + // bulk password reset, just do the thing } elseif ($request->input('bulk_actions') == 'bulkpasswordreset') { foreach ($users as $user) { if (($user->activated == '1') && ($user->email != '')) { @@ -59,7 +77,7 @@ class BulkUsersController extends Controller } } - return redirect()->back()->with('error', 'No users selected'); + return redirect()->back()->with('error', trans('general.no_users_selected')); } /** @@ -76,7 +94,7 @@ class BulkUsersController extends Controller $this->authorize('update', User::class); if ((! $request->filled('ids')) || $request->input('ids') <= 0) { - return redirect()->back()->with('error', 'No users selected'); + return redirect()->back()->with('error', trans('general.no_users_selected')); } $user_raw_array = $request->input('ids'); @@ -163,11 +181,11 @@ class BulkUsersController extends Controller $this->authorize('update', User::class); if ((! $request->filled('ids')) || (count($request->input('ids')) == 0)) { - return redirect()->back()->with('error', 'No users selected'); + return redirect()->back()->with('error', trans('general.no_users_selected')); } if (config('app.lock_passwords')) { - return redirect()->route('users.index')->with('error', 'Bulk delete is not enabled in this installation'); + return redirect()->route('users.index')->with('error', trans('general.feature_disabled')); } $user_raw_array = request('ids'); @@ -249,4 +267,80 @@ class BulkUsersController extends Controller $logAction->logaction('checkin from'); } } + + /** + * Save bulk-edited users + * + * @author [A. Gianotto] [] + * @since [v1.0] + * @param Request $request + * @return \Illuminate\Http\RedirectResponse + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function merge(Request $request) + { + $this->authorize('update', User::class); + + if (config('app.lock_passwords')) { + return redirect()->route('users.index')->with('error', trans('general.feature_disabled')); + } + + $user_ids_to_merge = $request->input('ids_to_merge'); + $user_ids_to_merge = array_diff($user_ids_to_merge, array($request->input('merge_into_id'))); + + if ((!$request->filled('merge_into_id')) || (count($user_ids_to_merge) < 1)) { + return redirect()->back()->with('error', trans('general.no_users_selected')); + } + + // Get the users + $merge_into_user = User::find($request->input('merge_into_id')); + $users_to_merge = User::whereIn('id', $user_ids_to_merge)->with('assets', 'licenses', 'consumables','accessories')->get(); + $admin = User::find(Auth::user()->id); + + // Walk users + foreach ($users_to_merge as $user_to_merge) { + + foreach ($user_to_merge->assets as $asset) { + \Log::debug('Updating asset: '.$asset->asset_tag . ' to '.$merge_into_user->id); + $asset->assigned_to = $request->input('merge_into_id'); + $asset->save(); + } + + foreach ($user_to_merge->licenses as $license) { + \Log::debug('Updating license pivot: '.$license->id . ' to '.$merge_into_user->id); + $user_to_merge->licenses()->updateExistingPivot($license->id, ['assigned_to' => $merge_into_user->id]); + } + + foreach ($user_to_merge->consumables as $consumable) { + \Log::debug('Updating consumable pivot: '.$consumable->id . ' to '.$merge_into_user->id); + $user_to_merge->consumables()->updateExistingPivot($consumable->id, ['assigned_to' => $merge_into_user->id]); + } + + foreach ($user_to_merge->accessories as $accessory) { + $user_to_merge->accessories()->updateExistingPivot($accessory->id, ['assigned_to' => $merge_into_user->id]); + } + + foreach ($user_to_merge->userlog as $log) { + $log->target_id = $user_to_merge->id; + $log->save(); + } + + User::where('manager_id', '=', $user_to_merge->id)->update(['manager_id' => $merge_into_user->id]); + + foreach ($user_to_merge->managedLocations as $managedLocation) { + $managedLocation->manager_id = $merge_into_user->id; + $managedLocation->save(); + } + + $user_to_merge->delete(); + //$user_to_merge->save(); + + event(new UserMerged($user_to_merge, $merge_into_user, $admin)); + + } + + return redirect()->route('users.index')->with('success', trans('general.merge_success', ['count' => $users_to_merge->count(), 'into_username' => $merge_into_user->username])); + + + } } diff --git a/app/Http/Livewire/SlackSettingsForm.php b/app/Http/Livewire/SlackSettingsForm.php index f6bc366c20..d2760cb890 100644 --- a/app/Http/Livewire/SlackSettingsForm.php +++ b/app/Http/Livewire/SlackSettingsForm.php @@ -92,7 +92,7 @@ class SlackSettingsForm extends Component $payload = json_encode( [ 'channel' => e($this->webhook_channel), - 'text' => trans('general.webhook_test_msg'), + 'text' => trans('general.webhook_test_msg', ['app' => $this->webhook_name]), 'username' => e($this->webhook_botname), 'icon_emoji' => ':heart:', ]); diff --git a/app/Http/Middleware/CheckPermissions.php b/app/Http/Middleware/CheckPermissions.php index 48df11ba51..a51484f283 100644 --- a/app/Http/Middleware/CheckPermissions.php +++ b/app/Http/Middleware/CheckPermissions.php @@ -3,7 +3,7 @@ namespace App\Http\Middleware; use Closure; -use Gate; +use Illuminate\Support\Facades\Gate; class CheckPermissions { diff --git a/app/Http/Transformers/AccessoriesTransformer.php b/app/Http/Transformers/AccessoriesTransformer.php index 00c30f9ea2..e812beb23e 100644 --- a/app/Http/Transformers/AccessoriesTransformer.php +++ b/app/Http/Transformers/AccessoriesTransformer.php @@ -4,7 +4,7 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\Accessory; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Storage; diff --git a/app/Http/Transformers/AssetMaintenancesTransformer.php b/app/Http/Transformers/AssetMaintenancesTransformer.php index 72ec786f68..6e7c2d5b2c 100644 --- a/app/Http/Transformers/AssetMaintenancesTransformer.php +++ b/app/Http/Transformers/AssetMaintenancesTransformer.php @@ -5,7 +5,7 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\Asset; use App\Models\AssetMaintenance; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; class AssetMaintenancesTransformer diff --git a/app/Http/Transformers/AssetsTransformer.php b/app/Http/Transformers/AssetsTransformer.php index 4b1616026b..740e905d8d 100644 --- a/app/Http/Transformers/AssetsTransformer.php +++ b/app/Http/Transformers/AssetsTransformer.php @@ -5,7 +5,7 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\Asset; use App\Models\Setting; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; diff --git a/app/Http/Transformers/CategoriesTransformer.php b/app/Http/Transformers/CategoriesTransformer.php index 5ea8ee3e01..64550b6370 100644 --- a/app/Http/Transformers/CategoriesTransformer.php +++ b/app/Http/Transformers/CategoriesTransformer.php @@ -4,7 +4,7 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\Category; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Storage; diff --git a/app/Http/Transformers/CompaniesTransformer.php b/app/Http/Transformers/CompaniesTransformer.php index bafe1f9f68..4f1de75dec 100644 --- a/app/Http/Transformers/CompaniesTransformer.php +++ b/app/Http/Transformers/CompaniesTransformer.php @@ -4,7 +4,7 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\Company; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Storage; diff --git a/app/Http/Transformers/ComponentsAssetsTransformer.php b/app/Http/Transformers/ComponentsAssetsTransformer.php index c2c7784fe4..437d00ca22 100644 --- a/app/Http/Transformers/ComponentsAssetsTransformer.php +++ b/app/Http/Transformers/ComponentsAssetsTransformer.php @@ -3,7 +3,7 @@ namespace App\Http\Transformers; use App\Models\Asset; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; class ComponentsAssetsTransformer diff --git a/app/Http/Transformers/ComponentsTransformer.php b/app/Http/Transformers/ComponentsTransformer.php index 20e2fc4abb..1610c3da5b 100644 --- a/app/Http/Transformers/ComponentsTransformer.php +++ b/app/Http/Transformers/ComponentsTransformer.php @@ -4,7 +4,7 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\Component; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Storage; diff --git a/app/Http/Transformers/ConsumablesTransformer.php b/app/Http/Transformers/ConsumablesTransformer.php index b6c3d18748..5079b28961 100644 --- a/app/Http/Transformers/ConsumablesTransformer.php +++ b/app/Http/Transformers/ConsumablesTransformer.php @@ -4,7 +4,7 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\Consumable; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Storage; diff --git a/app/Http/Transformers/DepartmentsTransformer.php b/app/Http/Transformers/DepartmentsTransformer.php index 4f80249adf..7a0d7647d1 100644 --- a/app/Http/Transformers/DepartmentsTransformer.php +++ b/app/Http/Transformers/DepartmentsTransformer.php @@ -4,7 +4,7 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\Department; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Storage; diff --git a/app/Http/Transformers/DepreciationsTransformer.php b/app/Http/Transformers/DepreciationsTransformer.php index 71aa6c97f7..78a01b4c1e 100644 --- a/app/Http/Transformers/DepreciationsTransformer.php +++ b/app/Http/Transformers/DepreciationsTransformer.php @@ -5,7 +5,7 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\Depreciable; use App\Models\Depreciation; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; class DepreciationsTransformer diff --git a/app/Http/Transformers/GroupsTransformer.php b/app/Http/Transformers/GroupsTransformer.php index 313ad113ec..81755afa43 100644 --- a/app/Http/Transformers/GroupsTransformer.php +++ b/app/Http/Transformers/GroupsTransformer.php @@ -4,7 +4,7 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\Group; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; class GroupsTransformer diff --git a/app/Http/Transformers/LicenseSeatsTransformer.php b/app/Http/Transformers/LicenseSeatsTransformer.php index 126361b17a..f82fd3a49f 100644 --- a/app/Http/Transformers/LicenseSeatsTransformer.php +++ b/app/Http/Transformers/LicenseSeatsTransformer.php @@ -4,7 +4,7 @@ namespace App\Http\Transformers; use App\Models\License; use App\Models\LicenseSeat; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; class LicenseSeatsTransformer diff --git a/app/Http/Transformers/LicensesTransformer.php b/app/Http/Transformers/LicensesTransformer.php index 3fb2a8943b..3c389a1b18 100644 --- a/app/Http/Transformers/LicensesTransformer.php +++ b/app/Http/Transformers/LicensesTransformer.php @@ -4,7 +4,7 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\License; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; class LicensesTransformer diff --git a/app/Http/Transformers/ManufacturersTransformer.php b/app/Http/Transformers/ManufacturersTransformer.php index a7ac0e6a42..bbcbda12b4 100644 --- a/app/Http/Transformers/ManufacturersTransformer.php +++ b/app/Http/Transformers/ManufacturersTransformer.php @@ -4,7 +4,7 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\Manufacturer; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Storage; diff --git a/app/Http/Transformers/PredefinedKitsTransformer.php b/app/Http/Transformers/PredefinedKitsTransformer.php index ebd91aef0c..a5d37e5c72 100644 --- a/app/Http/Transformers/PredefinedKitsTransformer.php +++ b/app/Http/Transformers/PredefinedKitsTransformer.php @@ -4,7 +4,7 @@ namespace App\Http\Transformers; use App\Models\PredefinedKit; use App\Models\SnipeModel; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; /** diff --git a/app/Http/Transformers/StatuslabelsTransformer.php b/app/Http/Transformers/StatuslabelsTransformer.php index cc229a590f..41dd336068 100644 --- a/app/Http/Transformers/StatuslabelsTransformer.php +++ b/app/Http/Transformers/StatuslabelsTransformer.php @@ -4,7 +4,7 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\Statuslabel; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; class StatuslabelsTransformer diff --git a/app/Http/Transformers/SuppliersTransformer.php b/app/Http/Transformers/SuppliersTransformer.php index 76b2a01514..71307a750d 100644 --- a/app/Http/Transformers/SuppliersTransformer.php +++ b/app/Http/Transformers/SuppliersTransformer.php @@ -4,7 +4,7 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\Supplier; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Storage; diff --git a/app/Http/Transformers/UsersTransformer.php b/app/Http/Transformers/UsersTransformer.php index 60ec094e8b..9447d65455 100644 --- a/app/Http/Transformers/UsersTransformer.php +++ b/app/Http/Transformers/UsersTransformer.php @@ -4,7 +4,7 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\User; -use Gate; +use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; class UsersTransformer diff --git a/app/Listeners/LogListener.php b/app/Listeners/LogListener.php index 791a84f57a..d14b674364 100644 --- a/app/Listeners/LogListener.php +++ b/app/Listeners/LogListener.php @@ -18,7 +18,9 @@ use App\Events\ItemDeclined; use App\Events\LicenseCheckedIn; use App\Events\LicenseCheckedOut; use App\Models\Actionlog; +use App\Models\User; use App\Models\LicenseSeat; +use App\Events\UserMerged; class LogListener { @@ -87,6 +89,43 @@ class LogListener $logaction->save(); } + + public function onUserMerged(UserMerged $event) + { + + $to_from_array = [ + 'to_id' => $event->merged_to->id, + 'to_username' => $event->merged_to->username, + 'from_id' => $event->merged_from->id, + 'from_username' => $event->merged_from->username, + ]; + + // Add a record to the users being merged FROM + \Log::debug('Users merged: '.$event->merged_from->id .' ('.$event->merged_from->username.') merged into '. $event->merged_to->id. ' ('.$event->merged_to->username.')'); + $logaction = new Actionlog(); + $logaction->item_id = $event->merged_from->id; + $logaction->item_type = User::class; + $logaction->target_id = $event->merged_to->id; + $logaction->target_type = User::class; + $logaction->action_type = 'merged'; + $logaction->note = trans('general.merged_log_this_user_from', $to_from_array); + $logaction->user_id = $event->admin->id; + $logaction->save(); + + // Add a record to the users being merged TO + $logaction = new Actionlog(); + $logaction->target_id = $event->merged_from->id; + $logaction->target_type = User::class; + $logaction->item_id = $event->merged_to->id; + $logaction->item_type = User::class; + $logaction->action_type = 'merged'; + $logaction->note = trans('general.merged_log_this_user_into', $to_from_array); + $logaction->user_id = $event->admin->id; + $logaction->save(); + + + } + /** * Register the listeners for the subscriber. * @@ -99,6 +138,7 @@ class LogListener 'CheckoutableCheckedOut', 'CheckoutAccepted', 'CheckoutDeclined', + 'UserMerged', ]; foreach ($list as $event) { @@ -108,4 +148,6 @@ class LogListener ); } } + + } diff --git a/app/Models/Group.php b/app/Models/Group.php index a68579adac..c0de8c263d 100755 --- a/app/Models/Group.php +++ b/app/Models/Group.php @@ -16,6 +16,11 @@ class Group extends SnipeModel 'name' => 'required|min:2|max:255', ]; + protected $fillable = [ + 'name', + 'permissions' + ]; + /** * Whether the model should inject it's identifier to the unique * validation rules before attempting validation. If this property diff --git a/app/Presenters/ActionlogPresenter.php b/app/Presenters/ActionlogPresenter.php index 617cdb6a2a..cd581d33ce 100644 --- a/app/Presenters/ActionlogPresenter.php +++ b/app/Presenters/ActionlogPresenter.php @@ -50,6 +50,8 @@ class ActionlogPresenter extends Presenter return 'far fa-save'; } elseif ($this->itemType() == 'component') { return 'far fa-hdd'; + } elseif ($this->itemType() == 'user') { + return 'fa-solid fa-people-arrows'; } } diff --git a/config/logging.php b/config/logging.php index 65b717750d..7cb5d86039 100644 --- a/config/logging.php +++ b/config/logging.php @@ -112,27 +112,31 @@ $config = [ 'handler' => \Rollbar\Laravel\MonologHandler::class, 'access_token' => env('ROLLBAR_TOKEN'), 'level' => env('ROLLBAR_LEVEL', 'error'), - 'check_ignore' => function($isUncaught, $args, $payload) { - if (App::environment('production') && is_object($args) && get_class($args) == Rollbar\ErrorWrapper::class && $args->errorLevel == E_WARNING ) { - \Log::info("IGNORING E_WARNING in production mode: ".$args->getMessage()); - return true; // "TRUE - you should ignore it!" - } - $needle = "ArieTimmerman\\Laravel\\SCIMServer\\Exceptions\\SCIMException"; - if (App::environment('production') && is_string($args) && strncmp($args, $needle, strlen($needle) ) === 0 ) { - \Log::info("String: '$args' looks like a SCIM Exception; ignoring error"); - return true; //yes, *do* ignore it - } - return false; - }, ], ], ]; -// Only add rollbar if the .env has a rollbar token if ((env('APP_ENV')=='production') && (env('ROLLBAR_TOKEN'))) { + // Only add rollbar if the .env has a rollbar token $config['channels']['stack']['channels'] = ['single', 'rollbar']; + + // and only add the rollbar filter under the same conditions + // Note: it will *not* be cacheable + $config['channels']['rollbar']['check_ignore'] = function ($isUncaught, $args, $payload) { + if (App::environment('production') && is_object($args) && get_class($args) == Rollbar\ErrorWrapper::class && $args->errorLevel == E_WARNING ) { + \Log::info("IGNORING E_WARNING in production mode: ".$args->getMessage()); + return true; // "TRUE - you should ignore it!" + } + $needle = "ArieTimmerman\\Laravel\\SCIMServer\\Exceptions\\SCIMException"; + if (App::environment('production') && is_string($args) && strncmp($args, $needle, strlen($needle) ) === 0 ) { + \Log::info("String: '$args' looks like a SCIM Exception; ignoring error"); + return true; //yes, *do* ignore it + } + return false; + }; + } diff --git a/package-lock.json b/package-lock.json index a803788bfb..327f8ede3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1533,9 +1533,9 @@ } }, "@types/eslint": { - "version": "8.4.5", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", - "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", + "version": "8.21.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.2.tgz", + "integrity": "sha512-EMpxUyystd3uZVByZap1DACsMXvb82ypQnGn89e1Y0a+LYu3JJscUd/gqhRsVFDkaD2MIiWo0MT8EfXr3DGRKw==", "requires": { "@types/estree": "*", "@types/json-schema": "*" @@ -4768,9 +4768,9 @@ } }, "enhanced-resolve": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", - "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", + "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", "requires": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -19758,9 +19758,9 @@ } }, "webpack": { - "version": "5.74.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", - "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", + "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", "requires": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^0.0.51", diff --git a/package.json b/package.json index 25a082bc8b..442513a9f9 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,6 @@ "tableexport.jquery.plugin": "1.26.0", "tether": "^1.4.0", "vue-resource": "^1.5.2", - "webpack": "^5.74.0" + "webpack": "^5.76.0" } } diff --git a/resources/lang/en/admin/users/general.php b/resources/lang/en/admin/users/general.php index a3b1699e2d..3ef8b9a948 100644 --- a/resources/lang/en/admin/users/general.php +++ b/resources/lang/en/admin/users/general.php @@ -50,4 +50,5 @@ return [ 'email_credentials' => 'Email credentials', 'email_credentials_text' => 'Email my credentials to the email address above', 'next_save_user' => 'Next: Save User', + 'all_assigned_list_generation' => 'Generated on:' ]; diff --git a/resources/lang/en/general.php b/resources/lang/en/general.php index 97be38f509..8b6063f421 100644 --- a/resources/lang/en/general.php +++ b/resources/lang/en/general.php @@ -45,7 +45,7 @@ return [ 'bulk_edit' => 'Bulk Edit', 'bulk_delete' => 'Bulk Delete', 'bulk_actions' => 'Bulk Actions', - 'bulk_checkin_delete' => 'Bulk Checkin Items from Users', + 'bulk_checkin_delete' => 'Bulk Checkin / Delete Users', 'byod' => 'BYOD', 'byod_help' => 'This device is owned by the user', 'bystatus' => 'by Status', @@ -407,7 +407,16 @@ return [ 'true' => 'True', 'false' => 'False', 'integration_option' => 'Integration Option', - + 'log_does_not_exist' => 'No matching log record exists.', + 'merge_users' => 'Merge Users', + 'merge_information' => 'This will merge the :count users into a single user. Select the user you wish to merge the others into below, and the associated assets, licences, etc will be moved over to the selected user and the other users will be marked as deleted.', + 'warning_merge_information' => 'This action CANNOT be undone and should ONLY be used when you need to merge users because of a bad import or sync. Be sure to run a backup first.', + 'no_users_selected' => 'No users selected', + 'not_enough_users_selected' => 'At least :count users must be selected', + 'merge_success' => ':count users merged successfully into :into_username!', + 'merged' => 'merged', + 'merged_log_this_user_into' => 'Merged this user (ID :to_id - :to_username) into user ID :from_id (:from_username) ', + 'merged_log_this_user_from' => 'Merged user ID :from_id (:from_username) into this user (ID :to_id - :to_username)', ]; \ No newline at end of file diff --git a/resources/views/livewire/slack-settings-form.blade.php b/resources/views/livewire/slack-settings-form.blade.php index 7cce7503c6..ce6ba52c0e 100644 --- a/resources/views/livewire/slack-settings-form.blade.php +++ b/resources/views/livewire/slack-settings-form.blade.php @@ -134,7 +134,7 @@ - @if($webhook_selected == 'Slack' || $webhook_selected == 'Discord') + @if($webhook_selected == 'slack') @if($webhook_endpoint != null && $webhook_channel != null)
diff --git a/resources/views/partials/asset-bulk-actions.blade.php b/resources/views/partials/asset-bulk-actions.blade.php index e7f8368296..6a94ad4871 100644 --- a/resources/views/partials/asset-bulk-actions.blade.php +++ b/resources/views/partials/asset-bulk-actions.blade.php @@ -12,7 +12,7 @@ {{ trans('button.bulk_actions') }} - @can('update', \App\Models\Asset::class) @endcan diff --git a/resources/views/partials/users-bulk-actions.blade.php b/resources/views/partials/users-bulk-actions.blade.php index 016f549214..cf22fde03d 100644 --- a/resources/views/partials/users-bulk-actions.blade.php +++ b/resources/views/partials/users-bulk-actions.blade.php @@ -9,9 +9,10 @@ @can('delete', \App\Models\User::class)
- + diff --git a/resources/views/users/confirm-bulk-delete.blade.php b/resources/views/users/confirm-bulk-delete.blade.php index 663602bd36..5393b9b817 100644 --- a/resources/views/users/confirm-bulk-delete.blade.php +++ b/resources/views/users/confirm-bulk-delete.blade.php @@ -106,8 +106,10 @@
diff --git a/resources/views/users/confirm-merge.blade.php b/resources/views/users/confirm-merge.blade.php new file mode 100644 index 0000000000..c8d87916a4 --- /dev/null +++ b/resources/views/users/confirm-merge.blade.php @@ -0,0 +1,143 @@ +@extends('layouts/default') +{{-- Page title --}} +@section('title') + {!! trans('general.merge_users') !!} + @parent +@stop + +{{-- Page content --}} +@section('content') + +
+
+
+
+
+ +
+

+ {{ trans('general.merge_information', array('count' => count($users))) }} +

+
+ + {{csrf_field()}} +
+
+
+ + {{ trans('general.warning_merge_information') }} +
+
+
+ + @if (config('app.lock_passwords')) +
+
+
+

{{ trans('general.feature_disabled') }}

+
+
+
+ @endif + +
+
+ + + + + + + + + + + + + + + @foreach ($users as $user) + isSuperUser() ? ' class="danger"':'') !!}> + + + + + + + + + + + @endforeach + + +
{{ trans('general.name') }}{{ trans('general.email') }}{{ trans('general.username') }}{{ trans('general.groups') }} + + {{ trans('general.assets') }} + + + {{ trans('general.accessories') }} + + + {{ trans('general.licenses') }} + + + {{ trans('general.consumables') }} +
+ + + + {{ $user->email }} + + + {{ $user->username }} + + @foreach ($user->groups as $group) + + {{ $group->name }} +   + @endforeach + + {{ number_format($user->assets()->count()) }} + + {{ number_format($user->accessories()->count()) }} + + {{ number_format($user->licenses()->count()) }} + + {{ number_format($user->consumables()->count()) }} +
+
+
+
+ + + @foreach ($users as $user) + + @endforeach + +
+
+
+
+ +@stop + +@section('moar_scripts') + + @if (!(config('app.lock_passwords'))) + + + @endif + +@stop \ No newline at end of file diff --git a/resources/views/users/print.blade.php b/resources/views/users/print.blade.php index 08ff7baf73..ebcfd170bd 100644 --- a/resources/views/users/print.blade.php +++ b/resources/views/users/print.blade.php @@ -2,7 +2,7 @@ - {{ trans('general.assigned_to', ['name' => $show_user->present()->fullName()]) }} + {{ trans('general.assigned_to', ['name' => $show_user->present()->fullName()]) }} - {{ date('Y-m-d H:i', time()) }}