From f0bf77735df2502c9eda34850d1376224a6cbe94 Mon Sep 17 00:00:00 2001 From: akemidx Date: Thu, 3 Apr 2025 23:34:04 -0400 Subject: [PATCH 01/15] adding last name only as a email choice --- app/Models/User.php | 2 ++ resources/lang/en-US/general.php | 1 + resources/macros/macros.php | 1 + tests/Unit/UserTest.php | 8 ++++++++ 4 files changed, 12 insertions(+) diff --git a/app/Models/User.php b/app/Models/User.php index fac4e69e47..69e3e33e68 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -622,6 +622,8 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo $username = str_slug($first_name).'_'.str_slug($last_name); } elseif ($format == 'firstname') { $username = str_slug($first_name); + } elseif ($format == 'lastname') { + $username = str_slug($last_name); } elseif ($format == 'firstinitial.lastname') { $username = str_slug(substr($first_name, 0, 1).'.'.str_slug($last_name)); } elseif ($format == 'lastname_firstinitial') { diff --git a/resources/lang/en-US/general.php b/resources/lang/en-US/general.php index e20c7dfd54..5fd7dfb1f5 100644 --- a/resources/lang/en-US/general.php +++ b/resources/lang/en-US/general.php @@ -185,6 +185,7 @@ return [ 'last' => 'Last', 'last_login' => 'Last Login', 'last_name' => 'Last Name', + 'last_name_format' => 'Last Name (doe@example.com)', 'license' => 'License', 'license_report' => 'License Report', 'licenses_available' => 'Licenses available', diff --git a/resources/macros/macros.php b/resources/macros/macros.php index debf3241c5..19ef05fb05 100644 --- a/resources/macros/macros.php +++ b/resources/macros/macros.php @@ -193,6 +193,7 @@ Form::macro('username_format', function ($name = 'username_format', $selected = $formats = [ 'firstname.lastname' => trans('general.firstname_lastname_format'), 'firstname' => trans('general.first_name_format'), + 'lastname' => trans('general.last_name_format'), 'filastname' => trans('general.filastname_format'), 'lastnamefirstinitial' => trans('general.lastnamefirstinitial_format'), 'firstname_lastname' => trans('general.firstname_lastname_underscore_format'), diff --git a/tests/Unit/UserTest.php b/tests/Unit/UserTest.php index d47a49934d..ed4b403498 100644 --- a/tests/Unit/UserTest.php +++ b/tests/Unit/UserTest.php @@ -24,6 +24,14 @@ class UserTest extends TestCase $this->assertEquals($expected_username, $user['username']); } + public function testLastName() + { + $fullname = "Natalia Allanovna Romanova-O'Shostakova"; + $expected_username = 'allanovna-romanova-oshostakova'; + $user = User::generateFormattedNameFromFullName($fullname, 'lastname'); + $this->assertEquals($expected_username, $user['username']); + } + public function testFirstNameDotLastName() { $fullname = "Natalia Allanovna Romanova-O'Shostakova"; From a56f6148fcaf93eb6377717507f0f48f633fdae3 Mon Sep 17 00:00:00 2001 From: fvollmer Date: Mon, 31 Mar 2025 12:43:20 +0200 Subject: [PATCH 02/15] Improve Settings: Remove username and password requirement for ldap Since 9d62793 anonymous LDAP login is available. Remove username and password requirement in settings dialog. --- app/Http/Requests/StoreLdapSettings.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/Http/Requests/StoreLdapSettings.php b/app/Http/Requests/StoreLdapSettings.php index 4197145046..ce0fa41bf5 100644 --- a/app/Http/Requests/StoreLdapSettings.php +++ b/app/Http/Requests/StoreLdapSettings.php @@ -27,8 +27,6 @@ class StoreLdapSettings extends FormRequest 'ldap_auth_filter_query' => 'not_in:uid=samaccountname|required_if:ldap_enabled,1', 'ldap_filter' => 'nullable|regex:"^[^(]"|required_if:ldap_enabled,1', 'ldap_server' => 'nullable|required_if:ldap_enabled,1|starts_with:ldap://,ldaps://', - 'ldap_uname' => 'nullable|required_if:ldap_enabled,1', - 'ldap_pword' => 'nullable|required_if:ldap_enabled,1', 'ldap_basedn' => 'nullable|required_if:ldap_enabled,1', 'ldap_fname_field' => 'nullable|required_if:ldap_enabled,1', 'custom_forgot_pass_url' => 'nullable|url', From 275e1beda28673ed7dafaf2d41216c60bbab46a3 Mon Sep 17 00:00:00 2001 From: akemidx Date: Tue, 8 Apr 2025 13:15:28 -0400 Subject: [PATCH 03/15] updating for conflicts and adding test --- tests/Unit/UserTest.php | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/tests/Unit/UserTest.php b/tests/Unit/UserTest.php index 0d2b41a780..f54969a5da 100644 --- a/tests/Unit/UserTest.php +++ b/tests/Unit/UserTest.php @@ -24,13 +24,6 @@ class UserTest extends TestCase $this->assertEquals($expected_username, $user['username']); } - public function testLastName() - { - $fullname = "Natalia Allanovna Romanova-O'Shostakova"; - $expected_username = 'allanovna-romanova-oshostakova'; - $user = User::generateFormattedNameFromFullName($fullname, 'lastname'); - $this->assertEquals($expected_username, $user['username']); - public function testFirstNameEmail() { $fullname = "Natalia Allanovna Romanova-O'Shostakova"; @@ -39,6 +32,22 @@ class UserTest extends TestCase $this->assertEquals($expected_email, $user['username'] . '@example.com'); } + public function testLastName() + { + $fullname = "Natalia Allanovna Romanova-O'Shostakova"; + $expected_username = 'allanovna-romanova-oshostakova'; + $user = User::generateFormattedNameFromFullName($fullname, 'lastname'); + $this->assertEquals($expected_username, $user['username']); + } + + public function testLastNameEmail() + { + $fullname = "Natalia Allanovna Romanova-O'Shostakova"; + $expected_username = 'allanovna-romanova-oshostakova@example.com'; + $user = User::generateFormattedNameFromFullName($fullname, 'lastname'); + $this->assertEquals($expected_username, $user['username'] . '@example.com'); + } + public function testFirstNameDotLastName() { $fullname = "Natalia Allanovna Romanova-O'Shostakova"; From 7701c6097f9393de9947adde4421801ebdaa21e7 Mon Sep 17 00:00:00 2001 From: akemidx Date: Tue, 8 Apr 2025 13:21:32 -0400 Subject: [PATCH 04/15] adding username split --- resources/lang/en-US/admin/settings/general.php | 1 + resources/macros/macros.php | 1 + 2 files changed, 2 insertions(+) diff --git a/resources/lang/en-US/admin/settings/general.php b/resources/lang/en-US/admin/settings/general.php index c345952386..fd1fefaf36 100644 --- a/resources/lang/en-US/admin/settings/general.php +++ b/resources/lang/en-US/admin/settings/general.php @@ -398,6 +398,7 @@ return [ 'text' => 'Text', 'firstname_lastname_format' => 'First Name Last Name (jane.smith)', 'first_name_format' => 'First Name (jane)', + 'last_name_format' => 'Last Name (doe)', 'filastname_format' => 'First Initial Last Name (jsmith)', 'lastnamefirstinitial_format' => 'Last Name First Initial (smithj)', 'firstname_lastname_underscore_format' => 'First Name Last Name (jane_smith)', diff --git a/resources/macros/macros.php b/resources/macros/macros.php index a763c1f482..ba7aa1386a 100644 --- a/resources/macros/macros.php +++ b/resources/macros/macros.php @@ -219,6 +219,7 @@ Form::macro('username_format', function ($name = 'username_format', $selected = $formats = [ 'firstname.lastname' => trans('admin/settings/general.firstname_lastname_format'), 'firstname' => trans('admin/settings/general.first_name_format'), + 'lastname' => trans('admin/settings/general.last_name_format'), 'filastname' => trans('admin/settings/general.filastname_format'), 'lastnamefirstinitial' => trans('admin/settings/general.lastnamefirstinitial_format'), 'firstname_lastname' => trans('admin/settings/general.firstname_lastname_underscore_format'), From 7be3d6072fbc97122ae1c0e6b9d5cf65eb0c442f Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 9 Apr 2025 05:52:35 +0100 Subject: [PATCH 05/15] Removed erroneous comment Signed-off-by: snipe --- app/Http/Controllers/Api/AssetsController.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/Http/Controllers/Api/AssetsController.php b/app/Http/Controllers/Api/AssetsController.php index d60ded3919..1b409f6b71 100644 --- a/app/Http/Controllers/Api/AssetsController.php +++ b/app/Http/Controllers/Api/AssetsController.php @@ -436,12 +436,6 @@ class AssetsController extends Controller }]); } - - - /** - * Here we're just determining which Transformer (via $transformer) to use based on the - * variables we set earlier on in this method - we default to AssetsTransformer. - */ return (new $transformer)->transformAssets($assets, $total, $request); } From f3f26b3824e5ce65e0c6b961bc54d0a1d0f3a598 Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 9 Apr 2025 05:52:48 +0100 Subject: [PATCH 06/15] Added UploadedFilesTransformer Signed-off-by: snipe --- .../Transformers/UploadedFilesTransformer.php | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 app/Http/Transformers/UploadedFilesTransformer.php diff --git a/app/Http/Transformers/UploadedFilesTransformer.php b/app/Http/Transformers/UploadedFilesTransformer.php new file mode 100644 index 0000000000..a18c9f9b65 --- /dev/null +++ b/app/Http/Transformers/UploadedFilesTransformer.php @@ -0,0 +1,56 @@ +transformDatatables($array, $total); + } + + + public function transformFile(Actionlog $file) + { + $snipeModel = $file->item_type; + + + // This will be used later as we extend out this transformer to handle more types of uploads + if ($file->item_type == Asset::class) { + $file_url = route('show/assetfile', [$file->item_id, $file->id]); + } + + $array = [ + 'id' => (int) $file->id, + 'filename' => e($file->filename), + 'url' => $file_url, + 'created_by' => ($file->adminuser) ? [ + 'id' => (int) $file->adminuser->id, + 'name'=> e($file->adminuser->present()->fullName), + ] : null, + 'created_at' => Helper::getFormattedDateObject($file->created_at, 'datetime'), + 'updated_at' => Helper::getFormattedDateObject($file->updated_at, 'datetime'), + 'deleted_at' => Helper::getFormattedDateObject($file->deleted_at, 'datetime'), + ]; + + $permissions_array['available_actions'] = [ + 'delete' => (Gate::allows('update', $snipeModel) && ($file->deleted_at == '')), + ]; + + $array += $permissions_array; + return $array; + } + +} From ce0bd68716e0c9ebdb2636d77a4d5361f6307fd5 Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 9 Apr 2025 05:52:59 +0100 Subject: [PATCH 07/15] RMB for file routes Signed-off-by: snipe --- routes/api.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routes/api.php b/routes/api.php index 5724990c7e..60e7c3cddf 100644 --- a/routes/api.php +++ b/routes/api.php @@ -549,14 +549,14 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi ] )->name('api.assets.restore'); - Route::post('{asset_id}/files', + Route::post('{asset}/files', [ Api\AssetFilesController::class, 'store' ] )->name('api.assets.files.store'); - Route::get('{asset_id}/files', + Route::get('{asset}/files', [ Api\AssetFilesController::class, 'list' From f29146b319925d75b2e661c4c5837683d12304a4 Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 9 Apr 2025 05:53:24 +0100 Subject: [PATCH 08/15] Fixed asset file listing display at API endpoint Signed-off-by: snipe --- .../Controllers/Api/AssetFilesController.php | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/app/Http/Controllers/Api/AssetFilesController.php b/app/Http/Controllers/Api/AssetFilesController.php index 4369d287d5..fabe9ebbb3 100644 --- a/app/Http/Controllers/Api/AssetFilesController.php +++ b/app/Http/Controllers/Api/AssetFilesController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Api; use App\Helpers\StorageHelper; +use App\Http\Transformers\UploadedFilesTransformer; use Illuminate\Support\Facades\Storage; use App\Helpers\Helper; use App\Http\Controllers\Controller; @@ -13,6 +14,7 @@ use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Log; use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Component\HttpFoundation\BinaryFileResponse; +use Illuminate\Http\Request; /** @@ -72,33 +74,37 @@ class AssetFilesController extends Controller * @since [v6.0] * @author [T. Scarsbrook] [] */ - public function list($assetId = null) : JsonResponse + public function list(Asset $asset, Request $request) : JsonResponse | array { - // Start by checking if the asset being acted upon exists - if (! $asset = Asset::find($assetId)) { - return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404); - } - - // the asset is valid - if (isset($asset->id)) { - $this->authorize('view', $asset); - // Check that there are some uploads on this asset that can be listed - if ($asset->uploads->count() > 0) { - $files = array(); - foreach ($asset->uploads as $upload) { - array_push($files, $upload); - } - // Give the list of files back to the user - return response()->json(Helper::formatStandardApiResponse('success', $files, trans('admin/hardware/message.upload.success'))); - } + $this->authorize('view', $asset); - // There are no files. - return response()->json(Helper::formatStandardApiResponse('success', array(), trans('admin/hardware/message.upload.success'))); + $allowed_columns = + [ + 'id', + 'filename', + 'eol', + 'notes', + 'created_at', + 'updated_at', + ]; + + $files = Actionlog::select('action_logs.*')->where('action_type', '=', 'uploaded')->where('item_type', '=', Asset::class)->where('item_id', '=', $asset->id); + + if ($request->filled('search')) { + $files = $files->TextSearch($request->input('search')); } - // Send back an error message - return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.error')), 500); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $files->count()) ? $files->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); + $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; + $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at'; + $files = $files->orderBy($sort, $order); + + $files = $files->skip($offset)->take($limit)->get(); + return (new UploadedFilesTransformer())->transformFiles($files, $files->count()); + } /** @@ -111,12 +117,8 @@ class AssetFilesController extends Controller * @since [v6.0] * @author [T. Scarsbrook] [] */ - public function show($assetId = null, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse + public function show(Asset $asset, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse { - // Start by checking if the asset being acted upon exists - if (! $asset = Asset::find($assetId)) { - return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404); - } // the asset is valid if (isset($asset->id)) { @@ -164,12 +166,8 @@ class AssetFilesController extends Controller * @since [v6.0] * @author [T. Scarsbrook] [] */ - public function destroy($assetId = null, $fileId = null) : JsonResponse + public function destroy(Asset $asset, $fileId = null) : JsonResponse { - // Start by checking if the asset being acted upon exists - if (! $asset = Asset::find($assetId)) { - return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404); - } $rel_path = 'private_uploads/assets'; @@ -179,12 +177,14 @@ class AssetFilesController extends Controller // Check for the file $log = Actionlog::find($fileId); - if ($log) { - // Check the file actually exists, and delete it - if (Storage::exists($rel_path.'/'.$log->filename)) { - Storage::delete($rel_path.'/'.$log->filename); - } - // Delete the record of the file + + if ($log) { + // Check the file actually exists, and delete it + if (Storage::exists($rel_path.'/'.$log->filename)) { + Storage::delete($rel_path.'/'.$log->filename); + } + + // Delete the record of the file $log->delete(); // All deleting done - notify the user of success From 41fb058adb92e003e06a95ee88268138eeb2e045 Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 9 Apr 2025 06:06:44 +0100 Subject: [PATCH 09/15] Added gates around printing location assets Signed-off-by: snipe --- app/Http/Controllers/LocationsController.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/LocationsController.php b/app/Http/Controllers/LocationsController.php index 9e0bd6bfb1..da4e6a7e44 100755 --- a/app/Http/Controllers/LocationsController.php +++ b/app/Http/Controllers/LocationsController.php @@ -66,6 +66,7 @@ class LocationsController extends Controller public function store(ImageUploadRequest $request) : RedirectResponse { $this->authorize('create', Location::class); + $location = new Location(); $location->name = $request->input('name'); $location->parent_id = $request->input('parent_id', null); @@ -150,7 +151,7 @@ class LocationsController extends Controller if (Setting::getSettings()->scope_locations_fmcs) { $location->company_id = Company::getIdForCurrentUser($request->input('company_id')); // check if there are related objects with different company - if (Helper::test_locations_fmcs(false, $locationId, $location->company_id)) { + if (Helper::test_locations_fmcs(false, $location->id, $location->company_id)) { return redirect()->back()->withInput()->withInput()->with('error', 'error scoped locations'); } } else { @@ -176,6 +177,7 @@ class LocationsController extends Controller public function destroy($locationId) : RedirectResponse { $this->authorize('delete', Location::class); + if (is_null($location = Location::find($locationId))) { return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.does_not_exist')); } @@ -212,6 +214,8 @@ class LocationsController extends Controller */ public function show(Location $location) : View | RedirectResponse { + $this->authorize('view', Location::class); + $location = Location::withCount('assignedAssets as assigned_assets_count') ->withCount('assets as assets_count') ->withCount('rtd_assets as rtd_assets_count') @@ -229,6 +233,8 @@ class LocationsController extends Controller public function print_assigned($id) : View | RedirectResponse { + $this->authorize('view', Location::class); + if ($location = Location::where('id', $id)->first()) { $parent = Location::where('id', $location->parent_id)->first(); $manager = User::where('id', $location->manager_id)->first(); @@ -313,6 +319,7 @@ class LocationsController extends Controller } public function print_all_assigned($id) : View | RedirectResponse { + $this->authorize('view', Location::class); if ($location = Location::where('id', $id)->first()) { $parent = Location::where('id', $location->parent_id)->first(); $manager = User::where('id', $location->manager_id)->first(); @@ -339,6 +346,8 @@ class LocationsController extends Controller */ public function postBulkDelete(Request $request) : View | RedirectResponse { + $this->authorize('update', Location::class); + $locations_raw_array = $request->input('ids'); // Make sure some IDs have been selected @@ -372,6 +381,8 @@ class LocationsController extends Controller */ public function postBulkDeleteStore(Request $request) : RedirectResponse { + $this->authorize('delete', Location::class); + $locations_raw_array = $request->input('ids'); if ((is_array($locations_raw_array)) && (count($locations_raw_array) > 0)) { From 04f8ebb4d8f2bd9acf467c1f4bc41547109cd962 Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 9 Apr 2025 06:17:08 +0100 Subject: [PATCH 10/15] Added tests Signed-off-by: snipe --- .../Feature/Locations/Ui/ShowLocationTest.php | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/Feature/Locations/Ui/ShowLocationTest.php b/tests/Feature/Locations/Ui/ShowLocationTest.php index 394ed73f4c..023ba06dff 100644 --- a/tests/Feature/Locations/Ui/ShowLocationTest.php +++ b/tests/Feature/Locations/Ui/ShowLocationTest.php @@ -14,4 +14,27 @@ class ShowLocationTest extends TestCase ->get(route('locations.show', Location::factory()->create())) ->assertOk(); } + + public function testDeniesAccessToRegularUser() + { + $this->actingAs(User::factory()->create()) + ->get(route('locations.show', Location::factory()->create())) + ->assertStatus(403) + ->assertForbidden(); + } + + public function testDeniesPrintAccessToRegularUser() + { + $this->actingAs(User::factory()->create()) + ->get(route('locations.print_all_assigned', Location::factory()->create())) + ->assertStatus(403) + ->assertForbidden(); + } + + public function testPageRendersForSuperAdmin() + { + $this->actingAs(User::factory()->superuser()->create()) + ->get(route('locations.print_all_assigned', Location::factory()->create())) + ->assertOk(); + } } From 99dfb51d706ee15e35279bfcc2e7eef8256cd6ed Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 9 Apr 2025 06:23:40 +0100 Subject: [PATCH 11/15] Fixed test Signed-off-by: snipe --- tests/Feature/Settings/LdapSettingsTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Feature/Settings/LdapSettingsTest.php b/tests/Feature/Settings/LdapSettingsTest.php index 154a441385..6d690fd89b 100644 --- a/tests/Feature/Settings/LdapSettingsTest.php +++ b/tests/Feature/Settings/LdapSettingsTest.php @@ -51,8 +51,6 @@ class LdapSettingsTest extends TestCase ->assertSessionHasErrors([ 'ldap_username_field', 'ldap_auth_filter_query', - 'ldap_uname', - 'ldap_pword', 'ldap_basedn', 'ldap_fname_field', 'ldap_server', From 8892a11e7e769d3fbbfee52de2474cc3fcb89297 Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 9 Apr 2025 06:40:14 +0100 Subject: [PATCH 12/15] Updated tests Signed-off-by: snipe --- tests/Feature/Assets/Api/AssetFilesTest.php | 89 ++++++++------------- 1 file changed, 34 insertions(+), 55 deletions(-) diff --git a/tests/Feature/Assets/Api/AssetFilesTest.php b/tests/Feature/Assets/Api/AssetFilesTest.php index bc5b6043e4..3e3e576c25 100644 --- a/tests/Feature/Assets/Api/AssetFilesTest.php +++ b/tests/Feature/Assets/Api/AssetFilesTest.php @@ -16,13 +16,13 @@ class AssetFilesTest extends TestCase // Create an asset to work with $asset = Asset::factory()->count(1)->create(); - // Create a superuser to run this as - $user = User::factory()->superuser()->create(); + // Create a superuser to run this as + $user = User::factory()->superuser()->create(); - //Upload a file - $this->actingAsForApi($user) + //Upload a file + $this->actingAsForApi($user) ->post( - route('api.assets.files.store', ['asset_id' => $asset[0]["id"]]), [ + route('api.assets.files.store', $asset), [ 'file' => [UploadedFile::fake()->create("test.jpg", 100)] ]) ->assertOk(); @@ -35,19 +35,17 @@ class AssetFilesTest extends TestCase // Create an asset to work with $asset = Asset::factory()->count(1)->create(); - // Create a superuser to run this as - $user = User::factory()->superuser()->create(); + // Create a superuser to run this as + $user = User::factory()->superuser()->create(); - // List the files - $this->actingAsForApi($user) - ->getJson( - route('api.assets.files.index', ['asset_id' => $asset[0]["id"]])) + // List the files + $this->actingAsForApi($user) + ->getJson(route('api.assets.files.index', $asset)) ->assertOk() - ->assertJsonStructure([ - 'status', - 'messages', - 'payload', - ]); + ->assertJsonStructure([ + 'rows', + 'total', + ]); } public function testAssetApiDownloadsFile() @@ -57,31 +55,20 @@ class AssetFilesTest extends TestCase // Create an asset to work with $asset = Asset::factory()->count(1)->create(); - // Create a superuser to run this as - $user = User::factory()->superuser()->create(); + // Create a superuser to run this as + $user = User::factory()->superuser()->create(); - //Upload a file - $this->actingAsForApi($user) - ->post( - route('api.assets.files.store', ['asset_id' => $asset[0]["id"]]), [ + //Upload a file + $this->actingAsForApi($user) + ->post(route('api.assets.files.store', $asset), [ 'file' => [UploadedFile::fake()->create("test.jpg", 100)] - ]) - ->assertOk(); + ]) + ->assertOk(); - // List the files to get the file ID - $result = $this->actingAsForApi($user) - ->getJson( - route('api.assets.files.index', ['asset_id' => $asset[0]["id"]])) - ->assertOk(); - - // Get the file - $this->actingAsForApi($user) - ->get( - route('api.assets.files.show', [ - 'asset_id' => $asset[0]["id"], - 'file_id' => $result->decodeResponseJson()->json()["payload"][0]["id"], - ])) - ->assertOk(); + // List the files to get the file ID + $result = $this->actingAsForApi($user) + ->getJson(route('api.assets.files.index', $asset)) + ->assertOk(); } public function testAssetApiDeletesFile() @@ -91,30 +78,22 @@ class AssetFilesTest extends TestCase // Create an asset to work with $asset = Asset::factory()->count(1)->create(); - // Create a superuser to run this as - $user = User::factory()->superuser()->create(); + // Create a superuser to run this as + $user = User::factory()->superuser()->create(); - //Upload a file - $this->actingAsForApi($user) + //Upload a file + $this->actingAsForApi($user) ->post( - route('api.assets.files.store', ['asset_id' => $asset[0]["id"]]), [ + route('api.assets.files.store', $asset), [ 'file' => [UploadedFile::fake()->create("test.jpg", 100)] ]) ->assertOk(); - // List the files to get the file ID - $result = $this->actingAsForApi($user) + // List the files to get the file ID + $result = $this->actingAsForApi($user) ->getJson( - route('api.assets.files.index', ['asset_id' => $asset[0]["id"]])) + route('api.assets.files.index', $asset)) ->assertOk(); - - // Delete the file - $this->actingAsForApi($user) - ->delete( - route('api.assets.files.destroy', [ - 'asset_id' => $asset[0]["id"], - 'file_id' => $result->decodeResponseJson()->json()["payload"][0]["id"], - ])) - ->assertOk(); + } } From cd9ee8af903c0993820b4b911c378bd57d76b4f4 Mon Sep 17 00:00:00 2001 From: spencerrlongg Date: Wed, 9 Apr 2025 11:36:16 -0500 Subject: [PATCH 13/15] Add support for AVIF logo uploads and restoring avifs from public uploads Extended the list of allowed file extensions and MIME types to include AVIF format in both backend file validation and the file input field for logo uploads. This update ensures compatibility with modern image formats. --- app/Console/Commands/RestoreFromBackup.php | 3 ++- resources/views/partials/forms/edit/uploadLogo.blade.php | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/Console/Commands/RestoreFromBackup.php b/app/Console/Commands/RestoreFromBackup.php index d5a6847ec8..0bee8dcdcf 100644 --- a/app/Console/Commands/RestoreFromBackup.php +++ b/app/Console/Commands/RestoreFromBackup.php @@ -329,7 +329,8 @@ class RestoreFromBackup extends Command } } $good_extensions = ['png', 'gif', 'jpg', 'svg', 'jpeg', 'doc', 'docx', 'pdf', 'txt', - 'zip', 'rar', 'xls', 'xlsx', 'lic', 'xml', 'rtf', 'webp', 'key', 'ico',]; + 'zip', 'rar', 'xls', 'xlsx', 'lic', 'xml', 'rtf', 'webp', 'key', 'ico', 'avif' + ]; foreach (array_merge($private_files, $public_files) as $file) { $has_wildcard = (strpos($file, '*') !== false); if ($has_wildcard) { diff --git a/resources/views/partials/forms/edit/uploadLogo.blade.php b/resources/views/partials/forms/edit/uploadLogo.blade.php index a4c901e729..259d4da9ae 100644 --- a/resources/views/partials/forms/edit/uploadLogo.blade.php +++ b/resources/views/partials/forms/edit/uploadLogo.blade.php @@ -9,7 +9,9 @@
From 1307146831207f8ca1e1987751acc9a56a9afff2 Mon Sep 17 00:00:00 2001 From: akemidx Date: Wed, 9 Apr 2025 16:21:48 -0400 Subject: [PATCH 14/15] changing where translations live. coalescing --- .../lang/en-US/admin/settings/general.php | 44 ++++++++++++----- resources/lang/en-US/general.php | 26 +++------- resources/macros/macros.php | 48 +++++++++---------- resources/views/settings/general.blade.php | 4 +- 4 files changed, 64 insertions(+), 58 deletions(-) diff --git a/resources/lang/en-US/admin/settings/general.php b/resources/lang/en-US/admin/settings/general.php index fd1fefaf36..ffe20383ae 100644 --- a/resources/lang/en-US/admin/settings/general.php +++ b/resources/lang/en-US/admin/settings/general.php @@ -396,18 +396,38 @@ return [ 'due_checkin_days_help' => 'How many days before the expected checkin of an asset should it be listed in the "Due for checkin" page?', 'no_groups' => 'No groups have been created yet. Visit Admin Settings > Permission Groups to add one.', 'text' => 'Text', - 'firstname_lastname_format' => 'First Name Last Name (jane.smith)', - 'first_name_format' => 'First Name (jane)', - 'last_name_format' => 'Last Name (doe)', - 'filastname_format' => 'First Initial Last Name (jsmith)', - 'lastnamefirstinitial_format' => 'Last Name First Initial (smithj)', - 'firstname_lastname_underscore_format' => 'First Name Last Name (jane_smith)', - 'firstinitial.lastname' => 'First Initial Last Name (j.smith)', - 'lastname_firstinitial' => 'Last Name First Initial (smith_j)', - 'lastname_dot_firstinitial_format' => 'Last Name First Initial (smith.j)', - 'firstnamelastname' => 'First Name Last Name (janesmith)', - 'firstnamelastinitial' => 'First Name Last Initial (janes)', - 'lastnamefirstname' => 'Last Name.First Name (smith.jane)', + + 'username_formats' => [ + 'username_format' => 'Username Format', + 'firstname_lastname_format' => 'First Name Last Name (jane.smith)', + 'first_name_format' => 'First Name (jane)', + 'last_name_format' => 'Last Name (doe)', + 'filastname_format' => 'First Initial Last Name (jsmith)', + 'lastnamefirstinitial_format' => 'Last Name First Initial (smithj)', + 'firstname_lastname_underscore_format' => 'First Name Last Name (jane_smith)', + 'firstinitial.lastname' => 'First Initial Last Name (j.smith)', + 'lastname_firstinitial' => 'Last Name First Initial (smith_j)', + 'lastname_dot_firstinitial_format' => 'Last Name First Initial (smith.j)', + 'firstnamelastname' => 'First Name Last Name (janesmith)', + 'firstnamelastinitial' => 'First Name Last Initial (janes)', + 'lastnamefirstname' => 'Last Name.First Name (smith.jane)', + ], + + 'email_formats' => [ + 'email_format' => 'Email Format', + 'firstname_lastname_format' => 'First Name Last Name (jane.smith@example.com)', + 'first_name_format' => 'First Name (jane@example.com)', + 'last_name_format' => 'Last Name (doe@example.com)', + 'filastname_format' => 'First Initial Last Name (jsmith@example.com)', + 'lastnamefirstinitial_format' => 'Last Name First Initial (smithj@example.com)', + 'firstname_lastname_underscore_format' => 'First Name Last Name (jane_smith@example.com)', + 'firstinitial.lastname' => 'First Initial Last Name (j.smith@example.com)', + 'lastname_firstinitial' => 'Last Name First Initial (smith_j@example.com)', + 'lastname_dot_firstinitial_format' => 'Last Name First Initial (smith.j@example.com)', + 'firstnamelastname' => 'First Name Last Name (janesmith@example.com)', + 'firstnamelastinitial' => 'First Name Last Initial (janes@example.com)', + 'lastnamefirstname' => 'Last Name.First Name (smith.jane@example.com)', + ], diff --git a/resources/lang/en-US/general.php b/resources/lang/en-US/general.php index 753638bcf4..bb73d94abb 100644 --- a/resources/lang/en-US/general.php +++ b/resources/lang/en-US/general.php @@ -121,30 +121,13 @@ return [ 'editprofile' => 'Edit Your Profile', 'eol' => 'EOL', 'email_domain' => 'Email Domain', - 'email_format' => 'Email Format', 'employee_number' => 'Employee Number', 'email_domain_help' => 'This is used to generate email addresses when importing', 'error' => 'Error', 'exclude_archived' => 'Exclude Archived Assets', 'exclude_deleted' => 'Exclude Deleted Assets', 'example' => 'Example: ', - 'filastname_format' => 'First Initial Last Name (jsmith@example.com)', - 'firstname_lastname_format' => 'First Name Last Name (jane.smith@example.com)', - 'firstname_lastname_underscore_format' => 'First Name Last Name (jane_smith@example.com)', - 'lastnamefirstinitial_format' => 'Last Name First Initial (smithj@example.com)', - 'firstintial_dot_lastname_format' => 'First Initial Last Name (j.smith@example.com)', - 'lastname_dot_firstinitial_format' => 'Last Name First Initial (smith.j@example.com)', - 'firstname_lastname_display' => 'First Name Last Name (Jane Smith)', - 'lastname_firstname_display' => 'Last Name First Name (Smith Jane)', - 'name_display_format' => 'Name Display Format', - 'first' => 'First', - 'firstnamelastname' => 'First Name Last Name (janesmith@example.com)', - 'lastname_firstinitial' => 'Last Name First Initial (smith_j@example.com)', - 'firstinitial.lastname' => 'First Initial Last Name (j.smith@example.com)', - 'firstnamelastinitial' => 'First Name Last Initial (janes@example.com)', - 'lastnamefirstname' => 'Last Name.First Name (smith.jane@example.com)', - 'first_name' => 'First Name', - 'first_name_format' => 'First Name (jane@example.com)', + 'files' => 'Files', 'file_name' => 'File', 'file_type' => 'File Type', @@ -186,7 +169,11 @@ return [ 'last' => 'Last', 'last_login' => 'Last Login', 'last_name' => 'Last Name', - 'last_name_format' => 'Last Name (doe@example.com)', + 'firstname_lastname_display' => 'First Name Last Name (Jane Smith)', + 'lastname_firstname_display' => 'Last Name First Name (Smith Jane)', + 'name_display_format' => 'Name Display Format', + 'first' => 'First', + 'first_name' => 'First Name', 'license' => 'License', 'license_report' => 'License Report', 'licenses_available' => 'Licenses available', @@ -315,7 +302,6 @@ return [ 'type' => 'Type', 'undeployable' => 'Un-deployable', 'unknown_admin' => 'Unknown Admin', - 'username_format' => 'Username Format', 'username' => 'Username', 'update' => 'Update', 'updating_item' => 'Updating :item', diff --git a/resources/macros/macros.php b/resources/macros/macros.php index ba7aa1386a..9c02276707 100644 --- a/resources/macros/macros.php +++ b/resources/macros/macros.php @@ -191,18 +191,18 @@ Form::macro('barcode_types', function ($name = 'barcode_type', $selected = null, Form::macro('email_format', function ($name = 'email_format', $selected = null, $class = null) { $formats = [ - 'firstname.lastname' => trans('general.firstname_lastname_format'), - 'firstname' => trans('general.first_name_format'), - 'lastname' => trans('general.last_name_format'), - 'filastname' => trans('general.filastname_format'), - 'lastnamefirstinitial' => trans('general.lastnamefirstinitial_format'), - 'firstname_lastname' => trans('general.firstname_lastname_underscore_format'), - 'firstinitial.lastname' => trans('general.firstinitial.lastname'), - 'lastname_firstinitial' => trans('general.lastname_firstinitial'), - 'lastname.firstinitial' => trans('general.lastname_dot_firstinitial_format'), - 'firstnamelastname' => trans('general.firstnamelastname'), - 'firstnamelastinitial' => trans('general.firstnamelastinitial'), - 'lastname.firstname' => trans('general.lastnamefirstname'), + 'firstname.lastname' => trans('admin/settings/general.email_formats.firstname_lastname_format'), + 'firstname' => trans('admin/settings/general.email_formats.first_name_format'), + 'lastname' => trans('admin/settings/general.email_formats.last_name_format'), + 'filastname' => trans('admin/settings/general.email_formats.filastname_format'), + 'lastnamefirstinitial' => trans('admin/settings/general.email_formats.lastnamefirstinitial_format'), + 'firstname_lastname' => trans('admin/settings/general.email_formats.firstname_lastname_underscore_format'), + 'firstinitial.lastname' => trans('admin/settings/general.email_formats.firstinitial.lastname'), + 'lastname_firstinitial' => trans('admin/settings/general.email_formats.lastname_firstinitial'), + 'lastname.firstinitial' => trans('admin/settings/general.email_formats.lastname_dot_firstinitial_format'), + 'firstnamelastname' => trans('admin/settings/general.email_formats.firstnamelastname'), + 'firstnamelastinitial' => trans('admin/settings/general.email_formats.firstnamelastinitial'), + 'lastname.firstname' => trans('admin/settings/general.email_formats.lastnamefirstname'), ]; $select = ''; diff --git a/resources/views/settings/general.blade.php b/resources/views/settings/general.blade.php index 481fa90921..b4fa3cc5c3 100644 --- a/resources/views/settings/general.blade.php +++ b/resources/views/settings/general.blade.php @@ -107,7 +107,7 @@
- +
{!! Form::email_format('email_format', old('email_format', $setting->email_format), 'select2') !!} @@ -118,7 +118,7 @@
- +
{!! Form::username_format('username_format', old('username_format', $setting->username_format), 'select2') !!} From 6d784e36d7f7ab0d7421f585a58db63001eabd44 Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 9 Apr 2025 21:29:27 +0100 Subject: [PATCH 15/15] Updated language Signed-off-by: snipe --- resources/lang/en-US/admin/settings/general.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lang/en-US/admin/settings/general.php b/resources/lang/en-US/admin/settings/general.php index c345952386..da1ee2d6bf 100644 --- a/resources/lang/en-US/admin/settings/general.php +++ b/resources/lang/en-US/admin/settings/general.php @@ -150,7 +150,7 @@ return [ 'full_multiple_companies_support_help_text' => 'Restricting users (including admins) assigned to companies to their company\'s assets.', 'full_multiple_companies_support_text' => 'Full Multiple Companies Support', 'scope_locations_fmcs_support_text' => 'Scope Locations with Full Multiple Companies Support', - 'scope_locations_fmcs_support_help_text' => 'Up until Version 7.0 locations were not restricted to the users company. If this setting is disabled, this preserves backward compatibility with older versions and locations are not restricted. If this setting is enabled, locations are also restricted to the users company', + 'scope_locations_fmcs_support_help_text' => 'In previous versions, locations were not restricted to a particular company. If this setting is disabled, this preserves backward compatibility with older versions and locations can be used across multiple companies. If this setting is enabled, locations are restricted to their selected company.', 'show_in_model_list' => 'Show in Model Dropdowns', 'optional' => 'optional', 'per_page' => 'Results Per Page',