From 9858b0f37f2f27c43c47e6f437b36e8a72985690 Mon Sep 17 00:00:00 2001 From: Godfrey M Date: Tue, 20 Aug 2024 14:23:55 -0700 Subject: [PATCH 01/15] fixes info tab for users and assets --- resources/assets/less/overrides.less | 18 ++++++++++++++++++ resources/views/hardware/view.blade.php | 7 ++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/resources/assets/less/overrides.less b/resources/assets/less/overrides.less index 3358e55632..7b4dfc9bc6 100644 --- a/resources/assets/less/overrides.less +++ b/resources/assets/less/overrides.less @@ -851,6 +851,24 @@ th.css-component > .th-inner::before margin-top:50px } } +@media screen and (max-width: 992px){ + .info-stack-container { + display: flex; + flex-direction: column; + } + .col-md-3.col-xs-12.col-sm-push-9.info-stack{ + left:auto; + order:2; + } + .col-md-9.col-xs-12.col-sm-pull-3.info-stack{ + right:auto; + order:1; + } + .info-stack-container > .col-md-9.col-xs-12.col-sm-pull-3.info-stack > .row-new-striped > .row > .col-sm-2{ + width:auto; + float:none; + } +} @media screen and (max-width: 1318px) and (min-width: 1200px){ .admin.box{ height:170px; diff --git a/resources/views/hardware/view.blade.php b/resources/views/hardware/view.blade.php index 2da4038340..61b90c28dc 100755 --- a/resources/views/hardware/view.blade.php +++ b/resources/views/hardware/view.blade.php @@ -160,9 +160,9 @@ @endif - +
-
+
@if (($asset->image) || (($asset->model) && ($asset->model->image!=''))) @@ -334,7 +334,7 @@ -
+
@@ -1078,6 +1078,7 @@
+
From 9d484077ae030020e3b1cd585783d8db03c5c0f5 Mon Sep 17 00:00:00 2001 From: Godfrey M Date: Tue, 20 Aug 2024 14:24:37 -0700 Subject: [PATCH 02/15] missed a file --- resources/views/users/view.blade.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/views/users/view.blade.php b/resources/views/users/view.blade.php index 8fbed865ee..dd36377c47 100755 --- a/resources/views/users/view.blade.php +++ b/resources/views/users/view.blade.php @@ -159,9 +159,9 @@
@endif - +
-
+
@@ -306,7 +306,7 @@ -
+
@@ -765,6 +765,7 @@ @endif
+
From 963911f2e19b04656b470d00da76cedfe5b3d068 Mon Sep 17 00:00:00 2001 From: Godfrey M Date: Wed, 21 Aug 2024 00:23:10 -0700 Subject: [PATCH 03/15] changed order of info stgack --- resources/assets/less/overrides.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/assets/less/overrides.less b/resources/assets/less/overrides.less index 7b4dfc9bc6..735791b4cd 100644 --- a/resources/assets/less/overrides.less +++ b/resources/assets/less/overrides.less @@ -858,11 +858,11 @@ th.css-component > .th-inner::before } .col-md-3.col-xs-12.col-sm-push-9.info-stack{ left:auto; - order:2; + order:1; } .col-md-9.col-xs-12.col-sm-pull-3.info-stack{ right:auto; - order:1; + order:2; } .info-stack-container > .col-md-9.col-xs-12.col-sm-pull-3.info-stack > .row-new-striped > .row > .col-sm-2{ width:auto; From 1ab29ec3a4c7ad902aa95cd35105105eb2ec567d Mon Sep 17 00:00:00 2001 From: r-xyz <100710244+r-xyz@users.noreply.github.com> Date: Wed, 21 Aug 2024 11:52:09 +0200 Subject: [PATCH 04/15] Rename docker startup scripts coherently. --- Dockerfile.alpine | 8 ++++---- Dockerfile.fpm-alpine | 6 +++--- docker/{entrypoint_alpine.sh => startup_alpine.sh} | 0 docker/{docker-entrypoint.sh => startup_alpine_fpm.sh} | 0 4 files changed, 7 insertions(+), 7 deletions(-) rename docker/{entrypoint_alpine.sh => startup_alpine.sh} (100%) rename docker/{docker-entrypoint.sh => startup_alpine_fpm.sh} (100%) diff --git a/Dockerfile.alpine b/Dockerfile.alpine index c08cbbd95c..2c83a1a119 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -79,12 +79,12 @@ USER root VOLUME ["/var/lib/snipeit"] -# Entrypoints -COPY docker/entrypoint_alpine.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh +# Startup script +COPY docker/startup_alpine.sh /startup.sh +RUN chmod +x /startup.sh ENTRYPOINT ["/sbin/tini", "--"] -CMD ["/entrypoint.sh"] +CMD ["/startup.sh"] EXPOSE 80 diff --git a/Dockerfile.fpm-alpine b/Dockerfile.fpm-alpine index 77302524d4..b7fb272985 100644 --- a/Dockerfile.fpm-alpine +++ b/Dockerfile.fpm-alpine @@ -97,7 +97,7 @@ RUN set -eux; \ VOLUME [ "/var/lib/snipeit" ] COPY --chown=www-data:www-data docker/docker-secrets.env /var/www/html/.env -COPY --chmod=655 docker/docker-entrypoint.sh /usr/local/bin/docker-snipeit-entrypoint +COPY --chmod=655 docker/startup_alpine_fpm.sh /startup.sh COPY docker/column-statistics.cnf /etc/mysql/conf.d/column-statistics.cnf -ENTRYPOINT [ "/usr/local/bin/docker-snipeit-entrypoint" ] -CMD [ "/usr/local/bin/docker-php-entrypoint", "php-fpm" ] +ENTRYPOINT [ "/startup.sh" ] +CMD [ "/startup.sh", "php-fpm" ] diff --git a/docker/entrypoint_alpine.sh b/docker/startup_alpine.sh similarity index 100% rename from docker/entrypoint_alpine.sh rename to docker/startup_alpine.sh diff --git a/docker/docker-entrypoint.sh b/docker/startup_alpine_fpm.sh similarity index 100% rename from docker/docker-entrypoint.sh rename to docker/startup_alpine_fpm.sh From 912bbf0e32ad362bb1055eb6d8c62e7ecdce4510 Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 21 Aug 2024 13:02:21 +0100 Subject: [PATCH 05/15] More print fixes Signed-off-by: snipe --- resources/views/hardware/view.blade.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/views/hardware/view.blade.php b/resources/views/hardware/view.blade.php index 2da4038340..b463f6d78c 100755 --- a/resources/views/hardware/view.blade.php +++ b/resources/views/hardware/view.blade.php @@ -1399,7 +1399,7 @@ @if (($file->filename) && (Storage::exists('private_uploads/assets/'.$file->filename))) - + @@ -1415,7 +1415,7 @@ @can('update', \App\Models\Asset::class) - + @endcan @@ -1503,11 +1503,11 @@ @if (($file->filename) && (Storage::exists('private_uploads/assetmodels/'.$file->filename))) - + - + @@ -1520,8 +1520,8 @@ @can('update', \App\Models\AssetModel::class) - - + + @endcan From d29b3bfb9ae71dc66bf3f49ee6f3453aa2847da9 Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 21 Aug 2024 13:06:16 +0100 Subject: [PATCH 06/15] Few more hidden-print classes Signed-off-by: snipe --- resources/views/hardware/view.blade.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/resources/views/hardware/view.blade.php b/resources/views/hardware/view.blade.php index b463f6d78c..e49b3b239a 100755 --- a/resources/views/hardware/view.blade.php +++ b/resources/views/hardware/view.blade.php @@ -1120,7 +1120,7 @@ @else -
+
{{ trans('general.no_results') }}
@@ -1157,7 +1157,7 @@ {{ Helper::formatCurrencyOutput($component->purchase_cost) }} each {{ $component->serial }} - {{ trans('general.checkin') }} + {{ trans('general.checkin') }} purchase_cost *$component->pivot->assigned_qty) ?> @@ -1175,7 +1175,7 @@ @else -
+
{{ trans('general.no_results') }}
@@ -1239,7 +1239,7 @@ @else -
+
{{ trans('general.no_results') }}
@@ -1403,7 +1403,7 @@ - + @endif @@ -1427,7 +1427,7 @@ @else -
+
{{ trans('general.no_results') }}
@@ -1504,11 +1504,11 @@ @if (($file->filename) && (Storage::exists('private_uploads/assetmodels/'.$file->filename))) - + - + @endif From 4ec361c7183547cd90e67c8c86e87055e55605be Mon Sep 17 00:00:00 2001 From: r-xyz <100710244+r-xyz@users.noreply.github.com> Date: Wed, 21 Aug 2024 19:49:51 +0200 Subject: [PATCH 07/15] Add AssetModel files endpoints to API --- .../Api/AssetModelFilesController.php | 200 ++++++++++++++++++ resources/lang/en-GB/admin/models/message.php | 6 + routes/api.php | 33 +++ .../AssetModels/Api/AssetModelFilesTest.php | 120 +++++++++++ 4 files changed, 359 insertions(+) create mode 100644 app/Http/Controllers/Api/AssetModelFilesController.php create mode 100644 tests/Feature/AssetModels/Api/AssetModelFilesTest.php diff --git a/app/Http/Controllers/Api/AssetModelFilesController.php b/app/Http/Controllers/Api/AssetModelFilesController.php new file mode 100644 index 0000000000..9d17de4aec --- /dev/null +++ b/app/Http/Controllers/Api/AssetModelFilesController.php @@ -0,0 +1,200 @@ + + * + * @version v1.0 + * @author [T. Scarsbrook] [] + */ +class AssetModelFilesController extends Controller +{ + /** + * Accepts a POST to upload a file to the server. + * + * @param \App\Http\Requests\UploadFileRequest $request + * @param int $assetModelId + * @since [v7.0.12] + * @author [r-xyz] + */ + public function store(UploadFileRequest $request, $assetModelId = null) : JsonResponse + { + // Start by checking if the asset being acted upon exists + if (! $assetModel = AssetModel::find($assetModelId)) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404); + } + + // Make sure we are allowed to update this asset + $this->authorize('update', $asset); + + if ($request->hasFile('file')) { + // If the file storage directory doesn't exist; create it + if (! Storage::exists('private_uploads/assetmodels')) { + Storage::makeDirectory('private_uploads/assetmodels', 775); + } + + // Loop over the attached files and add them to the asset + foreach ($request->file('file') as $file) { + $file_name = $request->handleFile('private_uploads/assetmodels/','model-'.$asset->id, $file); + + $asset->logUpload($file_name, e($request->get('notes'))); + } + + // All done - report success + return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/models/message.upload.success'))); + } + + // We only reach here if no files were included in the POST, so tell the user this + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.upload.nofiles')), 500); + } + + /** + * List the files for an asset. + * + * @param int $assetModelId + * @since [v7.0.12] + * @author [r-xyz] + */ + public function list($assetModelId = null) : JsonResponse + { + // Start by checking if the asset being acted upon exists + if (! $assetModel = AssetModel::find($assetModelId)) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404); + } + + // the asset is valid + if (isset($assetModel->id)) { + $this->authorize('view', $assetModel); + + // Check that there are some uploads on this asset that can be listed + if ($assetModel->uploads->count() > 0) { + $files = array(); + foreach ($assetModel->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/models/message.upload.success'))); + } + + // There are no files. + return response()->json(Helper::formatStandardApiResponse('success', array(), trans('admin/models/message.upload.success'))); + } + + // Send back an error message + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.error')), 500); + } + + /** + * Check for permissions and display the file. + * + * @param int $assetModelId + * @param int $fileId + * @return \Illuminate\Http\JsonResponse + * @throws \Illuminate\Auth\Access\AuthorizationException + * @since [v7.0.12] + * @author [r-xyz] + */ + public function show($assetModelId = null, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse + { + // Start by checking if the asset being acted upon exists + if (! $assetModel = AssetModel::find($assetModelId)) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404); + } + + // the asset is valid + if (isset($assetModel->id)) { + $this->authorize('view', $assetModel); + + // Check that the file being requested exists for the asset + if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $assetModel->id)->find($fileId)) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.no_match', ['id' => $fileId])), 404); + } + + // Form the full filename with path + $file = 'private_uploads/assetmodels/'.$log->filename; + Log::debug('Checking for '.$file); + + if ($log->action_type == 'audit') { + $file = 'private_uploads/audits/'.$log->filename; + } + + // Check the file actually exists on the filesystem + if (! Storage::exists($file)) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.does_not_exist', ['id' => $fileId])), 404); + } + + if (request('inline') == 'true') { + + $headers = [ + 'Content-Disposition' => 'inline', + ]; + + return Storage::download($file, $log->filename, $headers); + } + + return StorageHelper::downloader($file); + } + + // Send back an error message + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.error', ['id' => $fileId])), 500); + } + + /** + * Delete the associated file + * + * @param int $assetModelId + * @param int $fileId + * @since [v7.0.12] + * @author [r-xyz] + */ + public function destroy($assetModelId = null, $fileId = null) : JsonResponse + { + // Start by checking if the asset being acted upon exists + if (! $assetModel = AssetModel::find($assetModelId)) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404); + } + + $rel_path = 'private_uploads/assetmodels'; + + // the asset is valid + if (isset($assetModel->id)) { + $this->authorize('update', $assetModel); + + // 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 + $log->delete(); + + // All deleting done - notify the user of success + return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/models/message.deletefile.success')), 200); + } + + // The file doesn't seem to really exist, so report an error + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.deletefile.error')), 500); + } + + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.deletefile.error')), 500); + } +} diff --git a/resources/lang/en-GB/admin/models/message.php b/resources/lang/en-GB/admin/models/message.php index ae3bc34eed..18a3fb10ce 100644 --- a/resources/lang/en-GB/admin/models/message.php +++ b/resources/lang/en-GB/admin/models/message.php @@ -43,5 +43,11 @@ return array( 'success' => 'Model deleted!|:success_count models deleted!', 'success_partial' => ':success_count model(s) were deleted, however :fail_count were unable to be deleted because they still have assets associated with them.' ), + 'download' => [ + 'error' => 'File(s) not downloaded. Please try again.', + 'success' => 'File(s) successfully downloaded.', + 'does_not_exist' => 'No file exists', + 'no_match' => 'No matching record for that asset/file', + ], ); diff --git a/routes/api.php b/routes/api.php index 0eb0d834cf..b57ed08976 100644 --- a/routes/api.php +++ b/routes/api.php @@ -791,6 +791,12 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi ] )->name('api.models.assets'); + Route::post('{id}/restore', + [ + Api\AssetModelsController::class, + 'restore' + ] + )->name('api.models.restore'); Route::post('{id}/restore', [ Api\AssetModelsController::class, @@ -798,6 +804,33 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi ] )->name('api.models.restore'); + Route::post('{model_id}/files', + [ + Api\AssetModelFilesController::class, + 'store' + ] + )->name('api.models.files'); + + Route::get('{model_id}/files', + [ + Api\AssetModelFilesController::class, + 'list' + ] + )->name('api.models.files'); + + Route::get('{model_id}/file/{file_id}', + [ + Api\AssetModelFilesController::class, + 'show' + ] + )->name('api.models.assets.file'); + + Route::delete('{model_id}/file/{file_id}', + [ + Api\AssetModelFilesController::class, + 'destroy' + ] + )->name('api.models.file'); }); Route::resource('models', diff --git a/tests/Feature/AssetModels/Api/AssetModelFilesTest.php b/tests/Feature/AssetModels/Api/AssetModelFilesTest.php new file mode 100644 index 0000000000..c22609c0c7 --- /dev/null +++ b/tests/Feature/AssetModels/Api/AssetModelFilesTest.php @@ -0,0 +1,120 @@ +count(1)->create(); + + // Create a superuser to run this as + $user = User::factory()->superuser()->create(); + + //Upload a file + $this->actingAsForApi($user) + ->post( + route('api.models.files.store', ['model_id' => $model[0]["id"]]), [ + 'file' => [UploadedFile::fake()->create("test.jpg", 100)] + ]) + ->assertOk(); + } + + public function testAssetModelApiListsFiles() + { + // List all files on a model + + // Create an model to work with + $model = AssetModel::factory()->count(1)->create(); + + // Create a superuser to run this as + $user = User::factory()->superuser()->create(); + + // List the files + $this->actingAsForApi($user) + ->getJson( + route('api.models.files.index', ['model_id' => $model[0]["id"]])) + ->assertOk() + ->assertJsonStructure([ + 'status', + 'messages', + 'payload', + ]); + } + + public function testAssetModelApiDownloadsFile() + { + // Download a file from a model + + // Create a model to work with + $model = AssetModel::factory()->count(1)->create(); + + // Create a superuser to run this as + $user = User::factory()->superuser()->create(); + + //Upload a file + $this->actingAsForApi($user) + ->post( + route('api.models.files.store', ['model_id' => $model[0]["id"]]), [ + 'file' => [UploadedFile::fake()->create("test.jpg", 100)] + ]) + ->assertOk(); + + // List the files to get the file ID + $result = $this->actingAsForApi($user) + ->getJson( + route('api.models.files.index', ['model_id' => $model[0]["id"]])) + ->assertOk(); + + // Get the file + $this->actingAsForApi($user) + ->get( + route('api.models.files.show', [ + 'model_id' => $model[0]["id"], + 'file_id' => $result->decodeResponseJson()->json()["payload"][0]["id"], + ])) + ->assertOk(); + } + + public function testAssetModelApiDeletesFile() + { + // Delete a file from a model + + // Create a model to work with + $model = AssetModel::factory()->count(1)->create(); + + // Create a superuser to run this as + $user = User::factory()->superuser()->create(); + + //Upload a file + $this->actingAsForApi($user) + ->post( + route('api.models.files.store', ['model_id' => $model[0]["id"]]), [ + 'file' => [UploadedFile::fake()->create("test.jpg", 100)] + ]) + ->assertOk(); + + // List the files to get the file ID + $result = $this->actingAsForApi($user) + ->getJson( + route('api.models.files.index', ['model_id' => $model[0]["id"]])) + ->assertOk(); + + // Delete the file + $this->actingAsForApi($user) + ->delete( + route('api.models.files.destroy', [ + 'model_id' => $model[0]["id"], + 'file_id' => $result->decodeResponseJson()->json()["payload"][0]["id"], + ])) + ->assertOk(); + } +} From da7313bc9d2ee8614967abaf601782fe85e35113 Mon Sep 17 00:00:00 2001 From: r-xyz <100710244+r-xyz@users.noreply.github.com> Date: Wed, 21 Aug 2024 20:24:22 +0200 Subject: [PATCH 08/15] Fix models files API routes. --- routes/api.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/routes/api.php b/routes/api.php index b57ed08976..b4d5b5d52f 100644 --- a/routes/api.php +++ b/routes/api.php @@ -809,28 +809,28 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi Api\AssetModelFilesController::class, 'store' ] - )->name('api.models.files'); + )->name('api.models.files.store'); Route::get('{model_id}/files', [ Api\AssetModelFilesController::class, 'list' ] - )->name('api.models.files'); + )->name('api.models.files.index'); Route::get('{model_id}/file/{file_id}', [ Api\AssetModelFilesController::class, 'show' ] - )->name('api.models.assets.file'); + )->name('api.models.files.show'); Route::delete('{model_id}/file/{file_id}', [ Api\AssetModelFilesController::class, 'destroy' ] - )->name('api.models.file'); + )->name('api.models.files.destroy'); }); Route::resource('models', From cd7db5c4a8448239b632507ea53a25545b41d972 Mon Sep 17 00:00:00 2001 From: r-xyz <100710244+r-xyz@users.noreply.github.com> Date: Wed, 21 Aug 2024 22:24:08 +0200 Subject: [PATCH 09/15] Fix some typos in models file handler. --- app/Http/Controllers/Api/AssetModelFilesController.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Api/AssetModelFilesController.php b/app/Http/Controllers/Api/AssetModelFilesController.php index 9d17de4aec..90d283f72e 100644 --- a/app/Http/Controllers/Api/AssetModelFilesController.php +++ b/app/Http/Controllers/Api/AssetModelFilesController.php @@ -42,7 +42,7 @@ class AssetModelFilesController extends Controller } // Make sure we are allowed to update this asset - $this->authorize('update', $asset); + $this->authorize('update', $assetModel); if ($request->hasFile('file')) { // If the file storage directory doesn't exist; create it @@ -52,13 +52,13 @@ class AssetModelFilesController extends Controller // Loop over the attached files and add them to the asset foreach ($request->file('file') as $file) { - $file_name = $request->handleFile('private_uploads/assetmodels/','model-'.$asset->id, $file); + $file_name = $request->handleFile('private_uploads/assetmodels/','model-'.$assetModel->id, $file); - $asset->logUpload($file_name, e($request->get('notes'))); + $assetModel->logUpload($file_name, e($request->get('notes'))); } // All done - report success - return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/models/message.upload.success'))); + return response()->json(Helper::formatStandardApiResponse('success', $assetModel, trans('admin/models/message.upload.success'))); } // We only reach here if no files were included in the POST, so tell the user this From a8eb76fd8dae5f2ad6b716307b32ba477d31ed32 Mon Sep 17 00:00:00 2001 From: r-xyz <100710244+r-xyz@users.noreply.github.com> Date: Wed, 21 Aug 2024 22:25:41 +0200 Subject: [PATCH 10/15] Fixed model files API routes. --- routes/api.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/routes/api.php b/routes/api.php index b4d5b5d52f..35e6c92060 100644 --- a/routes/api.php +++ b/routes/api.php @@ -791,12 +791,6 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi ] )->name('api.models.assets'); - Route::post('{id}/restore', - [ - Api\AssetModelsController::class, - 'restore' - ] - )->name('api.models.restore'); Route::post('{id}/restore', [ Api\AssetModelsController::class, From 47d8e2f8b91f82768a9399c5d4de73d63974eb82 Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 22 Aug 2024 09:42:05 +0100 Subject: [PATCH 11/15] Add @Scarzy as a contributor --- .all-contributorsrc | 9 +++++++++ CONTRIBUTORS.md | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 68aec85323..679f2740ac 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -3190,6 +3190,15 @@ "contributions": [ "code" ] + }, + { + "login": "Scarzy", + "name": "Scarzy", + "avatar_url": "https://avatars.githubusercontent.com/u/1197791?v=4", + "profile": "https://github.com/Scarzy", + "contributions": [ + "code" + ] } ] } diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 6359814b49..1bea567693 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -52,7 +52,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken | [
bilias](https://github.com/bilias)
[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") | [
coach1988](https://github.com/coach1988)
[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") | [
MrM](https://github.com/mauro-miatello)
[💻](https://github.com/snipe/snipe-it/commits?author=mauro-miatello "Code") | [
koiakoia](https://github.com/koiakoia)
[💻](https://github.com/snipe/snipe-it/commits?author=koiakoia "Code") | [
Mustafa Online](https://github.com/mustafa-online)
[💻](https://github.com/snipe/snipe-it/commits?author=mustafa-online "Code") | [
franceslui](https://github.com/franceslui)
[💻](https://github.com/snipe/snipe-it/commits?author=franceslui "Code") | [
Q4kK](https://github.com/Q4kK)
[💻](https://github.com/snipe/snipe-it/commits?author=Q4kK "Code") | | [
squintfox](https://github.com/squintfox)
[💻](https://github.com/snipe/snipe-it/commits?author=squintfox "Code") | [
Jeff Clay](https://github.com/jeffclay)
[💻](https://github.com/snipe/snipe-it/commits?author=jeffclay "Code") | [
Phil J R](https://github.com/PP-JN-RL)
[💻](https://github.com/snipe/snipe-it/commits?author=PP-JN-RL "Code") | [
i_virus](https://www.corelight.com/)
[💻](https://github.com/snipe/snipe-it/commits?author=chandanchowdhury "Code") | [
Paul Grime](https://github.com/gitgrimbo)
[💻](https://github.com/snipe/snipe-it/commits?author=gitgrimbo "Code") | [
Lee Porte](https://leeporte.co.uk)
[💻](https://github.com/snipe/snipe-it/commits?author=LeePorte "Code") | [
BRYAN ](https://github.com/bryanlopezinc)
[💻](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Tests") | | [
U-H-T](https://github.com/U-H-T)
[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") | [
Matt Tyree](https://github.com/Tyree)
[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [
Florent Bervas](http://spoontux.net)
[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [
Daniel Albertsen](https://ditscheri.com)
[💻](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [
r-xyz](https://github.com/r-xyz)
[💻](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [
Steven Mainor](https://github.com/DrekiDegga)
[💻](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") | [
arne-kroeger](https://github.com/arne-kroeger)
[💻](https://github.com/snipe/snipe-it/commits?author=arne-kroeger "Code") | -| [
Glukose1](https://github.com/Glukose1)
[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") | +| [
Glukose1](https://github.com/Glukose1)
[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") | [
Scarzy](https://github.com/Scarzy)
[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! From 3ac07020945751505b06d4acc358575e92860e0e Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 22 Aug 2024 10:13:22 +0100 Subject: [PATCH 12/15] Fixed #15344 - make select2 for countries freeform-ish Signed-off-by: snipe --- resources/lang/en-US/localizations.php | 5 ++++- resources/macros/macros.php | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/resources/lang/en-US/localizations.php b/resources/lang/en-US/localizations.php index f335ddc1b3..e87af0718e 100644 --- a/resources/lang/en-US/localizations.php +++ b/resources/lang/en-US/localizations.php @@ -135,6 +135,7 @@ return [ 'EC'=>'Ecuador', 'EE'=>'Estonia', 'EG'=>'Egypt', + 'GB-ENG'=>'England', 'ER'=>'Eritrea', 'ES'=>'Spain', 'ET'=>'Ethiopia', @@ -233,6 +234,7 @@ return [ 'NG'=>'Nigeria', 'NI'=>'Nicaragua', 'NL'=>'Netherlands', + 'GB-NIR' => 'Northern Ireland', 'NO'=>'Norway', 'NP'=>'Nepal', 'NR'=>'Nauru', @@ -260,7 +262,7 @@ return [ 'RU'=>'Russian Federation', 'RW'=>'Rwanda', 'SA'=>'Saudi Arabia', - 'UK'=>'Scotland', + 'GB-SCT'=>'Scotland', 'SB'=>'Solomon Islands', 'SC'=>'Seychelles', 'SS'=>'South Sudan', @@ -312,6 +314,7 @@ return [ 'VI'=>'Virgin Islands (U.S.)', 'VN'=>'Viet Nam', 'VU'=>'Vanuatu', + 'GB-WLS' =>'Wales', 'WF'=>'Wallis And Futuna Islands', 'WS'=>'Samoa', 'YE'=>'Yemen', diff --git a/resources/macros/macros.php b/resources/macros/macros.php index 7fefba2aa6..1cc754b7cf 100644 --- a/resources/macros/macros.php +++ b/resources/macros/macros.php @@ -32,18 +32,27 @@ Form::macro('countries', function ($name = 'country', $selected = null, $class = $idclause = (!is_null($id)) ? $id : ''; - $select = ''; $select .= ''; - // Pull the autoglossonym array from the localizations translation file - foreach (trans('localizations.countries') as $abbr => $country) { + foreach ($countries_array as $abbr => $country) { // We have to handle it this way to handle deprecication warnings since you can't strtoupper on null if ($abbr!='') { $abbr = strtoupper($abbr); } - $select .= ' '; + // Loop through the countries configured in the localization file + $select .= ' '; + + } + + // If the country value doesn't exist in the array, add it as a new option and select it so we don't drop that data + if (!in_array($selected, $countries_array)) { + $select .= ' '; } $select .= ''; From 305dc049a498c913caacdd6f2c21f4320b9c9e52 Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 22 Aug 2024 11:01:28 +0100 Subject: [PATCH 13/15] Added asterisk help text Signed-off-by: snipe --- resources/lang/en-US/general.php | 1 + resources/views/partials/forms/edit/address.blade.php | 1 + resources/views/users/edit.blade.php | 2 ++ resources/views/users/view.blade.php | 4 +++- 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/resources/lang/en-US/general.php b/resources/lang/en-US/general.php index a60f0dedfd..4ebf1b8977 100644 --- a/resources/lang/en-US/general.php +++ b/resources/lang/en-US/general.php @@ -561,5 +561,6 @@ return [ 'remaining_var' => ':count Remaining', 'label' => 'Label', 'import_asset_tag_exists' => 'An asset with the asset tag :asset_tag already exists and an update was not requested. No change was made.', + 'countries_manually_entered_help' => 'Values with an asterisk (*) were manually entered and do not match existing ISO 3166 dropdown values', ]; diff --git a/resources/views/partials/forms/edit/address.blade.php b/resources/views/partials/forms/edit/address.blade.php index c68ecaa8c2..893bd01339 100644 --- a/resources/views/partials/forms/edit/address.blade.php +++ b/resources/views/partials/forms/edit/address.blade.php @@ -35,6 +35,7 @@ {{ Form::label('country', trans('general.country'), array('class' => 'col-md-3 control-label')) }}
{!! Form::countries('country', old('country', $item->country), 'select2') !!} +

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

{!! $errors->first('country', '') !!}
diff --git a/resources/views/users/edit.blade.php b/resources/views/users/edit.blade.php index 5f0246d022..1c23b76823 100755 --- a/resources/views/users/edit.blade.php +++ b/resources/views/users/edit.blade.php @@ -451,6 +451,8 @@
{!! Form::countries('country', old('country', $user->country), 'col-md-12 select2') !!} + +

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

{!! $errors->first('country', '') !!}
diff --git a/resources/views/users/view.blade.php b/resources/views/users/view.blade.php index 8fbed865ee..e2397ebe86 100755 --- a/resources/views/users/view.blade.php +++ b/resources/views/users/view.blade.php @@ -197,7 +197,9 @@ {{ trans('admin/users/general.print_assigned') }} @else - + @endif
@endcan From 5efddf6f5b287f83295e5bde066e7a5a707215e6 Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 22 Aug 2024 11:01:39 +0100 Subject: [PATCH 14/15] Allow clear on country dropdown Signed-off-by: snipe --- resources/macros/macros.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/macros/macros.php b/resources/macros/macros.php index 1cc754b7cf..5d0813115e 100644 --- a/resources/macros/macros.php +++ b/resources/macros/macros.php @@ -35,7 +35,7 @@ Form::macro('countries', function ($name = 'country', $selected = null, $class = // Pull the autoglossonym array from the localizations translation file $countries_array = trans('localizations.countries'); - $select = ''; $select .= ''; foreach ($countries_array as $abbr => $country) { @@ -46,13 +46,13 @@ Form::macro('countries', function ($name = 'country', $selected = null, $class = } // Loop through the countries configured in the localization file - $select .= ' '; + $select .= ' '; } // If the country value doesn't exist in the array, add it as a new option and select it so we don't drop that data if (!in_array($selected, $countries_array)) { - $select .= ' '; + $select .= ' '; } $select .= ''; From 94300d81d618d4b50c127e6094f78453412746b9 Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 22 Aug 2024 11:19:32 +0100 Subject: [PATCH 15/15] Updated dev assets Signed-off-by: snipe --- public/css/build/app.css | Bin 27154 -> 27588 bytes public/css/build/overrides.css | Bin 20304 -> 20738 bytes public/css/dist/all.css | Bin 513242 -> 514110 bytes public/mix-manifest.json | 6 +++--- 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/css/build/app.css b/public/css/build/app.css index 7f575c30ea689c4fa6dea4d31e97c93ff2c3a2c7..01822563280cfce28dab20039e566ff797c3e866 100644 GIT binary patch delta 364 zcmbPqh4ILB#tomsEG#XJ3Mw=es<{*t^fL3(@^y<#5|gualk@XR5;OBsi@-7p3MrYz z1v!b8Rtjl3sTI~h#gq4VNYyJS)PnRS=jZ6=rsx`j85PC4hDKmkajtGbX>o?GC6d8V z<8o5dN~{zTOH1-WW+^D-7p0^YSt%HTc*sUtVly%)2WS<@t@RMcLd`77OwYh(s1e*y ztp2luyC1)0P`iN2i}K5L^HR%oi}_26G7C~u6d+;>c9RusC0Ib7nmoZ=e6m}p!sbt5 G3c>)BXmf-B delta 14 WcmX?dopI6?#tomsHZO@#5C#A_;0E3R diff --git a/public/css/build/overrides.css b/public/css/build/overrides.css index 8062628551e1f943da7fd80293001f4421c814a3..bb8a8b5901b7a6f96d408d5f078bb30cdd65e0fc 100644 GIT binary patch delta 364 zcmcaGkFjYH;|2p83rkC*f(lKAYAyu@z0AC{eBI)b#N=$<oU2<F7M2#)7Pc+yv-Bowr1R8g=B4H97MCO@XX_^C=anR8=A{-X zRC6f+K}u$EK~7?&l|ouhYK1je2*lD&$t+4uF3HT#vrlzw?S;e`!1*OFqx|Vtf+n{z#zE;Ioub_}$l#*IxrC^9|k|j2ia&mw+V3<^t znVwN%rI1)!k`Hk!)J!9|nFuXNUbR!ugE^Zh%j^`U+sm?xGbz|j7A%vPE+5XwHGOg* bi{SJ}dhCkZXX~-cPaX|dU=pH!xC#RRP~F|k delta 40 scmdnDLH^cC`Gyw87N!>F7M2#)7Pc+yv-Gyh9c5P#UY_I3-T+|&05;AI