From c56affd66345a92f64fb8c7451ced58b5340dd70 Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 17 Oct 2024 00:07:37 +0100 Subject: [PATCH 01/17] Added SVG icon Signed-off-by: snipe --- app/Helpers/Helper.php | 37 ++----------------------------------- 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php index 18e149b57d83..6c43fec05a19 100644 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -1123,6 +1123,7 @@ public static function filetype_icon($filename) 'png' => 'far fa-image', 'webp' => 'far fa-image', 'avif' => 'far fa-image', + 'svg' => 'fas fa-vector-square', // word 'doc' => 'far fa-file-word', 'docx' => 'far fa-file-word', @@ -1135,7 +1136,7 @@ public static function filetype_icon($filename) //Text 'txt' => 'far fa-file-alt', 'rtf' => 'far fa-file-alt', - 'xml' => 'far fa-file-alt', + 'xml' => 'fas fa-code', // Misc 'pdf' => 'far fa-file-pdf', 'lic' => 'far fa-save', @@ -1148,41 +1149,7 @@ public static function filetype_icon($filename) return 'far fa-file'; } - public static function show_file_inline($filename) - { - $extension = substr(strrchr($filename, '.'), 1); - - if ($extension) { - switch ($extension) { - case 'jpg': - case 'jpeg': - case 'gif': - case 'png': - case 'webp': - case 'avif': - return true; - break; - default: - return false; - } - } - return false; - } - - /** - * Generate a random encrypted password. - * - * @author Wes Hulette - * - * @since 5.0.0 - * - * @return string - */ - public static function generateEncyrptedPassword(): string - { - return bcrypt(self::generateUnencryptedPassword()); - } /** * Get a random unencrypted password. From 96191a5e9391e86701095b74fefc9064e4bf74d3 Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 17 Oct 2024 00:07:54 +0100 Subject: [PATCH 02/17] Added method to decide if the file should be inlinable Signed-off-by: snipe --- app/Helpers/StorageHelper.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/app/Helpers/StorageHelper.php b/app/Helpers/StorageHelper.php index 2cdab1d66c40..0b56aa97cd3c 100644 --- a/app/Helpers/StorageHelper.php +++ b/app/Helpers/StorageHelper.php @@ -25,4 +25,25 @@ public static function downloader($filename, $disk = 'default') : BinaryFileResp return Storage::disk($disk)->download($filename); } } + + public static function allowSafeInline($file_with_path) { + + $allowed_inline = [ + 'pdf', + 'svg', + 'jpg', + 'gif', + 'svg', + 'avif', + 'webp', + 'png', + ]; + + if (in_array(pathinfo($file_with_path, PATHINFO_EXTENSION), $allowed_inline)) { + return true; + } + + return false; + + } } From ccd20194484313c6e53bf357687ef8a29a4fffe6 Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 17 Oct 2024 00:08:04 +0100 Subject: [PATCH 03/17] Removed unusded use statements Signed-off-by: snipe --- app/Http/Controllers/Users/UserFilesController.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/Http/Controllers/Users/UserFilesController.php b/app/Http/Controllers/Users/UserFilesController.php index 9e5f322c03eb..c1555705136d 100644 --- a/app/Http/Controllers/Users/UserFilesController.php +++ b/app/Http/Controllers/Users/UserFilesController.php @@ -7,9 +7,6 @@ use App\Http\Requests\UploadFileRequest; use App\Models\Actionlog; use App\Models\User; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Input; -use Illuminate\Support\Facades\Response; use Symfony\Component\HttpFoundation\JsonResponse; use Illuminate\Support\Facades\Storage; From c49abb6aeafbfbf43ebcb7e1a9c565288b72b9d9 Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 17 Oct 2024 00:08:38 +0100 Subject: [PATCH 04/17] Refactor the UserFilesController show method for simpler inlining Signed-off-by: snipe --- .../Controllers/Users/UserFilesController.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Users/UserFilesController.php b/app/Http/Controllers/Users/UserFilesController.php index c1555705136d..377692965b4a 100644 --- a/app/Http/Controllers/Users/UserFilesController.php +++ b/app/Http/Controllers/Users/UserFilesController.php @@ -113,6 +113,9 @@ public function destroy($userId = null, $fileId = null) public function show($userId = null, $fileId = null) { + + + if (empty($fileId)) { return redirect()->route('users.show')->with('error', 'Invalid file request'); } @@ -126,15 +129,21 @@ public function show($userId = null, $fileId = null) if ($log = Actionlog::whereNotNull('filename')->where('item_id', $user->id)->find($fileId)) { - // Display the file inline - if (request('inline') == 'true') { + $file = 'private_uploads/users/'.$log->filename; + + + if ((request('inline') == 'true') && (StorageHelper::allowSafeInline($file) === false)) { + + // Display the file as text is not allowed for security reasons $headers = [ 'Content-Disposition' => 'inline', + 'Content-Type' => 'text/plain', ]; - return Storage::download('private_uploads/users/'.$log->filename, $log->filename, $headers); + return Storage::download($file, $log->filename, $headers); + } - return Storage::download('private_uploads/users/'.$log->filename); + return Storage::download($file); } return redirect()->route('users.index')->with('error', trans('admin/users/message.log_record_not_found')); From c49921f50fc54785b87d59e6265b29088bba48fe Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 17 Oct 2024 00:08:54 +0100 Subject: [PATCH 05/17] Removed unused (maybe?) API endpoint Signed-off-by: snipe --- app/Http/Transformers/ActionlogsTransformer.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Http/Transformers/ActionlogsTransformer.php b/app/Http/Transformers/ActionlogsTransformer.php index d0605c747bb1..49eee4241575 100644 --- a/app/Http/Transformers/ActionlogsTransformer.php +++ b/app/Http/Transformers/ActionlogsTransformer.php @@ -158,7 +158,6 @@ public function transformActionlog (Actionlog $actionlog, $settings = null) [ 'url' => $file_url, 'filename' => $actionlog->filename, - 'inlineable' => (bool) Helper::show_file_inline($actionlog->filename), ] : null, 'item' => ($actionlog->item) ? [ From 017884f8432abd3afed7397a7967bb2813df0262 Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 17 Oct 2024 00:09:09 +0100 Subject: [PATCH 06/17] Added checks and filetype display Signed-off-by: snipe --- resources/views/users/view.blade.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/resources/views/users/view.blade.php b/resources/views/users/view.blade.php index 4ef3a0c24ff6..d33b40d94871 100755 --- a/resources/views/users/view.blade.php +++ b/resources/views/users/view.blade.php @@ -995,7 +995,8 @@ class="table table-striped snipe-table" - {{trans('general.file_type')}} + {{trans('general.icon')}} + {{trans('general.file_type')}} {{ trans('general.image') }} {{ trans('general.file_name') }} {{ trans('general.filesize') }} @@ -1009,9 +1010,11 @@ class="table table-striped snipe-table" @foreach ($user->uploads as $file) - + {{ Helper::filetype_icon($file->filename) }} - + + + {{ pathinfo('private_uploads/users/'.$file->filename, PATHINFO_EXTENSION) }} @if (($file->filename) && (Storage::exists('private_uploads/users/'.$file->filename))) @@ -1045,7 +1048,7 @@ class="table table-striped snipe-table" {{ trans('general.download') }} - + @endif From c01190fac279b38d8ee74a636d8cf9c2fd7a7285 Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 17 Oct 2024 00:18:34 +0100 Subject: [PATCH 07/17] Conditionally add content-type Signed-off-by: snipe --- app/Http/Controllers/Users/UserFilesController.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Users/UserFilesController.php b/app/Http/Controllers/Users/UserFilesController.php index 377692965b4a..15b8018b78d7 100644 --- a/app/Http/Controllers/Users/UserFilesController.php +++ b/app/Http/Controllers/Users/UserFilesController.php @@ -132,15 +132,19 @@ public function show($userId = null, $fileId = null) $file = 'private_uploads/users/'.$log->filename; - if ((request('inline') == 'true') && (StorageHelper::allowSafeInline($file) === false)) { - // Display the file as text is not allowed for security reasons + if (request('inline') == 'true') { + $headers = [ 'Content-Disposition' => 'inline', - 'Content-Type' => 'text/plain', ]; - return Storage::download($file, $log->filename, $headers); + // This is NOT allowed as inline - force it to be displayed as text + if (StorageHelper::allowSafeInline($file) === false) { + array_push($headers, ['Content-Type' => 'text/plain']); + } + + return Storage::download($file, $log->filename, $headers); } return Storage::download($file); From 02c80ff18af48c016433807b4bdc0f384ffde41a Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 17 Oct 2024 00:18:40 +0100 Subject: [PATCH 08/17] Added comment Signed-off-by: snipe --- app/Helpers/StorageHelper.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/Helpers/StorageHelper.php b/app/Helpers/StorageHelper.php index 0b56aa97cd3c..ef0facc27b67 100644 --- a/app/Helpers/StorageHelper.php +++ b/app/Helpers/StorageHelper.php @@ -26,6 +26,15 @@ public static function downloader($filename, $disk = 'default') : BinaryFileResp } } + + /** + * This determines the file types that should be allowed inline and checks their fileinfo extension + * + * @author [ + * @since v7.0.14 + * @param $file_with_path + * @return bool + */ public static function allowSafeInline($file_with_path) { $allowed_inline = [ From d67addc69ee2fde2fdc9d90e1eefa13f1c1e4f26 Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 17 Oct 2024 00:21:43 +0100 Subject: [PATCH 09/17] =?UTF-8?q?Removed=20filetype=20column=20-=20it?= =?UTF-8?q?=E2=80=99s=20dumb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: snipe --- resources/views/users/view.blade.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/resources/views/users/view.blade.php b/resources/views/users/view.blade.php index d33b40d94871..223d525f1d64 100755 --- a/resources/views/users/view.blade.php +++ b/resources/views/users/view.blade.php @@ -995,7 +995,6 @@ class="table table-striped snipe-table" - {{trans('general.icon')}} {{trans('general.file_type')}} {{ trans('general.image') }} {{ trans('general.file_name') }} @@ -1013,9 +1012,6 @@ class="table table-striped snipe-table" {{ Helper::filetype_icon($file->filename) }} - - {{ pathinfo('private_uploads/users/'.$file->filename, PATHINFO_EXTENSION) }} - @if (($file->filename) && (Storage::exists('private_uploads/users/'.$file->filename))) @if (Helper::checkUploadIsImage($file->get_src('users'))) From 4933aa5784e3a86021136775055d81eed7b3c966 Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 17 Oct 2024 00:27:04 +0100 Subject: [PATCH 10/17] Add StorageHelper to app config Signed-off-by: snipe --- config/app.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config/app.php b/config/app.php index bc74b4dd0547..39898ff4372b 100755 --- a/config/app.php +++ b/config/app.php @@ -280,7 +280,6 @@ Illuminate\Redis\RedisServiceProvider::class, Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, Illuminate\Session\SessionServiceProvider::class, -// Illuminate\Translation\TranslationServiceProvider::class, //replaced on next line App\Providers\SnipeTranslationServiceProvider::class, //we REPLACE the default Laravel translator with our own Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, @@ -373,7 +372,7 @@ 'Image' => Intervention\Image\ImageServiceProvider::class, 'Carbon' => Carbon\Carbon::class, 'Helper' => App\Helpers\Helper::class, - // makes it much easier to use 'Helper::blah' in blades (which is where we usually use this) + 'StorageHelper' => App\Helpers\StorageHelper::class, 'Icon' => App\Helpers\IconHelper::class, 'Socialite' => Laravel\Socialite\Facades\Socialite::class, From 0e9b3c9119e77259ed2f8740ae98e9149936bd51 Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 17 Oct 2024 00:27:39 +0100 Subject: [PATCH 11/17] Check for existence before trying to get the icon Signed-off-by: snipe --- resources/views/users/view.blade.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/resources/views/users/view.blade.php b/resources/views/users/view.blade.php index 223d525f1d64..7821b874026e 100755 --- a/resources/views/users/view.blade.php +++ b/resources/views/users/view.blade.php @@ -1009,13 +1009,17 @@ class="table table-striped snipe-table" @foreach ($user->uploads as $file) - - {{ Helper::filetype_icon($file->filename) }} + @if (Storage::exists('private_uploads/users/'.$file->filename)) + + {{ Helper::filetype_icon($file->filename) }} + @endif @if (($file->filename) && (Storage::exists('private_uploads/users/'.$file->filename))) @if (Helper::checkUploadIsImage($file->get_src('users'))) - + + + @else {{ trans('general.preview_not_available') }} @endif @@ -1044,7 +1048,7 @@ class="table table-striped snipe-table" {{ trans('general.download') }} - + @endif From d1149730be99fdc8e59e91ce3b02c863a08d4aa1 Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 17 Oct 2024 16:31:17 +0100 Subject: [PATCH 12/17] Apply blade component to files views Signed-off-by: snipe --- app/Helpers/StorageHelper.php | 1 + resources/views/accessories/view.blade.php | 106 +---------- resources/views/blade/filestable.blade.php | 140 ++++++++++++++ resources/views/components/view.blade.php | 98 +--------- resources/views/consumables/view.blade.php | 102 +---------- resources/views/hardware/view.blade.php | 201 +-------------------- resources/views/licenses/view.blade.php | 99 +--------- resources/views/models/view.blade.php | 98 +--------- resources/views/users/view.blade.php | 104 +---------- 9 files changed, 194 insertions(+), 755 deletions(-) create mode 100644 resources/views/blade/filestable.blade.php diff --git a/app/Helpers/StorageHelper.php b/app/Helpers/StorageHelper.php index ef0facc27b67..950e70cc49ac 100644 --- a/app/Helpers/StorageHelper.php +++ b/app/Helpers/StorageHelper.php @@ -29,6 +29,7 @@ public static function downloader($filename, $disk = 'default') : BinaryFileResp /** * This determines the file types that should be allowed inline and checks their fileinfo extension + * to determine that they are safe to display inline. * * @author [ * @since v7.0.14 diff --git a/resources/views/accessories/view.blade.php b/resources/views/accessories/view.blade.php index 1f7e5f0b9a3f..e3f98bf61a85 100644 --- a/resources/views/accessories/view.blade.php +++ b/resources/views/accessories/view.blade.php @@ -154,103 +154,17 @@ class="table table-striped snipe-table" @can('accessories.files', $accessory)
- -
-
-
- - - - - - - - - - - - - - - @if ($accessory->uploads->count() > 0) - @foreach ($accessory->uploads as $file) - - - - - - - - - - - - @endforeach - @else - - - - @endif - -
{{trans('general.file_type')}}{{ trans('general.image') }}{{ trans('general.file_name') }}{{ trans('general.filesize') }}{{ trans('general.notes') }}{{ trans('general.download') }}{{ trans('general.created_at') }}{{ trans('table.actions') }}
- - - {{ Helper::filetype_icon($file->filename) }} - - - @if ($file->filename) - @if ( Helper::checkUploadIsImage($file->get_src('accessories'))) - - @endif - @endif - - {{ $file->filename }} - - {{ @Helper::formatFilesizeUnits(Storage::exists('private_uploads/accessories/'.$file->filename) ? Storage::size('private_uploads/accessories/'.$file->filename) : '') }} - - @if ($file->note) - {{ $file->note }} - @endif - - @if ($file->filename) - - - {{ trans('general.download') }} - - - - - - - @endif - {{ $file->created_at }} - - - {{ trans('general.delete') }} - -
{{ trans('general.no_results') }}
+
+
+ + +
-
-
-
+
@endcan diff --git a/resources/views/blade/filestable.blade.php b/resources/views/blade/filestable.blade.php new file mode 100644 index 000000000000..8d258d606d97 --- /dev/null +++ b/resources/views/blade/filestable.blade.php @@ -0,0 +1,140 @@ + +@props([ + 'filepath', + 'object', + 'showfile_routename', + 'deletefile_routename', +]) + + +
+ + + + + + + + + + + + + + + + + + @foreach ($object->uploads as $file) + + + + + + + + + + + + + + + + @endforeach + + +
+ {{trans('general.id')}} + + {{trans('general.file_type')}} + + {{ trans('general.image') }} + + {{ trans('general.file_name') }} + + {{ trans('general.filesize') }} + + {{ trans('general.notes') }} + + {{ trans('general.download') }} + + {{ trans('general.created_at') }} + + {{ trans('general.created_by') }} + + {{ trans('table.actions') }} +
+ {{ $file->id }} + + @if (Storage::exists($filepath.$file->filename)) + {{ pathinfo($filepath.$file->filename, PATHINFO_EXTENSION) }} + + @endif + + @if (($file->filename) && (Storage::exists($filepath.$file->filename))) + @if (Helper::checkUploadIsImage($file->get_src($object))) + + + + @else + {{ trans('general.preview_not_available') }} + @endif + @else + + {{ trans('general.file_not_found') }} + @endif + + {{ $file->filename }} + + {{ (Storage::exists($filepath.$file->filename)) ? Helper::formatFilesizeUnits(Storage::size($filepath.$file->filename)) : '' }} + + @if ($file->note) + {{ $file->note }} + @endif + + @if ($file->filename) + @if (Storage::exists($filepath.$file->filename)) + + + + {{ trans('general.download') }} + + + + + + + @endif + @endif + + {{ $file->created_at }} + + {{ $file->created_by }} + + + + {{ trans('general.delete') }} + +
+
+ \ No newline at end of file diff --git a/resources/views/components/view.blade.php b/resources/views/components/view.blade.php index 287b0b23a878..5bd185bedfa4 100644 --- a/resources/views/components/view.blade.php +++ b/resources/views/components/view.blade.php @@ -140,96 +140,14 @@ class="table table-striped snipe-table" @can('components.files', $component)
- -
- - - - - - - - - - - - - - - @if ($component->uploads->count() > 0) - @foreach ($component->uploads as $file) - - - - - - - - - - - - @endforeach - @else - - - - @endif - -
{{trans('general.file_type')}}{{ trans('general.image') }}{{ trans('general.file_name') }}{{ trans('general.filesize') }}{{ trans('general.notes') }}{{ trans('general.download') }}{{ trans('general.created_at') }}{{ trans('table.actions') }}
- - {{ Helper::filetype_icon($file->filename) }} - - - @if ($file->filename) - @if ( Helper::checkUploadIsImage($file->get_src('components'))) - - @endif - @endif - - {{ $file->filename }} - - {{ @Helper::formatFilesizeUnits(Storage::exists('private_uploads/components/'.$file->filename) ? Storage::size('private_uploads/components/'.$file->filename) : '') }} - - @if ($file->note) - {{ $file->note }} - @endif - - @if ($file->filename) - - - {{ trans('general.download') }} - - - - - - - @endif - {{ $file->created_at }} - - - {{ trans('general.delete') }} - -
{{ trans('general.no_results') }}
+
+
+ +
@endcan diff --git a/resources/views/consumables/view.blade.php b/resources/views/consumables/view.blade.php index 88ea19195f36..87650ac9ebb9 100644 --- a/resources/views/consumables/view.blade.php +++ b/resources/views/consumables/view.blade.php @@ -428,102 +428,18 @@ class="table table-striped snipe-table"
+
+
+ -
-
- - - - - - - - - - - - - - - - @if ($consumable->uploads->count() > 0) - @foreach ($consumable->uploads as $file) - - - - - - - - - - - - @endforeach - @else - - - - @endif - -
{{trans('general.file_type')}}{{ trans('general.image') }}{{ trans('general.file_name') }}{{ trans('general.filesize') }}{{ trans('general.notes') }}{{ trans('general.download') }}{{ trans('general.created_at') }}{{ trans('table.actions') }}
- - {{ Helper::filetype_icon($file->filename) }} - - - @if ($file->filename) - @if ( Helper::checkUploadIsImage($file->get_src('consumables'))) - - @endif - @endif - - {{ $file->filename }} - - {{ @Helper::formatFilesizeUnits(Storage::exists('private_uploads/consumables/'.$file->filename) ? Storage::size('private_uploads/consumables/'.$file->filename) : '') }} - - @if ($file->note) - {!! nl2br(Helper::parseEscapedMarkedownInline($file->note)) !!} - @endif - - @if ($file->filename) - - - {{ trans('general.download') }} - - - - - - @endif - {{ $file->created_at }} - - - {{ trans('general.delete') }} - - -
{{ trans('general.no_results') }}
-
-
+
+
diff --git a/resources/views/hardware/view.blade.php b/resources/views/hardware/view.blade.php index 1e040b760362..f93c26cf8ee0 100755 --- a/resources/views/hardware/view.blade.php +++ b/resources/views/hardware/view.blade.php @@ -1362,102 +1362,11 @@ class="table table-striped snipe-table"
- - @if ($asset->uploads->count() > 0) - - - - - - - - - - - - - - - - @foreach ($asset->uploads as $file) - - - - - - - - - - - @endforeach - -
{{trans('general.file_type')}}{{ trans('general.image') }}{{ trans('general.file_name') }}{{ trans('general.filesize') }}{{ trans('general.notes') }}{{ trans('general.download') }}{{ trans('general.created_at') }}{{ trans('table.actions') }}
- @if ( Helper::checkUploadIsImage($file->get_src('assets'))) - - - - @endif - - @if (Storage::exists('private_uploads/assets/'.$file->filename)) - {{ $file->filename }} - @else - {{ $file->filename }} - @endif - - {{ @Helper::formatFilesizeUnits(Storage::exists('private_uploads/assets/'.$file->filename) ? Storage::size('private_uploads/assets/'.$file->filename) : '') }} - - @if ($file->note) - {{ $file->note }} - @endif - - @if (($file->filename) && (Storage::exists('private_uploads/assets/'.$file->filename))) - - - - - - - - @endif - - @if ($file->created_at) - {{ Helper::getFormattedDateObject($file->created_at, 'datetime', false) }} - @endif - - @can('update', \App\Models\Asset::class) - - - - @endcan -
- - @else - -
- - {{ trans('general.no_results') }} -
- @endif - +
@@ -1467,101 +1376,11 @@ class="table table-striped snipe-table"
- @if (($asset->model) && ($asset->model->uploads->count() > 0)) - - - - - - - - - - - - - - - - @foreach ($asset->model->uploads as $file) - - - - - - - - - - - @endforeach - -
{{trans('general.file_type')}}{{ trans('general.image') }}{{ trans('general.file_name') }}{{ trans('general.filesize') }}{{ trans('general.notes') }}{{ trans('general.download') }}{{ trans('general.created_at') }}{{ trans('table.actions') }}
- @if ( Helper::checkUploadIsImage($file->get_src('assetmodels'))) - - - - @endif - - @if (Storage::exists('private_uploads/assetmodels/'.$file->filename)) - {{ $file->filename }} - @else - {{ $file->filename }} - @endif - - {{ (Storage::exists('private_uploads/assetmodels/'.$file->filename)) ? Helper::formatFilesizeUnits(Storage::size('private_uploads/assetmodels/'.$file->filename)) : '' }} - - @if ($file->note) - {{ $file->note }} - @endif - - @if (($file->filename) && (Storage::exists('private_uploads/assetmodels/'.$file->filename))) - - - - - - - - - @endif - - @if ($file->created_at) - {{ Helper::getFormattedDateObject($file->created_at, 'datetime', false) }} - @endif - - @can('update', \App\Models\AssetModel::class) - - - - @endcan -
- - @else - -
- - {{ trans('general.no_results') }} -
- @endif +
diff --git a/resources/views/licenses/view.blade.php b/resources/views/licenses/view.blade.php index c20ef87b2dd3..2de0f9d6e0bc 100755 --- a/resources/views/licenses/view.blade.php +++ b/resources/views/licenses/view.blade.php @@ -464,100 +464,13 @@ class="table table-striped snipe-table" @can('licenses.files', $license)
-
- - - - - - - - - - - - - - - @if ($license->uploads->count() > 0) - @foreach ($license->uploads as $file) - - - - - - - - - - - - @endforeach - @else - - - - @endif - -
{{trans('general.file_type')}}{{ trans('general.image') }}{{ trans('general.file_name') }}{{ trans('general.filesize') }}{{ trans('general.notes') }}{{ trans('general.download') }}{{ trans('general.created_at') }}{{ trans('table.actions') }}
- - {{ Helper::filetype_icon($file->filename) }} - - - @if ($file->filename) - @if ((Storage::exists('private_uploads/licenses/'.$file->filename)) && ( Helper::checkUploadIsImage($file->get_src('licenses')))) - - @endif - @endif - - @if (Storage::exists('private_uploads/licenses/'.$file->filename)) - {{ $file->filename }} - @else - {{ $file->filename }} - @endif - - {{ (Storage::exists('private_uploads/licenses/'.$file->filename)) ? Helper::formatFilesizeUnits(Storage::size('private_uploads/licenses/'.$file->filename)) : '' }} - - @if ($file->note) - {{ $file->note }} - @endif - - @if ($file->filename) - - - {{ trans('general.download') }} - - - - - - @endif - {{ $file->created_at }} - - - {{ trans('general.delete') }} - -
{{ trans('general.no_results') }}
-
+ +
@endcan diff --git a/resources/views/models/view.blade.php b/resources/views/models/view.blade.php index 65f9bdd4feda..f26b9babd4b1 100755 --- a/resources/views/models/view.blade.php +++ b/resources/views/models/view.blade.php @@ -107,99 +107,11 @@ class="table table-striped snipe-table"
- @if ($model->uploads->count() > 0) - - - - - - - - - - - - - - - - @foreach ($model->uploads as $file) - - - - - - - - - - - @endforeach - -
{{trans('general.file_type')}}{{ trans('general.image') }}{{ trans('general.file_name') }}{{ trans('general.filesize') }}{{ trans('general.notes') }}{{ trans('general.download') }}{{ trans('general.created_at') }}{{ trans('table.actions') }}
- @if ((Storage::exists('private_uploads/assetmodels/'.$file->filename)) && ( Helper::checkUploadIsImage($file->get_src('assetmodels')))) - - - - @endif - - @if (Storage::exists('private_uploads/assetmodels/'.$file->filename)) - {{ $file->filename }} - @else - {{ $file->filename }} - @endif - - {{ (Storage::exists('private_uploads/assetmodels/'.$file->filename)) ? Helper::formatFilesizeUnits(Storage::size('private_uploads/assetmodels/'.$file->filename)) : '' }} - - @if ($file->note) - {{ $file->note }} - @endif - - @if (($file->filename) && (Storage::exists('private_uploads/assetmodels/'.$file->filename))) - - - - - - - - - @endif - - @if ($file->created_at) - {{ Helper::getFormattedDateObject($file->created_at, 'datetime', false) }} - @endif - - @can('update', \App\Models\AssetModel::class) - - @endcan -
- - @else - -
- - {{ trans('general.no_results') }} -
- @endif +
diff --git a/resources/views/users/view.blade.php b/resources/views/users/view.blade.php index 7821b874026e..495d0a2ca6ae 100755 --- a/resources/views/users/view.blade.php +++ b/resources/views/users/view.blade.php @@ -971,105 +971,11 @@ class="table table-striped snipe-table table-hover"
-
- - - - - - - - - - - - - - - - @foreach ($user->uploads as $file) - - - - - - - - - - - - - - - @endforeach - - -
{{trans('general.file_type')}}{{ trans('general.image') }}{{ trans('general.file_name') }}{{ trans('general.filesize') }}{{ trans('general.notes') }}{{ trans('general.download') }}{{ trans('general.created_at') }}{{ trans('table.actions') }}
- @if (Storage::exists('private_uploads/users/'.$file->filename)) - - {{ Helper::filetype_icon($file->filename) }} - @endif - - @if (($file->filename) && (Storage::exists('private_uploads/users/'.$file->filename))) - @if (Helper::checkUploadIsImage($file->get_src('users'))) - - - - @else - {{ trans('general.preview_not_available') }} - @endif - @else - - {{ trans('general.file_not_found') }} - @endif - - {{ $file->filename }} - - {{ (Storage::exists('private_uploads/users/'.$file->filename)) ? Helper::formatFilesizeUnits(Storage::size('private_uploads/users/'.$file->filename)) : '' }} - - @if ($file->note) - {{ $file->note }} - @endif - - @if ($file->filename) - @if (Storage::exists('private_uploads/users/'.$file->filename)) - - - {{ trans('general.download') }} - - - - - - @endif - @endif - {{ $file->created_at }} - - - {{ trans('general.delete') }} - -
-
+
From 61053238777c38579c6188d2380f0cb7b99ae59e Mon Sep 17 00:00:00 2001 From: snipe Date: Mon, 21 Oct 2024 14:11:20 +0100 Subject: [PATCH 13/17] Use plural class name for src Signed-off-by: snipe --- resources/views/blade/filestable.blade.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/resources/views/blade/filestable.blade.php b/resources/views/blade/filestable.blade.php index 8d258d606d97..50c24dd6c924 100644 --- a/resources/views/blade/filestable.blade.php +++ b/resources/views/blade/filestable.blade.php @@ -9,9 +9,9 @@
+ @if (($file->filename) && (Storage::exists($filepath.$file->filename))) - @if (Helper::checkUploadIsImage($file->get_src($object))) + @if (Helper::checkUploadIsImage($file->get_src(str_plural(strtolower(class_basename(get_class($object))))))) @@ -88,6 +89,7 @@ class="table table-striped snipe-table" {{ trans('general.file_not_found') }} @endif + {{ $file->filename }} From 06c599cc17ca8f7a29515564e5cac9be905e338d Mon Sep 17 00:00:00 2001 From: snipe Date: Mon, 21 Oct 2024 16:34:03 +0100 Subject: [PATCH 14/17] Added method to show or download file Signed-off-by: snipe --- app/Helpers/StorageHelper.php | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/app/Helpers/StorageHelper.php b/app/Helpers/StorageHelper.php index 950e70cc49ac..47700f913ac5 100644 --- a/app/Helpers/StorageHelper.php +++ b/app/Helpers/StorageHelper.php @@ -7,6 +7,7 @@ use Illuminate\Http\RedirectResponse; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\StreamedResponse; +use Illuminate\Contracts\Filesystem\FileNotFoundException; class StorageHelper { public static function downloader($filename, $disk = 'default') : BinaryFileResponse | RedirectResponse | StreamedResponse @@ -49,11 +50,40 @@ public static function allowSafeInline($file_with_path) { 'png', ]; - if (in_array(pathinfo($file_with_path, PATHINFO_EXTENSION), $allowed_inline)) { + + // The file exists and is allowed to be displayed inline + if (Storage::exists($file_with_path) && (in_array(pathinfo($file_with_path, PATHINFO_EXTENSION), $allowed_inline))) { return true; } - return false; } + + /** + * Decide whether to show the file inline or download it. + */ + public static function showOrDownloadFile($file, $filename) { + + $headers = []; + + if (request('inline') == 'true') { + + $headers = [ + 'Content-Disposition' => 'inline', + ]; + + // This is NOT allowed as inline - force it to be displayed as text in the browser + if (self::allowSafeInline($file) != true) { + $headers = array_merge($headers, ['Content-Type' => 'text/plain']); + } + } + + // Everything else seems okay, but the file doesn't exist on the server. + if (Storage::missing($file)) { + throw new FileNotFoundException(); + } + + return Storage::download($file, $filename, $headers); + + } } From ef9b6e3b072f88bfec52c4ec930816d30eb34cb9 Mon Sep 17 00:00:00 2001 From: snipe Date: Mon, 21 Oct 2024 16:34:54 +0100 Subject: [PATCH 15/17] Code cleanup Signed-off-by: snipe --- .../AccessoriesFilesController.php | 43 +++++-------------- .../Assets/AssetFilesController.php | 40 ++++++----------- .../Components/ComponentsFilesController.php | 35 +++++---------- .../ConsumablesFilesController.php | 36 ++++------------ .../Licenses/LicenseFilesController.php | 35 ++++----------- .../Controllers/Users/UserFilesController.php | 32 ++++---------- 6 files changed, 58 insertions(+), 163 deletions(-) diff --git a/app/Http/Controllers/Accessories/AccessoriesFilesController.php b/app/Http/Controllers/Accessories/AccessoriesFilesController.php index b63c202d3001..9ebdea9b836b 100644 --- a/app/Http/Controllers/Accessories/AccessoriesFilesController.php +++ b/app/Http/Controllers/Accessories/AccessoriesFilesController.php @@ -106,50 +106,29 @@ public function destroy($accessoryId = null, $fileId = null) : RedirectResponse * @param int $accessoryId * @param int $fileId */ - public function show($accessoryId = null, $fileId = null, $download = true) : View | RedirectResponse | Response | BinaryFileResponse | StreamedResponse + public function show($accessoryId = null, $fileId = null) : View | RedirectResponse | Response | BinaryFileResponse | StreamedResponse { - Log::debug('Private filesystem is: '.config('filesystems.default')); - $accessory = Accessory::find($accessoryId); - - // the accessory is valid - if (isset($accessory->id)) { + if ($accessory = Accessory::find($accessoryId)) { $this->authorize('view', $accessory); $this->authorize('accessories.files', $accessory); - if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $accessory->id)->find($fileId)) { - return redirect()->route('accessories.index')->with('error', trans('admin/users/message.log_record_not_found')); - } - - $file = 'private_uploads/accessories/'.$log->filename; - - if (Storage::missing($file)) { - Log::debug('FILE DOES NOT EXISTS for '.$file); - Log::debug('URL should be '.Storage::url($file)); + if ($log = Actionlog::whereNotNull('filename')->where('item_id', $accessory->id)->find($fileId)) { + $file = 'private_uploads/accessories/'.$log->filename; - return response('File '.$file.' ('.Storage::url($file).') not found on server', 404) - ->header('Content-Type', 'text/plain'); - } else { - - // Display the file inline - if (request('inline') == 'true') { - $headers = [ - 'Content-Disposition' => 'inline', - ]; - return Storage::download($file, $log->filename, $headers); + try { + return StorageHelper::showOrDownloadFile($file, $log->filename); + } catch (\Exception $e) { + return redirect()->route('accessories.show', ['accessory' => $accessory])->with('error', trans('general.file_not_found')); } + } + // todo - // We have to override the URL stuff here, since local defaults in Laravel's Flysystem - // won't work, as they're not accessible via the web - if (config('filesystems.default') == 'local') { // TODO - is there any way to fix this at the StorageHelper layer? - return StorageHelper::downloader($file); - } - } } - return redirect()->route('accessories.index')->with('error', trans('general.file_does_not_exist', ['id' => $fileId])); + return redirect()->route('accessories.index')->with('error', trans('general.file_not_found')); } } diff --git a/app/Http/Controllers/Assets/AssetFilesController.php b/app/Http/Controllers/Assets/AssetFilesController.php index b5a04759bbee..b8ae3814dabd 100644 --- a/app/Http/Controllers/Assets/AssetFilesController.php +++ b/app/Http/Controllers/Assets/AssetFilesController.php @@ -61,43 +61,29 @@ public function store(UploadFileRequest $request, $assetId = null) : RedirectRes */ public function show($assetId = null, $fileId = null) : View | RedirectResponse | Response | StreamedResponse | BinaryFileResponse { - $asset = Asset::find($assetId); - // the asset is valid - if (isset($asset->id)) { + if ($asset = Asset::find($assetId)) { + $this->authorize('view', $asset); - if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) { - return response('No matching record for that asset/file', 500) - ->header('Content-Type', 'text/plain'); - } + if ($log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) { + $file = 'private_uploads/assets/'.$log->filename; - $file = 'private_uploads/assets/'.$log->filename; + if ($log->action_type == 'audit') { + $file = 'private_uploads/audits/'.$log->filename; + } - if ($log->action_type == 'audit') { - $file = 'private_uploads/audits/'.$log->filename; - } + try { + return StorageHelper::showOrDownloadFile($file, $log->filename); + } catch (\Exception $e) { + return redirect()->route('hardware.show', ['hardware' => $asset])->with('error', trans('general.file_not_found')); + } - if (! Storage::exists($file)) { - return response('File '.$file.' not found on server', 404) - ->header('Content-Type', 'text/plain'); } - if (request('inline') == 'true') { - - $headers = [ - 'Content-Disposition' => 'inline', - ]; - - return Storage::download($file, $log->filename, $headers); - } + return redirect()->back()->with('error', trans('general.file_not_found_redirect')); - return StorageHelper::downloader($file); } - // Prepare the error message - $error = trans('admin/hardware/message.does_not_exist', ['id' => $fileId]); - // Redirect to the hardware management page - return redirect()->route('hardware.index')->with('error', $error); } /** diff --git a/app/Http/Controllers/Components/ComponentsFilesController.php b/app/Http/Controllers/Components/ComponentsFilesController.php index a7d42bb0729e..4fa3366d3204 100644 --- a/app/Http/Controllers/Components/ComponentsFilesController.php +++ b/app/Http/Controllers/Components/ComponentsFilesController.php @@ -112,40 +112,25 @@ public function destroy($componentId = null, $fileId = null) public function show($componentId = null, $fileId = null) { Log::debug('Private filesystem is: '.config('filesystems.default')); - $component = Component::find($componentId); + // the component is valid - if (isset($component->id)) { + if ($component = Component::find($componentId)) { $this->authorize('view', $component); $this->authorize('components.files', $component); - if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $component->id)->find($fileId)) { - return response('No matching record for that asset/file', 500) - ->header('Content-Type', 'text/plain'); - } - - $file = 'private_uploads/components/'.$log->filename; + if ($log = Actionlog::whereNotNull('filename')->where('item_id', $component->id)->find($fileId)) { - if (Storage::missing($file)) { - Log::debug('FILE DOES NOT EXISTS for '.$file); - Log::debug('URL should be '.Storage::url($file)); + $file = 'private_uploads/components/'.$log->filename; - return response('File '.$file.' ('.Storage::url($file).') not found on server', 404) - ->header('Content-Type', 'text/plain'); - } else { - - // Display the file inline - if (request('inline') == 'true') { - $headers = [ - 'Content-Disposition' => 'inline', - ]; - return Storage::download($file, $log->filename, $headers); + try { + return StorageHelper::showOrDownloadFile($file, $log->filename); + } catch (\Exception $e) { + return redirect()->route('components.show', ['component' => $component])->with('error', trans('general.file_not_found')); } - - if (config('filesystems.default') == 'local') { // TODO - is there any way to fix this at the StorageHelper layer? - return StorageHelper::downloader($file); - } } + // todo + } return redirect()->route('components.index')->with('error', trans('general.file_does_not_exist', ['id' => $fileId])); diff --git a/app/Http/Controllers/Consumables/ConsumablesFilesController.php b/app/Http/Controllers/Consumables/ConsumablesFilesController.php index 35a4ae841ec3..6ada31f63fd3 100644 --- a/app/Http/Controllers/Consumables/ConsumablesFilesController.php +++ b/app/Http/Controllers/Consumables/ConsumablesFilesController.php @@ -104,7 +104,6 @@ public function destroy($consumableId = null, $fileId = null) * @since [v1.4] * @param int $consumableId * @param int $fileId - * @return \Symfony\Consumable\HttpFoundation\Response * @throws \Illuminate\Auth\Access\AuthorizationException */ public function show($consumableId = null, $fileId = null) @@ -116,36 +115,17 @@ public function show($consumableId = null, $fileId = null) $this->authorize('view', $consumable); $this->authorize('consumables.files', $consumable); - if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $consumable->id)->find($fileId)) { - return response('No matching record for that asset/file', 500) - ->header('Content-Type', 'text/plain'); - } - - $file = 'private_uploads/consumables/'.$log->filename; - - if (Storage::missing($file)) { - Log::debug('FILE DOES NOT EXISTS for '.$file); - Log::debug('URL should be '.Storage::url($file)); - - return response('File '.$file.' ('.Storage::url($file).') not found on server', 404) - ->header('Content-Type', 'text/plain'); - } else { - - // Display the file inline - if (request('inline') == 'true') { - $headers = [ - 'Content-Disposition' => 'inline', - ]; - return Storage::download($file, $log->filename, $headers); - } - + if ($log = Actionlog::whereNotNull('filename')->where('item_id', $consumable->id)->find($fileId)) { + $file = 'private_uploads/consumables/'.$log->filename; - // We have to override the URL stuff here, since local defaults in Laravel's Flysystem - // won't work, as they're not accessible via the web - if (config('filesystems.default') == 'local') { // TODO - is there any way to fix this at the StorageHelper layer? - return StorageHelper::downloader($file); + try { + return StorageHelper::showOrDownloadFile($file, $log->filename); + } catch (\Exception $e) { + return redirect()->route('consumables.show', ['consumable' => $consumable])->with('error', trans('general.file_not_found')); } } + // todo + } return redirect()->route('consumables.index')->with('error', trans('general.file_does_not_exist', ['id' => $fileId])); diff --git a/app/Http/Controllers/Licenses/LicenseFilesController.php b/app/Http/Controllers/Licenses/LicenseFilesController.php index fa18e8cf4868..662a696c4906 100644 --- a/app/Http/Controllers/Licenses/LicenseFilesController.php +++ b/app/Http/Controllers/Licenses/LicenseFilesController.php @@ -112,37 +112,18 @@ public function show($licenseId = null, $fileId = null, $download = true) $this->authorize('view', $license); $this->authorize('licenses.files', $license); - if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $license->id)->find($fileId)) { - return response('No matching record for that asset/file', 500) - ->header('Content-Type', 'text/plain'); - } - - $file = 'private_uploads/licenses/'.$log->filename; - - if (Storage::missing($file)) { - Log::debug('NOT EXISTS for '.$file); - Log::debug('NOT EXISTS URL should be '.Storage::url($file)); - - return response('File '.$file.' ('.Storage::url($file).') not found on server', 404) - ->header('Content-Type', 'text/plain'); - } else { - - if (request('inline') == 'true') { + if ($log = Actionlog::whereNotNull('filename')->where('item_id', $license->id)->find($fileId)) { + $file = 'private_uploads/licenses/'.$log->filename; - $headers = [ - 'Content-Disposition' => 'inline', - ]; - - return Storage::download($file, $log->filename, $headers); + try { + return StorageHelper::showOrDownloadFile($file, $log->filename); + } catch (\Exception $e) { + return redirect()->route('licenses.show', ['licenses' => $license])->with('error', trans('general.file_not_found')); } + } - // We have to override the URL stuff here, since local defaults in Laravel's Flysystem - // won't work, as they're not accessible via the web - if (config('filesystems.default') == 'local') { // TODO - is there any way to fix this at the StorageHelper layer? - return StorageHelper::downloader($file); + // todo - } - } } return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist', ['id' => $fileId])); diff --git a/app/Http/Controllers/Users/UserFilesController.php b/app/Http/Controllers/Users/UserFilesController.php index 15b8018b78d7..de3a37de32ce 100644 --- a/app/Http/Controllers/Users/UserFilesController.php +++ b/app/Http/Controllers/Users/UserFilesController.php @@ -114,43 +114,27 @@ public function show($userId = null, $fileId = null) { - - if (empty($fileId)) { return redirect()->route('users.show')->with('error', 'Invalid file request'); } - $user = User::find($userId); - - // the license is valid - if (isset($user->id)) { + if ($user = User::find($userId)) { $this->authorize('view', $user); if ($log = Actionlog::whereNotNull('filename')->where('item_id', $user->id)->find($fileId)) { - $file = 'private_uploads/users/'.$log->filename; - - - if (request('inline') == 'true') { - - $headers = [ - 'Content-Disposition' => 'inline', - ]; - - // This is NOT allowed as inline - force it to be displayed as text - if (StorageHelper::allowSafeInline($file) === false) { - array_push($headers, ['Content-Type' => 'text/plain']); - } - - return Storage::download($file, $log->filename, $headers); + try { + return StorageHelper::showOrDownloadFile($file, $log->filename); + } catch (\Exception $e) { + return redirect()->route('users.show', ['user' => $user])->with('error', trans('general.file_not_found')); } - - return Storage::download($file); } - return redirect()->route('users.index')->with('error', trans('admin/users/message.log_record_not_found')); + // todo + + return redirect()->back()->with('error', trans('general.file_not_found')); } // Redirect to the user management page if the user doesn't exist From 787e651778dbc04acf66cd77faf0386f5a137534 Mon Sep 17 00:00:00 2001 From: snipe Date: Mon, 21 Oct 2024 16:52:21 +0100 Subject: [PATCH 16/17] Fixed todos with log message Signed-off-by: snipe --- .../Controllers/Accessories/AccessoriesFilesController.php | 2 +- app/Http/Controllers/Assets/AssetFilesController.php | 5 +++-- app/Http/Controllers/Assets/AssetsController.php | 1 - .../Controllers/Components/ComponentsFilesController.php | 2 +- .../Controllers/Consumables/ConsumablesFilesController.php | 3 ++- app/Http/Controllers/Licenses/LicenseFilesController.php | 3 ++- app/Http/Controllers/Users/UserFilesController.php | 4 +++- resources/lang/en-US/general.php | 1 + 8 files changed, 13 insertions(+), 8 deletions(-) diff --git a/app/Http/Controllers/Accessories/AccessoriesFilesController.php b/app/Http/Controllers/Accessories/AccessoriesFilesController.php index 9ebdea9b836b..ebc1e4b8e0d8 100644 --- a/app/Http/Controllers/Accessories/AccessoriesFilesController.php +++ b/app/Http/Controllers/Accessories/AccessoriesFilesController.php @@ -125,7 +125,7 @@ public function show($accessoryId = null, $fileId = null) : View | RedirectRespo } } - // todo + return redirect()->route('accessories.show', ['accessory' => $accessory])->with('error', trans('general.log_record_not_found')); } diff --git a/app/Http/Controllers/Assets/AssetFilesController.php b/app/Http/Controllers/Assets/AssetFilesController.php index b8ae3814dabd..d15055c4b2fb 100644 --- a/app/Http/Controllers/Assets/AssetFilesController.php +++ b/app/Http/Controllers/Assets/AssetFilesController.php @@ -80,10 +80,11 @@ public function show($assetId = null, $fileId = null) : View | RedirectResponse } - return redirect()->back()->with('error', trans('general.file_not_found_redirect')); - + return redirect()->route('hardware.show', ['hardware' => $asset])->with('error', trans('general.log_record_not_found')); } + return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist')); + } /** diff --git a/app/Http/Controllers/Assets/AssetsController.php b/app/Http/Controllers/Assets/AssetsController.php index dda54f4c8d39..50a758ac37eb 100755 --- a/app/Http/Controllers/Assets/AssetsController.php +++ b/app/Http/Controllers/Assets/AssetsController.php @@ -17,7 +17,6 @@ use App\Models\Setting; use App\Models\Statuslabel; use App\Models\User; -use Illuminate\Support\Facades\Auth; use App\View\Label; use Carbon\Carbon; use Illuminate\Support\Facades\DB; diff --git a/app/Http/Controllers/Components/ComponentsFilesController.php b/app/Http/Controllers/Components/ComponentsFilesController.php index 4fa3366d3204..83468a0b10b4 100644 --- a/app/Http/Controllers/Components/ComponentsFilesController.php +++ b/app/Http/Controllers/Components/ComponentsFilesController.php @@ -129,7 +129,7 @@ public function show($componentId = null, $fileId = null) return redirect()->route('components.show', ['component' => $component])->with('error', trans('general.file_not_found')); } } - // todo + return redirect()->route('components.show', ['component' => $component])->with('error', trans('general.log_record_not_found')); } diff --git a/app/Http/Controllers/Consumables/ConsumablesFilesController.php b/app/Http/Controllers/Consumables/ConsumablesFilesController.php index 6ada31f63fd3..054fdc0b81a9 100644 --- a/app/Http/Controllers/Consumables/ConsumablesFilesController.php +++ b/app/Http/Controllers/Consumables/ConsumablesFilesController.php @@ -124,7 +124,8 @@ public function show($consumableId = null, $fileId = null) return redirect()->route('consumables.show', ['consumable' => $consumable])->with('error', trans('general.file_not_found')); } } - // todo + // The log record doesn't exist somehow + return redirect()->route('consumables.show', ['consumable' => $consumable])->with('error', trans('general.log_record_not_found')); } diff --git a/app/Http/Controllers/Licenses/LicenseFilesController.php b/app/Http/Controllers/Licenses/LicenseFilesController.php index 662a696c4906..6ab3cb7703aa 100644 --- a/app/Http/Controllers/Licenses/LicenseFilesController.php +++ b/app/Http/Controllers/Licenses/LicenseFilesController.php @@ -122,7 +122,8 @@ public function show($licenseId = null, $fileId = null, $download = true) } } - // todo + // The log record doesn't exist somehow + return redirect()->route('licenses.show', ['licenses' => $license])->with('error', trans('general.log_record_not_found')); } diff --git a/app/Http/Controllers/Users/UserFilesController.php b/app/Http/Controllers/Users/UserFilesController.php index de3a37de32ce..e99bfe298f78 100644 --- a/app/Http/Controllers/Users/UserFilesController.php +++ b/app/Http/Controllers/Users/UserFilesController.php @@ -132,7 +132,9 @@ public function show($userId = null, $fileId = null) } } - // todo + // The log record doesn't exist somehow + return redirect()->route('users.show', ['user' => $user])->with('error', trans('general.log_record_not_found')); + return redirect()->back()->with('error', trans('general.file_not_found')); } diff --git a/resources/lang/en-US/general.php b/resources/lang/en-US/general.php index 34315c99a8ce..c2ca8bf4c739 100644 --- a/resources/lang/en-US/general.php +++ b/resources/lang/en-US/general.php @@ -434,6 +434,7 @@ 'alt_uploaded_image_thumbnail' => 'Uploaded thumbnail', 'placeholder_kit' => 'Select a kit', 'file_not_found' => 'File not found', + 'log_record_not_found' => 'No record for that log entry was found.', 'preview_not_available' => '(no preview)', 'setup' => 'Setup', 'pre_flight' => 'Pre-Flight', From a05c33febfb49b6a845e762817dd20e16db71d0e Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 22 Oct 2024 15:43:19 +0100 Subject: [PATCH 17/17] Squashed commit of the following: commit 147fcfb8ebc4ceeea96d803d6a455abeba54f45a Merge: 58a3d09b5 fdcc17ca2 Author: snipe Date: Tue Oct 22 15:12:55 2024 +0100 Merge pull request #15676 from Toreg87/fixes/api_create_user_fmcs Fix user creation with FullMultipleCompanySupport enabled over API commit 58a3d09b5fd7ffa979bfb9553cacbb24ae452462 Merge: 30a06a594 867fa2f36 Author: snipe Date: Tue Oct 22 14:55:42 2024 +0100 Merge pull request #15703 from marcusmoore/bug/sc-27188 Linked accessory files in activity report commit 30a06a594289571097e2d30901546bc9a17b4bac Merge: 6c6af78e0 ce3086317 Author: snipe Date: Tue Oct 22 11:47:06 2024 +0100 Merge pull request #15693 from marcusmoore/chore/remove-parallel-testing Removed brianium/paratest commit 6c6af78e0840fc4f134e5bbb7965f21f4adcc0e1 Merge: 9b06bbb6c 3f79fd7ea Author: snipe Date: Tue Oct 22 11:46:04 2024 +0100 Merge pull request #15705 from marcusmoore/tests/icon-component-test Added test to ensure icon component does not end in newline commit 3f79fd7ea744bd18134785c37b87a2f4bcff0347 Author: Marcus Moore Date: Mon Oct 21 17:07:40 2024 -0700 Add test to ensure icon component does not end in newline commit 9b06bbb6c37fcea9b6202ad9fb2b4d952210dd01 Merge: 46ad1d072 d7f70146f Author: snipe Date: Mon Oct 21 22:38:26 2024 +0100 Merge pull request #15704 from marcusmoore/bug/remove-extra-icon Removed second icon in accessory file list commit ce30863177e499a29f395c9c88ce9c67bd669a74 Author: Marcus Moore Date: Mon Oct 21 13:57:04 2024 -0700 Remove brianium/paratest dependency commit d7f70146f4a886795ddb118cc2f71bbadded72dc Author: Marcus Moore Date: Mon Oct 21 13:48:25 2024 -0700 Remove extra icon in accessory file upload list commit 867fa2f36e02bdfe0ee74ae98b590fd013f6fc7a Author: Marcus Moore Date: Mon Oct 21 12:40:24 2024 -0700 Display file in activity report for accessories commit 0933a2d4ea6d5babd6ef3e0e2f8350c2e088648d Author: Marcus Moore Date: Thu Oct 17 18:01:48 2024 -0700 Remove --parallel flag commit 46ad1d072f5c45d20c1b078d2b3f84a3a0f36632 Merge: bcb4bd9eb 3cf746d7d Author: snipe Date: Thu Oct 17 15:29:47 2024 +0100 Merge pull request #15680 from uberbrady/bulk_checkout_to_bulk_actions Bulk checkout to bulk actions commit bcb4bd9eb4e419e8a125a7dccd3e79c39dc13e21 Merge: 250037540 f50ccbcc4 Author: snipe Date: Thu Oct 17 10:20:13 2024 +0100 Merge pull request #15683 from Toreg87/fixes/outdated_comment Fix outdated comment in CompanyableTrait commit f50ccbcc492db6c98cabf6dc6752dd99ab82bce7 Author: Tobias Regnery Date: Thu Oct 17 11:07:28 2024 +0200 Fix outdated comment in CompanyableTrait As of commit 5800e8d the user model uses CompanyableTrait so remove this clearly outdated comment commit 3cf746d7df83ef3e7cfa45c602fc182ebe8f11e3 Author: Brady Wetherington Date: Wed Oct 16 23:13:32 2024 +0100 Rework the bulk checkout to not change how all checkouts work commit 6b7af802af41c92a36e77605415869c9e72ec192 Author: Brady Wetherington Date: Thu Oct 10 13:28:23 2024 +0100 Add 'bulk checkout' as one of the bulk actions in the bulk actions toolbar commit fdcc17ca2c33d38a7af505c99d9547e014f5f783 Author: Tobias Regnery Date: Wed Oct 16 11:18:24 2024 +0200 Fix user creation with FullMultipleCompanySupport enabled over API It is currently possible as a non-superuser to create a new user or patch an existing user with arbitrary company over the API if FullMultipleCompanySupport is enabled. Altough a highly unlikely scenario as the user needs permission to create API keys and new users, it is a bug that should get fixed. Add a call to getIdForCurrentUser() to normalize the company_id if FullMultipleCompanySupport is enabled. Signed-off-by: snipe --- .github/workflows/tests-mysql.yml | 2 +- .github/workflows/tests-postgres.yml | 2 +- .github/workflows/tests-sqlite.yml | 2 +- app/Http/Controllers/Api/UsersController.php | 6 + .../Assets/BulkAssetsController.php | 23 ++- .../Transformers/ActionlogsTransformer.php | 4 +- app/Models/CompanyableTrait.php | 3 - composer.json | 1 - composer.lock | 156 +----------------- .../lang/en-US/admin/hardware/message.php | 5 + resources/views/accessories/view.blade.php | 1 - .../views/hardware/bulk-checkout.blade.php | 7 + .../partials/asset-bulk-actions.blade.php | 23 +-- .../BladeComponents/IconComponentTest.php | 20 +++ 14 files changed, 73 insertions(+), 182 deletions(-) create mode 100644 tests/Unit/BladeComponents/IconComponentTest.php diff --git a/.github/workflows/tests-mysql.yml b/.github/workflows/tests-mysql.yml index 737a86dca3e9..310414cda68a 100644 --- a/.github/workflows/tests-mysql.yml +++ b/.github/workflows/tests-mysql.yml @@ -76,4 +76,4 @@ jobs: DB_DATABASE: snipeit DB_PORT: ${{ job.services.mysql.ports[3306] }} DB_USERNAME: root - run: php artisan test --parallel + run: php artisan test diff --git a/.github/workflows/tests-postgres.yml b/.github/workflows/tests-postgres.yml index 0c361511b8a0..ae48277be3e3 100644 --- a/.github/workflows/tests-postgres.yml +++ b/.github/workflows/tests-postgres.yml @@ -74,4 +74,4 @@ jobs: DB_PORT: ${{ job.services.postgresql.ports[5432] }} DB_USERNAME: snipeit DB_PASSWORD: password - run: php artisan test --parallel + run: php artisan test diff --git a/.github/workflows/tests-sqlite.yml b/.github/workflows/tests-sqlite.yml index 49c7c92d8e4a..8bf0115169fb 100644 --- a/.github/workflows/tests-sqlite.yml +++ b/.github/workflows/tests-sqlite.yml @@ -58,4 +58,4 @@ jobs: - name: Execute tests (Unit and Feature tests) via PHPUnit env: DB_CONNECTION: sqlite_testing - run: php artisan test --parallel + run: php artisan test diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index 4714b29ea3e9..a9c8c26f14ca 100644 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -14,6 +14,7 @@ use App\Models\Actionlog; use App\Models\Asset; use App\Models\Accessory; +use App\Models\Company; use App\Models\Consumable; use App\Models\License; use App\Models\User; @@ -371,6 +372,7 @@ public function store(SaveUserRequest $request) : JsonResponse $user = new User; $user->fill($request->all()); + $user->company_id = Company::getIdForCurrentUser($request->input('company_id')); $user->created_by = auth()->id(); if ($request->has('permissions')) { @@ -452,6 +454,10 @@ public function update(SaveUserRequest $request, User $user): JsonResponse $user->fill($request->all()); + if ($request->filled('company_id')) { + $user->company_id = Company::getIdForCurrentUser($request->input('company_id')); + } + if ($user->id == $request->input('manager_id')) { return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot be your own manager')); } diff --git a/app/Http/Controllers/Assets/BulkAssetsController.php b/app/Http/Controllers/Assets/BulkAssetsController.php index 1ce08e65e9af..c27cfe3e0c69 100644 --- a/app/Http/Controllers/Assets/BulkAssetsController.php +++ b/app/Http/Controllers/Assets/BulkAssetsController.php @@ -52,6 +52,10 @@ public function edit(Request $request) : View | RedirectResponse } $asset_ids = $request->input('ids'); + if ($request->input('bulk_actions') === 'checkout') { + $request->session()->flashInput(['selected_assets' => $asset_ids]); + return redirect()->route('hardware.bulkcheckout.show'); + } // Figure out where we need to send the user after the update is complete, and store that in the session $bulk_back_url = request()->headers->get('referer'); @@ -571,31 +575,34 @@ public function storeCheckout(AssetCheckoutRequest $request) : RedirectResponse } $errors = []; - DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, $errors, $asset_ids, $request) { + DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, &$errors, $asset_ids, $request) { //NOTE: $errors is passsed by reference! foreach ($asset_ids as $asset_id) { $asset = Asset::findOrFail($asset_id); $this->authorize('checkout', $asset); - $error = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null); + $checkout_success = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null); + //TODO - I think this logic is duplicated in the checkOut method? if ($target->location_id != '') { $asset->location_id = $target->location_id; - $asset->unsetEventDispatcher(); - $asset->save(); + // TODO - I don't know why this is being saved without events + $asset::withoutEvents(function () use ($asset) { + $asset->save(); + }); } - if ($error) { - array_merge_recursive($errors, $asset->getErrors()->toArray()); + if (!$checkout_success) { + $errors = array_merge_recursive($errors, $asset->getErrors()->toArray()); } } }); if (! $errors) { // Redirect to the new asset page - return redirect()->to('hardware')->with('success', trans('admin/hardware/message.checkout.success')); + return redirect()->to('hardware')->with('success', trans_choice('admin/hardware/message.multi-checkout.success', $asset_ids)); } // Redirect to the asset management page with error - return redirect()->route('hardware.bulkcheckout.show')->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($errors); + return redirect()->route('hardware.bulkcheckout.show')->withInput()->with('error', trans_choice('admin/hardware/message.multi-checkout.error', $asset_ids))->withErrors($errors); } catch (ModelNotFoundException $e) { return redirect()->route('hardware.bulkcheckout.show')->with('error', $e->getErrors()); } diff --git a/app/Http/Transformers/ActionlogsTransformer.php b/app/Http/Transformers/ActionlogsTransformer.php index 49eee4241575..4e6341c8f33f 100644 --- a/app/Http/Transformers/ActionlogsTransformer.php +++ b/app/Http/Transformers/ActionlogsTransformer.php @@ -141,6 +141,8 @@ public function transformActionlog (Actionlog $actionlog, $settings = null) if ($actionlog->item) { if ($actionlog->itemType() == 'asset') { $file_url = route('show/assetfile', ['assetId' => $actionlog->item->id, 'fileId' => $actionlog->id]); + } elseif ($actionlog->itemType() == 'accessory') { + $file_url = route('show.accessoryfile', ['accessoryId' => $actionlog->item->id, 'fileId' => $actionlog->id]); } elseif ($actionlog->itemType() == 'license') { $file_url = route('show.licensefile', ['licenseId' => $actionlog->item->id, 'fileId' => $actionlog->id]); } elseif ($actionlog->itemType() == 'user') { @@ -345,4 +347,4 @@ public function changedInfo(array $clean_meta) -} \ No newline at end of file +} diff --git a/app/Models/CompanyableTrait.php b/app/Models/CompanyableTrait.php index df67f2be4fa4..04a620d8e3d5 100644 --- a/app/Models/CompanyableTrait.php +++ b/app/Models/CompanyableTrait.php @@ -8,9 +8,6 @@ trait CompanyableTrait * This trait is used to scope models to the current company. To use this scope on companyable models, * we use the "use Companyable;" statement at the top of the mode. * - * We CANNOT USE THIS ON USERS, as it causes an infinite loop and prevents users from logging in, since this scope will be - * applied to the currently logged in (or logging in) user in addition to the user model for viewing lists of users. - * * @see \App\Models\Company\Company::scopeCompanyables() * @return void */ diff --git a/composer.json b/composer.json index 6d893125787c..d3637c3a4b13 100644 --- a/composer.json +++ b/composer.json @@ -74,7 +74,6 @@ "ext-exif": "*" }, "require-dev": { - "brianium/paratest": "^7.0", "fakerphp/faker": "^1.16", "larastan/larastan": "^2.9", "mockery/mockery": "^1.4", diff --git a/composer.lock b/composer.lock index 3f79921b26df..0631fc275e29 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3819ab4ef72eb77fabe494c0e746b83b", + "content-hash": "5341bc5be02b3c33e28e46e06dd99f29", "packages": [ { "name": "alek13/slack", @@ -11790,101 +11790,6 @@ ], "time": "2024-04-13T18:00:56+00:00" }, - { - "name": "brianium/paratest", - "version": "v7.3.1", - "source": { - "type": "git", - "url": "https://github.com/paratestphp/paratest.git", - "reference": "551f46f52a93177d873f3be08a1649ae886b4a30" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/551f46f52a93177d873f3be08a1649ae886b4a30", - "reference": "551f46f52a93177d873f3be08a1649ae886b4a30", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-simplexml": "*", - "fidry/cpu-core-counter": "^0.5.1 || ^1.0.0", - "jean85/pretty-package-versions": "^2.0.5", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", - "phpunit/php-code-coverage": "^10.1.7", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-timer": "^6.0", - "phpunit/phpunit": "^10.4.2", - "sebastian/environment": "^6.0.1", - "symfony/console": "^6.3.4 || ^7.0.0", - "symfony/process": "^6.3.4 || ^7.0.0" - }, - "require-dev": { - "doctrine/coding-standard": "^12.0.0", - "ext-pcov": "*", - "ext-posix": "*", - "infection/infection": "^0.27.6", - "phpstan/phpstan": "^1.10.40", - "phpstan/phpstan-deprecation-rules": "^1.1.4", - "phpstan/phpstan-phpunit": "^1.3.15", - "phpstan/phpstan-strict-rules": "^1.5.2", - "squizlabs/php_codesniffer": "^3.7.2", - "symfony/filesystem": "^6.3.1 || ^7.0.0" - }, - "bin": [ - "bin/paratest", - "bin/paratest.bat", - "bin/paratest_for_phpstorm" - ], - "type": "library", - "autoload": { - "psr-4": { - "ParaTest\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Brian Scaturro", - "email": "scaturrob@gmail.com", - "role": "Developer" - }, - { - "name": "Filippo Tessarotto", - "email": "zoeslam@gmail.com", - "role": "Developer" - } - ], - "description": "Parallel testing for PHP", - "homepage": "https://github.com/paratestphp/paratest", - "keywords": [ - "concurrent", - "parallel", - "phpunit", - "testing" - ], - "support": { - "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.3.1" - }, - "funding": [ - { - "url": "https://github.com/sponsors/Slamdunk", - "type": "github" - }, - { - "url": "https://paypal.me/filippotessarotto", - "type": "paypal" - } - ], - "time": "2023-10-31T09:24:17+00:00" - }, { "name": "clue/ndjson-react", "version": "v1.3.0", @@ -12781,65 +12686,6 @@ }, "time": "2020-07-09T08:09:16+00:00" }, - { - "name": "jean85/pretty-package-versions", - "version": "2.0.6", - "source": { - "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2.0.0", - "php": "^7.1|^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.2", - "jean85/composer-provided-replaced-stub-package": "^1.0", - "phpstan/phpstan": "^1.4", - "phpunit/phpunit": "^7.5|^8.5|^9.4", - "vimeo/psalm": "^4.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Jean85\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" - } - ], - "description": "A library to get pretty versions strings of installed dependencies", - "keywords": [ - "composer", - "package", - "release", - "versions" - ], - "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6" - }, - "time": "2024-03-08T09:58:59+00:00" - }, { "name": "justinrainbow/json-schema", "version": "5.3.0", diff --git a/resources/lang/en-US/admin/hardware/message.php b/resources/lang/en-US/admin/hardware/message.php index 041d32f56c90..27877e02f9fc 100644 --- a/resources/lang/en-US/admin/hardware/message.php +++ b/resources/lang/en-US/admin/hardware/message.php @@ -79,6 +79,11 @@ 'no_assets_selected' => 'You must select at least one asset from the list', ], + 'multi-checkout' => [ + 'error' => 'Asset was not checked out, please try again|Assets were not checked out, please try again', + 'success' => 'Asset checked out successfully.|Assets checked out successfully.', + ], + 'checkin' => [ 'error' => 'Asset was not checked in, please try again', 'success' => 'Asset checked in successfully.', diff --git a/resources/views/accessories/view.blade.php b/resources/views/accessories/view.blade.php index e3f98bf61a85..4dc8802e8da2 100644 --- a/resources/views/accessories/view.blade.php +++ b/resources/views/accessories/view.blade.php @@ -161,7 +161,6 @@ class="table table-striped snipe-table" showfile_routename="show.accessoryfile" deletefile_routename="delete/accessoryfile" :object="$accessory" /> - diff --git a/resources/views/hardware/bulk-checkout.blade.php b/resources/views/hardware/bulk-checkout.blade.php index 405e5e47cd26..39e2cdf10cbf 100644 --- a/resources/views/hardware/bulk-checkout.blade.php +++ b/resources/views/hardware/bulk-checkout.blade.php @@ -115,5 +115,12 @@ @section('moar_scripts') @include('partials/assets-assigned') + @stop diff --git a/resources/views/partials/asset-bulk-actions.blade.php b/resources/views/partials/asset-bulk-actions.blade.php index b597ad647f78..992fb52bba10 100644 --- a/resources/views/partials/asset-bulk-actions.blade.php +++ b/resources/views/partials/asset-bulk-actions.blade.php @@ -16,17 +16,20 @@ diff --git a/tests/Unit/BladeComponents/IconComponentTest.php b/tests/Unit/BladeComponents/IconComponentTest.php new file mode 100644 index 000000000000..b418b38384ff --- /dev/null +++ b/tests/Unit/BladeComponents/IconComponentTest.php @@ -0,0 +1,20 @@ + 'checkout'])->render(); + + $this->assertFalse( + Str::endsWith($renderedTemplateString, PHP_EOL), + 'Newline found at end of icon component. Bootstrap tables will not render if there is a newline at the end of the file.' + ); + } +}