diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index e340d70b097c..fb7554fcd7af 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -61,14 +61,12 @@ public function report(Throwable $exception) public function render($request, Throwable $e) { - // CSRF token mismatch error if ($e instanceof \Illuminate\Session\TokenMismatchException) { return redirect()->back()->with('error', trans('general.token_expired')); } // Invalid JSON exception - // TODO: don't understand why we have to do this when we have the invalidJson() method, below, but, well, whatever if ($e instanceof JsonException) { return response()->json(Helper::formatStandardApiResponse('error', null, 'Invalid JSON'), 422); } @@ -88,8 +86,9 @@ public function render($request, Throwable $e) return redirect()->back()->withInput()->with('error', trans('validation.date', ['attribute' => 'date'])); } + // Handle API requests that fail - if ($request->ajax() || $request->wantsJson()) { + if ($request->ajax() || $request->wantsJson() || ($request->route() && $request->route()->named('api.files.*'))) { // Handle API requests that fail because Carbon cannot parse the date on validation (when a submitted date value is definitely not a date) if ($e instanceof InvalidFormatException) { @@ -102,6 +101,7 @@ public function render($request, Throwable $e) return response()->json(Helper::formatStandardApiResponse('error', null, $className . ' not found'), 200); } + // Handle API requests that fail because of an HTTP status code and return a useful error message if ($this->isHttpException($e)) { @@ -116,12 +116,24 @@ public function render($request, Throwable $e) return response()->json(Helper::formatStandardApiResponse('error', null, 'Method not allowed'), 405); default: return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode), $statusCode); - } } + + // This handles API validation exceptions that happen at the Form Request level, so they + // never even get to the controller where we normally nicely format JSON responses + if ($e instanceof ValidationException) { + $response = $this->invalidJson($request, $e); + return response()->json(Helper::formatStandardApiResponse('error', null, $e->errors()), 200); + } + + // return response()->json(Helper::formatStandardApiResponse('error', null, 'Undefined exception'), 200); + + + } + // This is traaaaash but it handles models that are not found while using route model binding :( // The only alternative is to set that at *each* route, which is crazypants if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) { diff --git a/app/Helpers/StorageHelper.php b/app/Helpers/StorageHelper.php index 47700f913ac5..3b345797edd2 100644 --- a/app/Helpers/StorageHelper.php +++ b/app/Helpers/StorageHelper.php @@ -48,17 +48,29 @@ public static function allowSafeInline($file_with_path) { 'avif', 'webp', 'png', + 'gif', ]; - // 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; } + public static function getFiletype($file_with_path) { + + // The file exists and is allowed to be displayed inline + if (Storage::exists($file_with_path)) { + return pathinfo($file_with_path, PATHINFO_EXTENSION); + } + + return null; + + } + /** * Decide whether to show the file inline or download it. */ diff --git a/app/Http/Controllers/Api/AssetFilesController.php b/app/Http/Controllers/Api/AssetFilesController.php deleted file mode 100644 index fabe9ebbb3cc..000000000000 --- a/app/Http/Controllers/Api/AssetFilesController.php +++ /dev/null @@ -1,200 +0,0 @@ - - * - * @version v1.0 - * @author [T. Scarsbrook] [] - */ -class AssetFilesController extends Controller -{ - /** - * Accepts a POST to upload a file to the server. - * - * @param \App\Http\Requests\UploadFileRequest $request - * @param int $assetId - * @since [v6.0] - * @author [T. Scarsbrook] [] - */ - public function store(UploadFileRequest $request, $assetId = null) : JsonResponse - { - // Start by checking if the asset being acted upon exists - if (! $asset = Asset::find($assetId)) { - return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404); - } - - // 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/assets')) { - Storage::makeDirectory('private_uploads/assets', 775); - } - - // Loop over the attached files and add them to the asset - foreach ($request->file('file') as $file) { - $file_name = $request->handleFile('private_uploads/assets/','hardware-'.$asset->id, $file); - - $asset->logUpload($file_name, e($request->get('notes'))); - } - - // All done - report success - return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/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/hardware/message.upload.nofiles')), 500); - } - - /** - * List the files for an asset. - * - * @param int $assetId - * @since [v6.0] - * @author [T. Scarsbrook] [] - */ - public function list(Asset $asset, Request $request) : JsonResponse | array - { - - $this->authorize('view', $asset); - - $allowed_columns = - [ - 'id', - 'filename', - 'eol', - 'notes', - 'created_at', - 'updated_at', - ]; - - $files = Actionlog::select('action_logs.*')->where('action_type', '=', 'uploaded')->where('item_type', '=', Asset::class)->where('item_id', '=', $asset->id); - - if ($request->filled('search')) { - $files = $files->TextSearch($request->input('search')); - } - - // Make sure the offset and limit are actually integers and do not exceed system limits - $offset = ($request->input('offset') > $files->count()) ? $files->count() : abs($request->input('offset')); - $limit = app('api_limit_value'); - $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; - $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at'; - $files = $files->orderBy($sort, $order); - - $files = $files->skip($offset)->take($limit)->get(); - return (new UploadedFilesTransformer())->transformFiles($files, $files->count()); - - } - - /** - * Check for permissions and display the file. - * - * @param int $assetId - * @param int $fileId - * @return \Illuminate\Http\JsonResponse - * @throws \Illuminate\Auth\Access\AuthorizationException - * @since [v6.0] - * @author [T. Scarsbrook] [] - */ - public function show(Asset $asset, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse - { - - // the asset is valid - if (isset($asset->id)) { - $this->authorize('view', $asset); - - // Check that the file being requested exists for the asset - if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) { - return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.no_match', ['id' => $fileId])), 404); - } - - // Form the full filename with path - $file = 'private_uploads/assets/'.$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/hardware/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/hardware/message.download.error', ['id' => $fileId])), 500); - } - - /** - * Delete the associated file - * - * @param int $assetId - * @param int $fileId - * @since [v6.0] - * @author [T. Scarsbrook] [] - */ - public function destroy(Asset $asset, $fileId = null) : JsonResponse - { - - $rel_path = 'private_uploads/assets'; - - // the asset is valid - if (isset($asset->id)) { - $this->authorize('update', $asset); - - // 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/hardware/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/hardware/message.deletefile.error')), 500); - } - - return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.deletefile.error')), 500); - } -} diff --git a/app/Http/Controllers/Api/AssetModelFilesController.php b/app/Http/Controllers/Api/AssetModelFilesController.php deleted file mode 100644 index d0a4747bf402..000000000000 --- a/app/Http/Controllers/Api/AssetModelFilesController.php +++ /dev/null @@ -1,184 +0,0 @@ - - * - * @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', $assetModel); - - 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-'.$assetModel->id, $file); - - $assetModel->logUpload($file_name, e($request->get('notes'))); - } - - // All done - report 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 - return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.upload.nofiles')), 500); - } - - /** - * List the files for an asset. - * - * @param int $assetmodel - * @since [v7.0.12] - * @author [r-xyz] - */ - public function list($assetmodel_id) : JsonResponse | array - { - // Start by checking if the asset being acted upon exists - if (! $assetModel = AssetModel::find($assetmodel_id)) { - return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404); - } - - $assetmodel = AssetModel::with('uploads')->find($assetmodel_id); - $this->authorize('view', $assetmodel); - return (new AssetModelsTransformer)->transformAssetModelFiles($assetmodel, $assetmodel->uploads()->count()); - } - - /** - * 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/app/Http/Controllers/Api/UploadedFilesController.php b/app/Http/Controllers/Api/UploadedFilesController.php new file mode 100644 index 000000000000..88399592a7b8 --- /dev/null +++ b/app/Http/Controllers/Api/UploadedFilesController.php @@ -0,0 +1,242 @@ + Accessory::class, + 'assets' => Asset::class, + 'components' => Component::class, + 'consumables' => Consumable::class, + 'locations' => Location::class, + 'models' => AssetModel::class, + 'users' => User::class, + ]; + + static $map_storage_path = [ + 'accessories' => 'private_uploads/accessories/', + 'assets' => 'private_uploads/assets/', + 'components' => 'private_uploads/components/', + 'consumables' => 'private_uploads/consumables/', + 'locations' => 'private_uploads/locations/', + 'models' => 'private_uploads/assetmodels/', + 'users' => 'private_uploads/users/', + ]; + + static $map_file_prefix= [ + 'accessories' => 'accessory', + 'assets' => 'asset', + 'components' => 'component', + 'consumables' => 'consumable', + 'locations' => 'location', + 'models' => 'model', + 'users' => 'user', + ]; + + + + + /** + * List the files for an object. + * + * @since [v7.0.12] + * @author [r-xyz] + */ + public function index(Request $request, $object_type, $id) : JsonResponse | array + { + + $object = self::$map_object_type[$object_type]::find($id); + $this->authorize('view', $object); + + if (!$object) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object'))); + } + + // Columns allowed for sorting + $allowed_columns = + [ + 'id', + 'filename', + 'action_type', + 'note', + 'created_at', + ]; + + $uploads = $object->uploads(); + $offset = ($request->input('offset') > $object->count()) ? $object->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); + $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; + $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'action_logs.created_at'; + + // Text search on action_logs fields + // We could use the normal Actionlogs text scope, but it's a very heavy query since it's searcghing across all relations + // And we generally won't need that here + if ($request->filled('search')) { + + $uploads->where(function ($query) use ($request) { + $query->where('filename', 'LIKE', '%' . $request->input('search') . '%') + ->orWhere('note', 'LIKE', '%' . $request->input('search') . '%'); + }); + } + + $uploads = $uploads->skip($offset)->take($limit)->orderBy($sort, $order)->get(); + return (new UploadedFilesTransformer())->transformFiles($uploads, $uploads->count()); + } + + + /** + * 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, $object_type, $id) : JsonResponse + { + + $object = self::$map_object_type[$object_type]::find($id); + $this->authorize('view', $object); + + if (!$object) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object'))); + } + + // If the file storage directory doesn't exist, create it + if (! Storage::exists(self::$map_storage_path[$object_type])) { + Storage::makeDirectory(self::$map_storage_path[$object_type], 775); + } + + + if ($request->hasFile('file')) { + // Loop over the attached files and add them to the object + foreach ($request->file('file') as $file) { + $file_name = $request->handleFile(self::$map_storage_path[$object_type],self::$map_file_prefix[$object_type].'-'.$object->id, $file); + $files[] = $file_name; + $object->logUpload($file_name, $request->get('notes')); + } + + $files = Actionlog::select('action_logs.*')->where('action_type', '=', 'uploaded') + ->where('item_type', '=', self::$map_object_type[$object_type]) + ->where('item_id', '=', $id)->whereIn('filename', $files) + ->get(); + + return response()->json(Helper::formatStandardApiResponse('success', (new UploadedFilesTransformer())->transformFiles($files, count($files)), trans_choice('general.file_upload_status.upload.success', count($files)))); + } + + // No files were submitted + return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.nofiles'))); + } + + + + /** + * Check for permissions and display the file. + * + * @param AssetModel $model + * @param int $fileId + * @return \Illuminate\Http\JsonResponse + * @throws \Illuminate\Auth\Access\AuthorizationException + * @since [v7.0.12] + * @author [r-xyz] + */ + public function show($object_type, $id, $file_id) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse + { + $object = self::$map_object_type[$object_type]::find($id); + $this->authorize('view', $object); + + if (!$object) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object'))); + } + + + // Check that the file being requested exists for the asset + if (! $log = Actionlog::whereNotNull('filename') + ->where('item_type', AssetModel::class) + ->where('item_id', $object->id)->find($file_id)) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_id')), 404); + } + + + if (! Storage::exists(self::$map_storage_path[$object_type].'/'.$log->filename)) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.file_not_found'), 200)); + } + + if (request('inline') == 'true') { + $headers = [ + 'Content-Disposition' => 'inline', + ]; + return Storage::download(self::$map_storage_path[$object_type].'/'.$log->filename, $log->filename, $headers); + } + + return StorageHelper::downloader(self::$map_storage_path[$object_type].'/'.$log->filename); + + } + + /** + * Delete the associated file + * + * @param AssetModel $model + * @param int $fileId + * @since [v7.0.12] + * @author [r-xyz] + */ + public function destroy($object_type, $id, $file_id) : JsonResponse + { + \Log::error('destroy called for '.$object_type.' with id '.$id.' and file_id '.$file_id); + + $object = self::$map_object_type[$object_type]::find($id); + $this->authorize('update', self::$map_object_type[$object_type]); + + if (!$object) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object'))); + } + + + // Check for the file + $log = Actionlog::find($file_id)->where('item_type', self::$map_object_type[$object_type]) + ->where('item_id', $object->id)->first(); + + if ($log) { + // Check the file actually exists, and delete it + if (Storage::exists(self::$map_storage_path[$object_type].'/'.$log->filename)) { + Storage::delete(self::$map_storage_path[$object_type].'/'.$log->filename); + } + // Delete the record of the file + if ($log->delete()) { + return response()->json(Helper::formatStandardApiResponse('success', null, trans_choice('general.file_upload_status.delete.success', 1)), 200); + } + + + } + + // The file doesn't seem to really exist, so report an error + return response()->json(Helper::formatStandardApiResponse('error', null, trans_choice('general.file_upload_status.delete.error', 1)), 500); + + } +} diff --git a/app/Http/Controllers/Assets/AssetFilesController.php b/app/Http/Controllers/Assets/AssetFilesController.php index cf119edddc8c..125f6cd8d24e 100644 --- a/app/Http/Controllers/Assets/AssetFilesController.php +++ b/app/Http/Controllers/Assets/AssetFilesController.php @@ -45,7 +45,7 @@ public function store(UploadFileRequest $request, Asset $asset) : RedirectRespon return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.upload.success')); } - return redirect()->back()->with('error', trans('admin/hardware/message.upload.nofiles')); + return redirect()->back()->with('error', trans('general.file_upload_status.nofiles')); } /** diff --git a/app/Http/Controllers/Users/UserFilesController.php b/app/Http/Controllers/Users/UserFilesController.php index 45bd0c6329d8..a3438678496c 100644 --- a/app/Http/Controllers/Users/UserFilesController.php +++ b/app/Http/Controllers/Users/UserFilesController.php @@ -25,32 +25,25 @@ class UserFilesController extends Controller public function store(UploadFileRequest $request, User $user) { $this->authorize('update', $user); - $files = $request->file('file'); - if (is_null($files)) { - return redirect()->back()->with('error', trans('admin/users/message.upload.nofiles')); - } - foreach ($files as $file) { - $file_name = $request->handleFile('private_uploads/users/', 'user-'.$user->id, $file); - - //Log the uploaded file to the log - $logAction = new Actionlog(); - $logAction->item_id = $user->id; - $logAction->item_type = User::class; - $logAction->created_by = auth()->id(); - $logAction->note = $request->input('notes'); - $logAction->target_id = null; - $logAction->created_at = date("Y-m-d H:i:s"); - $logAction->filename = $file_name; - $logAction->action_type = 'uploaded'; - - if (! $logAction->save()) { - return JsonResponse::create(['error' => 'Failed validation: '.print_r($logAction->getErrors(), true)], 500); + if ($request->hasFile('file')) { + + if (! Storage::exists('private_uploads/users')) { + Storage::makeDirectory('private_uploads/users', 775); } - return redirect()->back()->withFragment('files')->with('success', trans('admin/users/message.upload.success')); + $file_count = 0; + foreach ($request->file('file') as $file) { + $file_name = $request->handleFile('private_uploads/users/','hardware-'.$user->id, $file); + $user->logUpload($file_name, $request->get('notes')); + $file_count++; + } + + return redirect()->back()->withFragment('files')->with('success', trans_choice('general.file_upload_status.upload.success', $file_count)); } + return redirect()->back()->with('error', trans('general.file_upload_status.nofiles')); + } diff --git a/app/Http/Requests/UploadFileRequest.php b/app/Http/Requests/UploadFileRequest.php index e58f1a1be37d..3ea358e3a83a 100644 --- a/app/Http/Requests/UploadFileRequest.php +++ b/app/Http/Requests/UploadFileRequest.php @@ -6,6 +6,7 @@ use enshrined\svgSanitize\Sanitizer; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Log; +use \App\Helpers\Helper; class UploadFileRequest extends Request { @@ -27,7 +28,7 @@ public function authorize() */ public function rules() { - $max_file_size = \App\Helpers\Helper::file_upload_max_size(); + $max_file_size = Helper::file_upload_max_size(); return [ 'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,xlsx,lic,xml,rtf,json,webp,avif|max:'.$max_file_size, @@ -37,34 +38,52 @@ public function rules() /** * Sanitizes (if needed) and Saves a file to the appropriate location * Returns the 'short' (storage-relative) filename - * - * TODO - this has a lot of similarities to UploadImageRequest's handleImage; is there - * a way to merge them or extend one into the other? */ public function handleFile(string $dirname, string $name_prefix, $file): string { - $extension = $file->getClientOriginalExtension(); - $file_name = $name_prefix.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$file->guessExtension(); + + $file_name = $name_prefix.'-'.str_random(8).'-'.str_replace(' ', '-', $file->getClientOriginalName()); // Check for SVG and sanitize it if ($file->getMimeType() === 'image/svg+xml') { - Log::debug('This is an SVG'); - Log::debug($file_name); - - $sanitizer = new Sanitizer(); - $dirtySVG = file_get_contents($file->getRealPath()); - $cleanSVG = $sanitizer->sanitize($dirtySVG); - - try { - Storage::put($dirname.$file_name, $cleanSVG); - } catch (\Exception $e) { - Log::debug('Upload no workie :( '); - Log::debug($e); - } - + $uploaded_file = $this->handleSVG($file); } else { - $put_results = Storage::put($dirname.$file_name, file_get_contents($file)); + $uploaded_file = file_get_contents($file); + } + + try { + Storage::put($dirname.$file_name, $uploaded_file); + } catch (\Exception $e) { + Log::debug($e); } + return $file_name; } + + public function handleSVG($file) { + $sanitizer = new Sanitizer(); + $dirtySVG = file_get_contents($file->getRealPath()); + return $sanitizer->sanitize($dirtySVG); + } + + + /** + * Get the validation error messages that apply to the request, but + * replace the attribute name with the name of the file that was attempted and failed + * to make it clearer to the user which file is the bad one. + * @return array + */ + public function attributes(): array + { + $attributes = []; + + if ($this->file) { + for ($i = 0; $i < count($this->file); $i++) { + $attributes['file.'.$i] = $this->file[$i]->getClientOriginalName(); + } + } + + return $attributes; + + } } diff --git a/app/Http/Transformers/DatatablesTransformer.php b/app/Http/Transformers/DatatablesTransformer.php index 0e69109391fc..8b61c6cc4871 100644 --- a/app/Http/Transformers/DatatablesTransformer.php +++ b/app/Http/Transformers/DatatablesTransformer.php @@ -4,6 +4,10 @@ class DatatablesTransformer { + + /** + * Transform data for bootstrap tables and API responses for lists of things + **/ public function transformDatatables($objects, $total = null) { (isset($total)) ? $objects_array['total'] = $total : $objects_array['total'] = count($objects); @@ -11,4 +15,15 @@ public function transformDatatables($objects, $total = null) return $objects_array; } + + /** + * Transform data for returning the status of items within a bulk action + **/ + public function transformBulkResponseWithStatusAndObjects($objects, $total) + { + (isset($total)) ? $objects_array['total'] = $total : $objects_array['total'] = count($objects); + $objects_array['rows'] = $objects; + + return $objects_array; + } } diff --git a/app/Http/Transformers/UploadedFilesTransformer.php b/app/Http/Transformers/UploadedFilesTransformer.php index a18c9f9b65e5..ee91d7097420 100644 --- a/app/Http/Transformers/UploadedFilesTransformer.php +++ b/app/Http/Transformers/UploadedFilesTransformer.php @@ -3,10 +3,10 @@ namespace App\Http\Transformers; use App\Helpers\Helper; +use App\Helpers\StorageHelper; use App\Models\Actionlog; -use App\Models\Asset; -use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; +use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Storage; class UploadedFilesTransformer @@ -26,23 +26,26 @@ public function transformFile(Actionlog $file) { $snipeModel = $file->item_type; - - // This will be used later as we extend out this transformer to handle more types of uploads - if ($file->item_type == Asset::class) { - $file_url = route('show/assetfile', [$file->item_id, $file->id]); - } - $array = [ 'id' => (int) $file->id, + 'icon' => Helper::filetype_icon($file->filename), + 'name' => e($file->filename), + 'item' => ($file->item_type) ? [ + 'id' => (int) $file->item_id, + 'type' => strtolower(class_basename($file->item_type)), + ] : null, 'filename' => e($file->filename), - 'url' => $file_url, + 'filetype' => StorageHelper::getFiletype($file->uploads_file_path()), + 'url' => $file->uploads_file_url(), + 'note' => ($file->note) ? e($file->note) : null, 'created_by' => ($file->adminuser) ? [ 'id' => (int) $file->adminuser->id, 'name'=> e($file->adminuser->present()->fullName), ] : null, 'created_at' => Helper::getFormattedDateObject($file->created_at, 'datetime'), - 'updated_at' => Helper::getFormattedDateObject($file->updated_at, 'datetime'), 'deleted_at' => Helper::getFormattedDateObject($file->deleted_at, 'datetime'), + 'inline' => StorageHelper::allowSafeInline($file->uploads_file_path()), + 'exists_on_disk' => (Storage::exists($file->uploads_file_path()) ? true : false), ]; $permissions_array['available_actions'] = [ @@ -53,4 +56,5 @@ public function transformFile(Actionlog $file) return $array; } + } diff --git a/app/Models/Actionlog.php b/app/Models/Actionlog.php index dd86ae25c68b..a0ddd1687689 100755 --- a/app/Models/Actionlog.php +++ b/app/Models/Actionlog.php @@ -444,6 +444,62 @@ public function determineActionSource(): string } + public function uploads_file_url() + { + + switch ($this->item_type) { + case Accessory::class: + return route('show/accessoryfile', [$this->item_id, $this->id]); + case Asset::class: + return route('show/assetfile', [$this->item_id, $this->id]); + case AssetModel::class: + return route('show/modelfile', [$this->item_id, $this->id]); + case Consumable::class: + return route('show/locationsfile', [$this->item_id, $this->id]); + case Component::class: + return route('show/componentsfile', [$this->item_id, $this->id]); + case License::class: + return route('show/licensesfile', [$this->item_id, $this->id]); + case Location::class: + return route('show/locationsfile', [$this->item_id, $this->id]); + case User::class: + return route('show/userfile', [$this->item_id, $this->id]); + default: + return null; + } + } + + public function uploads_file_path() + { + + switch ($this->item_type) { + case Accessory::class: + return 'private_uploads/accessories/'.$this->filename; + case Asset::class: + return 'private_uploads/assets/'.$this->filename; + case AssetModel::class: + return 'private_uploads/assetmodels/'.$this->filename; + case Consumable::class: + return 'private_uploads/consumables/'.$this->filename; + case Component::class: + return 'private_uploads/components/'.$this->filename; + case License::class: + return 'private_uploads/licenses/'.$this->filename; + case Location::class: + return 'private_uploads/locations/'.$this->filename; + case User::class: + return 'private_uploads/users/'.$this->filename; + default: + return null; + } + } + + + + + + + // Manually sets $this->source for determineActionSource() public function setActionSource($source = null): void { diff --git a/app/Models/User.php b/app/Models/User.php index 9b211e6a045e..34a73e039d1c 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -34,6 +34,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo use Notifiable; use Presentable; use Searchable; + use Loggable; protected $hidden = ['password', 'remember_token', 'permissions', 'reset_password_code', 'persist_code']; protected $table = 'users'; diff --git a/app/Presenters/UploadsPresenter.php b/app/Presenters/UploadsPresenter.php new file mode 100644 index 000000000000..37f1c95f48a4 --- /dev/null +++ b/app/Presenters/UploadsPresenter.php @@ -0,0 +1,101 @@ + 'id', + 'searchable' => false, + 'sortable' => true, + 'switchable' => true, + 'title' => trans('general.id'), + 'visible' => false, + ], + [ + 'field' => 'icon', + 'searchable' => false, + 'sortable' => false, + 'switchable' => false, + 'title' => trans('general.type'), + 'formatter' => 'iconFormatter', + ], + [ + 'field' => 'image', + 'searchable' => false, + 'sortable' => false, + 'switchable' => true, + 'title' => trans('general.image'), + 'formatter' => 'inlineImageFormatter', + ], + [ + 'field' => 'filename', + 'searchable' => false, + 'sortable' => false, + 'switchable' => true, + 'title' => trans('general.file_name'), + 'visible' => true, + 'formatter' => 'fileUploadNameFormatter', + ], + [ + 'field' => 'download', + 'searchable' => false, + 'sortable' => false, + 'switchable' => true, + 'title' => trans('general.download'), + 'visible' => true, + 'formatter' => 'downloadOrOpenInNewWindowFormatter', + ], + [ + 'field' => 'note', + 'searchable' => true, + 'sortable' => true, + 'switchable' => true, + 'title' => trans('general.notes'), + 'visible' => true, + ], + [ + 'field' => 'created_by', + 'searchable' => false, + 'sortable' => true, + 'title' => trans('general.created_by'), + 'visible' => false, + 'formatter' => 'usersLinkObjFormatter', + ], + [ + 'field' => 'created_at', + 'searchable' => true, + 'sortable' => true, + 'switchable' => true, + 'title' => trans('general.created_at'), + 'visible' => false, + 'formatter' => 'dateDisplayFormatter', + ], [ + 'field' => 'available_actions', + 'searchable' => false, + 'sortable' => false, + 'switchable' => false, + 'title' => trans('table.actions'), + 'formatter' => 'deleteUploadFormatter', + ], + ]; + + return json_encode($layout); + } + +} diff --git a/public/js/dist/bootstrap-table.js b/public/js/dist/bootstrap-table.js index cdad7cf2c0a3..ddcdd7e7aa74 100644 --- a/public/js/dist/bootstrap-table.js +++ b/public/js/dist/bootstrap-table.js @@ -29125,6 +29125,2583 @@ })); +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery')) : + typeof define === 'function' && define.amd ? define(['jquery'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.jQuery)); +})(this, (function ($) { 'use strict'; + + function _assertThisInitialized(e) { + if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return e; + } + function _callSuper(t, o, e) { + return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); + } + function _classCallCheck(a, n) { + if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); + } + function _defineProperties(e, r) { + for (var t = 0; t < r.length; t++) { + var o = r[t]; + o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); + } + } + function _createClass(e, r, t) { + return _defineProperties(e.prototype, r), Object.defineProperty(e, "prototype", { + writable: !1 + }), e; + } + function _get() { + return _get = "undefined" != typeof Reflect && Reflect.get ? Reflect.get.bind() : function (e, t, r) { + var p = _superPropBase(e, t); + if (p) { + var n = Object.getOwnPropertyDescriptor(p, t); + return n.get ? n.get.call(arguments.length < 3 ? e : r) : n.value; + } + }, _get.apply(null, arguments); + } + function _getPrototypeOf(t) { + return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { + return t.__proto__ || Object.getPrototypeOf(t); + }, _getPrototypeOf(t); + } + function _inherits(t, e) { + if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); + t.prototype = Object.create(e && e.prototype, { + constructor: { + value: t, + writable: !0, + configurable: !0 + } + }), Object.defineProperty(t, "prototype", { + writable: !1 + }), e && _setPrototypeOf(t, e); + } + function _isNativeReflectConstruct() { + try { + var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); + } catch (t) {} + return (_isNativeReflectConstruct = function () { + return !!t; + })(); + } + function _possibleConstructorReturn(t, e) { + if (e && ("object" == typeof e || "function" == typeof e)) return e; + if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); + return _assertThisInitialized(t); + } + function _setPrototypeOf(t, e) { + return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { + return t.__proto__ = e, t; + }, _setPrototypeOf(t, e); + } + function _superPropBase(t, o) { + for (; !{}.hasOwnProperty.call(t, o) && null !== (t = _getPrototypeOf(t));); + return t; + } + function _superPropGet(t, o, e, r) { + var p = _get(_getPrototypeOf(t.prototype ), o, e); + return "function" == typeof p ? function (t) { + return p.apply(e, t); + } : p; + } + function _toPrimitive(t, r) { + if ("object" != typeof t || !t) return t; + var e = t[Symbol.toPrimitive]; + if (void 0 !== e) { + var i = e.call(t, r); + if ("object" != typeof i) return i; + throw new TypeError("@@toPrimitive must return a primitive value."); + } + return (String )(t); + } + function _toPropertyKey(t) { + var i = _toPrimitive(t, "string"); + return "symbol" == typeof i ? i : i + ""; + } + + var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + + var es_array_concat = {}; + + var globalThis_1; + var hasRequiredGlobalThis; + + function requireGlobalThis () { + if (hasRequiredGlobalThis) return globalThis_1; + hasRequiredGlobalThis = 1; + var check = function (it) { + return it && it.Math === Math && it; + }; + + // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 + globalThis_1 = + // eslint-disable-next-line es/no-global-this -- safe + check(typeof globalThis == 'object' && globalThis) || + check(typeof window == 'object' && window) || + // eslint-disable-next-line no-restricted-globals -- safe + check(typeof self == 'object' && self) || + check(typeof commonjsGlobal == 'object' && commonjsGlobal) || + check(typeof globalThis_1 == 'object' && globalThis_1) || + // eslint-disable-next-line no-new-func -- fallback + (function () { return this; })() || Function('return this')(); + return globalThis_1; + } + + var objectGetOwnPropertyDescriptor = {}; + + var fails; + var hasRequiredFails; + + function requireFails () { + if (hasRequiredFails) return fails; + hasRequiredFails = 1; + fails = function (exec) { + try { + return !!exec(); + } catch (error) { + return true; + } + }; + return fails; + } + + var descriptors; + var hasRequiredDescriptors; + + function requireDescriptors () { + if (hasRequiredDescriptors) return descriptors; + hasRequiredDescriptors = 1; + var fails = requireFails(); + + // Detect IE8's incomplete defineProperty implementation + descriptors = !fails(function () { + // eslint-disable-next-line es/no-object-defineproperty -- required for testing + return Object.defineProperty({}, 1, { get: function () { return 7; } })[1] !== 7; + }); + return descriptors; + } + + var functionBindNative; + var hasRequiredFunctionBindNative; + + function requireFunctionBindNative () { + if (hasRequiredFunctionBindNative) return functionBindNative; + hasRequiredFunctionBindNative = 1; + var fails = requireFails(); + + functionBindNative = !fails(function () { + // eslint-disable-next-line es/no-function-prototype-bind -- safe + var test = (function () { /* empty */ }).bind(); + // eslint-disable-next-line no-prototype-builtins -- safe + return typeof test != 'function' || test.hasOwnProperty('prototype'); + }); + return functionBindNative; + } + + var functionCall; + var hasRequiredFunctionCall; + + function requireFunctionCall () { + if (hasRequiredFunctionCall) return functionCall; + hasRequiredFunctionCall = 1; + var NATIVE_BIND = requireFunctionBindNative(); + + var call = Function.prototype.call; + + functionCall = NATIVE_BIND ? call.bind(call) : function () { + return call.apply(call, arguments); + }; + return functionCall; + } + + var objectPropertyIsEnumerable = {}; + + var hasRequiredObjectPropertyIsEnumerable; + + function requireObjectPropertyIsEnumerable () { + if (hasRequiredObjectPropertyIsEnumerable) return objectPropertyIsEnumerable; + hasRequiredObjectPropertyIsEnumerable = 1; + var $propertyIsEnumerable = {}.propertyIsEnumerable; + // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe + var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; + + // Nashorn ~ JDK8 bug + var NASHORN_BUG = getOwnPropertyDescriptor && !$propertyIsEnumerable.call({ 1: 2 }, 1); + + // `Object.prototype.propertyIsEnumerable` method implementation + // https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable + objectPropertyIsEnumerable.f = NASHORN_BUG ? function propertyIsEnumerable(V) { + var descriptor = getOwnPropertyDescriptor(this, V); + return !!descriptor && descriptor.enumerable; + } : $propertyIsEnumerable; + return objectPropertyIsEnumerable; + } + + var createPropertyDescriptor; + var hasRequiredCreatePropertyDescriptor; + + function requireCreatePropertyDescriptor () { + if (hasRequiredCreatePropertyDescriptor) return createPropertyDescriptor; + hasRequiredCreatePropertyDescriptor = 1; + createPropertyDescriptor = function (bitmap, value) { + return { + enumerable: !(bitmap & 1), + configurable: !(bitmap & 2), + writable: !(bitmap & 4), + value: value + }; + }; + return createPropertyDescriptor; + } + + var functionUncurryThis; + var hasRequiredFunctionUncurryThis; + + function requireFunctionUncurryThis () { + if (hasRequiredFunctionUncurryThis) return functionUncurryThis; + hasRequiredFunctionUncurryThis = 1; + var NATIVE_BIND = requireFunctionBindNative(); + + var FunctionPrototype = Function.prototype; + var call = FunctionPrototype.call; + var uncurryThisWithBind = NATIVE_BIND && FunctionPrototype.bind.bind(call, call); + + functionUncurryThis = NATIVE_BIND ? uncurryThisWithBind : function (fn) { + return function () { + return call.apply(fn, arguments); + }; + }; + return functionUncurryThis; + } + + var classofRaw; + var hasRequiredClassofRaw; + + function requireClassofRaw () { + if (hasRequiredClassofRaw) return classofRaw; + hasRequiredClassofRaw = 1; + var uncurryThis = requireFunctionUncurryThis(); + + var toString = uncurryThis({}.toString); + var stringSlice = uncurryThis(''.slice); + + classofRaw = function (it) { + return stringSlice(toString(it), 8, -1); + }; + return classofRaw; + } + + var indexedObject; + var hasRequiredIndexedObject; + + function requireIndexedObject () { + if (hasRequiredIndexedObject) return indexedObject; + hasRequiredIndexedObject = 1; + var uncurryThis = requireFunctionUncurryThis(); + var fails = requireFails(); + var classof = requireClassofRaw(); + + var $Object = Object; + var split = uncurryThis(''.split); + + // fallback for non-array-like ES3 and non-enumerable old V8 strings + indexedObject = fails(function () { + // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346 + // eslint-disable-next-line no-prototype-builtins -- safe + return !$Object('z').propertyIsEnumerable(0); + }) ? function (it) { + return classof(it) === 'String' ? split(it, '') : $Object(it); + } : $Object; + return indexedObject; + } + + var isNullOrUndefined; + var hasRequiredIsNullOrUndefined; + + function requireIsNullOrUndefined () { + if (hasRequiredIsNullOrUndefined) return isNullOrUndefined; + hasRequiredIsNullOrUndefined = 1; + // we can't use just `it == null` since of `document.all` special case + // https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot-aec + isNullOrUndefined = function (it) { + return it === null || it === undefined; + }; + return isNullOrUndefined; + } + + var requireObjectCoercible; + var hasRequiredRequireObjectCoercible; + + function requireRequireObjectCoercible () { + if (hasRequiredRequireObjectCoercible) return requireObjectCoercible; + hasRequiredRequireObjectCoercible = 1; + var isNullOrUndefined = requireIsNullOrUndefined(); + + var $TypeError = TypeError; + + // `RequireObjectCoercible` abstract operation + // https://tc39.es/ecma262/#sec-requireobjectcoercible + requireObjectCoercible = function (it) { + if (isNullOrUndefined(it)) throw new $TypeError("Can't call method on " + it); + return it; + }; + return requireObjectCoercible; + } + + var toIndexedObject; + var hasRequiredToIndexedObject; + + function requireToIndexedObject () { + if (hasRequiredToIndexedObject) return toIndexedObject; + hasRequiredToIndexedObject = 1; + // toObject with fallback for non-array-like ES3 strings + var IndexedObject = requireIndexedObject(); + var requireObjectCoercible = requireRequireObjectCoercible(); + + toIndexedObject = function (it) { + return IndexedObject(requireObjectCoercible(it)); + }; + return toIndexedObject; + } + + var isCallable; + var hasRequiredIsCallable; + + function requireIsCallable () { + if (hasRequiredIsCallable) return isCallable; + hasRequiredIsCallable = 1; + // https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot + var documentAll = typeof document == 'object' && document.all; + + // `IsCallable` abstract operation + // https://tc39.es/ecma262/#sec-iscallable + // eslint-disable-next-line unicorn/no-typeof-undefined -- required for testing + isCallable = typeof documentAll == 'undefined' && documentAll !== undefined ? function (argument) { + return typeof argument == 'function' || argument === documentAll; + } : function (argument) { + return typeof argument == 'function'; + }; + return isCallable; + } + + var isObject; + var hasRequiredIsObject; + + function requireIsObject () { + if (hasRequiredIsObject) return isObject; + hasRequiredIsObject = 1; + var isCallable = requireIsCallable(); + + isObject = function (it) { + return typeof it == 'object' ? it !== null : isCallable(it); + }; + return isObject; + } + + var getBuiltIn; + var hasRequiredGetBuiltIn; + + function requireGetBuiltIn () { + if (hasRequiredGetBuiltIn) return getBuiltIn; + hasRequiredGetBuiltIn = 1; + var globalThis = requireGlobalThis(); + var isCallable = requireIsCallable(); + + var aFunction = function (argument) { + return isCallable(argument) ? argument : undefined; + }; + + getBuiltIn = function (namespace, method) { + return arguments.length < 2 ? aFunction(globalThis[namespace]) : globalThis[namespace] && globalThis[namespace][method]; + }; + return getBuiltIn; + } + + var objectIsPrototypeOf; + var hasRequiredObjectIsPrototypeOf; + + function requireObjectIsPrototypeOf () { + if (hasRequiredObjectIsPrototypeOf) return objectIsPrototypeOf; + hasRequiredObjectIsPrototypeOf = 1; + var uncurryThis = requireFunctionUncurryThis(); + + objectIsPrototypeOf = uncurryThis({}.isPrototypeOf); + return objectIsPrototypeOf; + } + + var environmentUserAgent; + var hasRequiredEnvironmentUserAgent; + + function requireEnvironmentUserAgent () { + if (hasRequiredEnvironmentUserAgent) return environmentUserAgent; + hasRequiredEnvironmentUserAgent = 1; + var globalThis = requireGlobalThis(); + + var navigator = globalThis.navigator; + var userAgent = navigator && navigator.userAgent; + + environmentUserAgent = userAgent ? String(userAgent) : ''; + return environmentUserAgent; + } + + var environmentV8Version; + var hasRequiredEnvironmentV8Version; + + function requireEnvironmentV8Version () { + if (hasRequiredEnvironmentV8Version) return environmentV8Version; + hasRequiredEnvironmentV8Version = 1; + var globalThis = requireGlobalThis(); + var userAgent = requireEnvironmentUserAgent(); + + var process = globalThis.process; + var Deno = globalThis.Deno; + var versions = process && process.versions || Deno && Deno.version; + var v8 = versions && versions.v8; + var match, version; + + if (v8) { + match = v8.split('.'); + // in old Chrome, versions of V8 isn't V8 = Chrome / 10 + // but their correct versions are not interesting for us + version = match[0] > 0 && match[0] < 4 ? 1 : +(match[0] + match[1]); + } + + // BrowserFS NodeJS `process` polyfill incorrectly set `.v8` to `0.0` + // so check `userAgent` even if `.v8` exists, but 0 + if (!version && userAgent) { + match = userAgent.match(/Edge\/(\d+)/); + if (!match || match[1] >= 74) { + match = userAgent.match(/Chrome\/(\d+)/); + if (match) version = +match[1]; + } + } + + environmentV8Version = version; + return environmentV8Version; + } + + var symbolConstructorDetection; + var hasRequiredSymbolConstructorDetection; + + function requireSymbolConstructorDetection () { + if (hasRequiredSymbolConstructorDetection) return symbolConstructorDetection; + hasRequiredSymbolConstructorDetection = 1; + /* eslint-disable es/no-symbol -- required for testing */ + var V8_VERSION = requireEnvironmentV8Version(); + var fails = requireFails(); + var globalThis = requireGlobalThis(); + + var $String = globalThis.String; + + // eslint-disable-next-line es/no-object-getownpropertysymbols -- required for testing + symbolConstructorDetection = !!Object.getOwnPropertySymbols && !fails(function () { + var symbol = Symbol('symbol detection'); + // Chrome 38 Symbol has incorrect toString conversion + // `get-own-property-symbols` polyfill symbols converted to object are not Symbol instances + // nb: Do not call `String` directly to avoid this being optimized out to `symbol+''` which will, + // of course, fail. + return !$String(symbol) || !(Object(symbol) instanceof Symbol) || + // Chrome 38-40 symbols are not inherited from DOM collections prototypes to instances + !Symbol.sham && V8_VERSION && V8_VERSION < 41; + }); + return symbolConstructorDetection; + } + + var useSymbolAsUid; + var hasRequiredUseSymbolAsUid; + + function requireUseSymbolAsUid () { + if (hasRequiredUseSymbolAsUid) return useSymbolAsUid; + hasRequiredUseSymbolAsUid = 1; + /* eslint-disable es/no-symbol -- required for testing */ + var NATIVE_SYMBOL = requireSymbolConstructorDetection(); + + useSymbolAsUid = NATIVE_SYMBOL && + !Symbol.sham && + typeof Symbol.iterator == 'symbol'; + return useSymbolAsUid; + } + + var isSymbol; + var hasRequiredIsSymbol; + + function requireIsSymbol () { + if (hasRequiredIsSymbol) return isSymbol; + hasRequiredIsSymbol = 1; + var getBuiltIn = requireGetBuiltIn(); + var isCallable = requireIsCallable(); + var isPrototypeOf = requireObjectIsPrototypeOf(); + var USE_SYMBOL_AS_UID = requireUseSymbolAsUid(); + + var $Object = Object; + + isSymbol = USE_SYMBOL_AS_UID ? function (it) { + return typeof it == 'symbol'; + } : function (it) { + var $Symbol = getBuiltIn('Symbol'); + return isCallable($Symbol) && isPrototypeOf($Symbol.prototype, $Object(it)); + }; + return isSymbol; + } + + var tryToString; + var hasRequiredTryToString; + + function requireTryToString () { + if (hasRequiredTryToString) return tryToString; + hasRequiredTryToString = 1; + var $String = String; + + tryToString = function (argument) { + try { + return $String(argument); + } catch (error) { + return 'Object'; + } + }; + return tryToString; + } + + var aCallable; + var hasRequiredACallable; + + function requireACallable () { + if (hasRequiredACallable) return aCallable; + hasRequiredACallable = 1; + var isCallable = requireIsCallable(); + var tryToString = requireTryToString(); + + var $TypeError = TypeError; + + // `Assert: IsCallable(argument) is true` + aCallable = function (argument) { + if (isCallable(argument)) return argument; + throw new $TypeError(tryToString(argument) + ' is not a function'); + }; + return aCallable; + } + + var getMethod; + var hasRequiredGetMethod; + + function requireGetMethod () { + if (hasRequiredGetMethod) return getMethod; + hasRequiredGetMethod = 1; + var aCallable = requireACallable(); + var isNullOrUndefined = requireIsNullOrUndefined(); + + // `GetMethod` abstract operation + // https://tc39.es/ecma262/#sec-getmethod + getMethod = function (V, P) { + var func = V[P]; + return isNullOrUndefined(func) ? undefined : aCallable(func); + }; + return getMethod; + } + + var ordinaryToPrimitive; + var hasRequiredOrdinaryToPrimitive; + + function requireOrdinaryToPrimitive () { + if (hasRequiredOrdinaryToPrimitive) return ordinaryToPrimitive; + hasRequiredOrdinaryToPrimitive = 1; + var call = requireFunctionCall(); + var isCallable = requireIsCallable(); + var isObject = requireIsObject(); + + var $TypeError = TypeError; + + // `OrdinaryToPrimitive` abstract operation + // https://tc39.es/ecma262/#sec-ordinarytoprimitive + ordinaryToPrimitive = function (input, pref) { + var fn, val; + if (pref === 'string' && isCallable(fn = input.toString) && !isObject(val = call(fn, input))) return val; + if (isCallable(fn = input.valueOf) && !isObject(val = call(fn, input))) return val; + if (pref !== 'string' && isCallable(fn = input.toString) && !isObject(val = call(fn, input))) return val; + throw new $TypeError("Can't convert object to primitive value"); + }; + return ordinaryToPrimitive; + } + + var sharedStore = {exports: {}}; + + var isPure; + var hasRequiredIsPure; + + function requireIsPure () { + if (hasRequiredIsPure) return isPure; + hasRequiredIsPure = 1; + isPure = false; + return isPure; + } + + var defineGlobalProperty; + var hasRequiredDefineGlobalProperty; + + function requireDefineGlobalProperty () { + if (hasRequiredDefineGlobalProperty) return defineGlobalProperty; + hasRequiredDefineGlobalProperty = 1; + var globalThis = requireGlobalThis(); + + // eslint-disable-next-line es/no-object-defineproperty -- safe + var defineProperty = Object.defineProperty; + + defineGlobalProperty = function (key, value) { + try { + defineProperty(globalThis, key, { value: value, configurable: true, writable: true }); + } catch (error) { + globalThis[key] = value; + } return value; + }; + return defineGlobalProperty; + } + + var hasRequiredSharedStore; + + function requireSharedStore () { + if (hasRequiredSharedStore) return sharedStore.exports; + hasRequiredSharedStore = 1; + var IS_PURE = requireIsPure(); + var globalThis = requireGlobalThis(); + var defineGlobalProperty = requireDefineGlobalProperty(); + + var SHARED = '__core-js_shared__'; + var store = sharedStore.exports = globalThis[SHARED] || defineGlobalProperty(SHARED, {}); + + (store.versions || (store.versions = [])).push({ + version: '3.39.0', + mode: IS_PURE ? 'pure' : 'global', + copyright: '© 2014-2024 Denis Pushkarev (zloirock.ru)', + license: 'https://github.com/zloirock/core-js/blob/v3.39.0/LICENSE', + source: 'https://github.com/zloirock/core-js' + }); + return sharedStore.exports; + } + + var shared; + var hasRequiredShared; + + function requireShared () { + if (hasRequiredShared) return shared; + hasRequiredShared = 1; + var store = requireSharedStore(); + + shared = function (key, value) { + return store[key] || (store[key] = value || {}); + }; + return shared; + } + + var toObject; + var hasRequiredToObject; + + function requireToObject () { + if (hasRequiredToObject) return toObject; + hasRequiredToObject = 1; + var requireObjectCoercible = requireRequireObjectCoercible(); + + var $Object = Object; + + // `ToObject` abstract operation + // https://tc39.es/ecma262/#sec-toobject + toObject = function (argument) { + return $Object(requireObjectCoercible(argument)); + }; + return toObject; + } + + var hasOwnProperty_1; + var hasRequiredHasOwnProperty; + + function requireHasOwnProperty () { + if (hasRequiredHasOwnProperty) return hasOwnProperty_1; + hasRequiredHasOwnProperty = 1; + var uncurryThis = requireFunctionUncurryThis(); + var toObject = requireToObject(); + + var hasOwnProperty = uncurryThis({}.hasOwnProperty); + + // `HasOwnProperty` abstract operation + // https://tc39.es/ecma262/#sec-hasownproperty + // eslint-disable-next-line es/no-object-hasown -- safe + hasOwnProperty_1 = Object.hasOwn || function hasOwn(it, key) { + return hasOwnProperty(toObject(it), key); + }; + return hasOwnProperty_1; + } + + var uid; + var hasRequiredUid; + + function requireUid () { + if (hasRequiredUid) return uid; + hasRequiredUid = 1; + var uncurryThis = requireFunctionUncurryThis(); + + var id = 0; + var postfix = Math.random(); + var toString = uncurryThis(1.0.toString); + + uid = function (key) { + return 'Symbol(' + (key === undefined ? '' : key) + ')_' + toString(++id + postfix, 36); + }; + return uid; + } + + var wellKnownSymbol; + var hasRequiredWellKnownSymbol; + + function requireWellKnownSymbol () { + if (hasRequiredWellKnownSymbol) return wellKnownSymbol; + hasRequiredWellKnownSymbol = 1; + var globalThis = requireGlobalThis(); + var shared = requireShared(); + var hasOwn = requireHasOwnProperty(); + var uid = requireUid(); + var NATIVE_SYMBOL = requireSymbolConstructorDetection(); + var USE_SYMBOL_AS_UID = requireUseSymbolAsUid(); + + var Symbol = globalThis.Symbol; + var WellKnownSymbolsStore = shared('wks'); + var createWellKnownSymbol = USE_SYMBOL_AS_UID ? Symbol['for'] || Symbol : Symbol && Symbol.withoutSetter || uid; + + wellKnownSymbol = function (name) { + if (!hasOwn(WellKnownSymbolsStore, name)) { + WellKnownSymbolsStore[name] = NATIVE_SYMBOL && hasOwn(Symbol, name) + ? Symbol[name] + : createWellKnownSymbol('Symbol.' + name); + } return WellKnownSymbolsStore[name]; + }; + return wellKnownSymbol; + } + + var toPrimitive; + var hasRequiredToPrimitive; + + function requireToPrimitive () { + if (hasRequiredToPrimitive) return toPrimitive; + hasRequiredToPrimitive = 1; + var call = requireFunctionCall(); + var isObject = requireIsObject(); + var isSymbol = requireIsSymbol(); + var getMethod = requireGetMethod(); + var ordinaryToPrimitive = requireOrdinaryToPrimitive(); + var wellKnownSymbol = requireWellKnownSymbol(); + + var $TypeError = TypeError; + var TO_PRIMITIVE = wellKnownSymbol('toPrimitive'); + + // `ToPrimitive` abstract operation + // https://tc39.es/ecma262/#sec-toprimitive + toPrimitive = function (input, pref) { + if (!isObject(input) || isSymbol(input)) return input; + var exoticToPrim = getMethod(input, TO_PRIMITIVE); + var result; + if (exoticToPrim) { + if (pref === undefined) pref = 'default'; + result = call(exoticToPrim, input, pref); + if (!isObject(result) || isSymbol(result)) return result; + throw new $TypeError("Can't convert object to primitive value"); + } + if (pref === undefined) pref = 'number'; + return ordinaryToPrimitive(input, pref); + }; + return toPrimitive; + } + + var toPropertyKey; + var hasRequiredToPropertyKey; + + function requireToPropertyKey () { + if (hasRequiredToPropertyKey) return toPropertyKey; + hasRequiredToPropertyKey = 1; + var toPrimitive = requireToPrimitive(); + var isSymbol = requireIsSymbol(); + + // `ToPropertyKey` abstract operation + // https://tc39.es/ecma262/#sec-topropertykey + toPropertyKey = function (argument) { + var key = toPrimitive(argument, 'string'); + return isSymbol(key) ? key : key + ''; + }; + return toPropertyKey; + } + + var documentCreateElement; + var hasRequiredDocumentCreateElement; + + function requireDocumentCreateElement () { + if (hasRequiredDocumentCreateElement) return documentCreateElement; + hasRequiredDocumentCreateElement = 1; + var globalThis = requireGlobalThis(); + var isObject = requireIsObject(); + + var document = globalThis.document; + // typeof document.createElement is 'object' in old IE + var EXISTS = isObject(document) && isObject(document.createElement); + + documentCreateElement = function (it) { + return EXISTS ? document.createElement(it) : {}; + }; + return documentCreateElement; + } + + var ie8DomDefine; + var hasRequiredIe8DomDefine; + + function requireIe8DomDefine () { + if (hasRequiredIe8DomDefine) return ie8DomDefine; + hasRequiredIe8DomDefine = 1; + var DESCRIPTORS = requireDescriptors(); + var fails = requireFails(); + var createElement = requireDocumentCreateElement(); + + // Thanks to IE8 for its funny defineProperty + ie8DomDefine = !DESCRIPTORS && !fails(function () { + // eslint-disable-next-line es/no-object-defineproperty -- required for testing + return Object.defineProperty(createElement('div'), 'a', { + get: function () { return 7; } + }).a !== 7; + }); + return ie8DomDefine; + } + + var hasRequiredObjectGetOwnPropertyDescriptor; + + function requireObjectGetOwnPropertyDescriptor () { + if (hasRequiredObjectGetOwnPropertyDescriptor) return objectGetOwnPropertyDescriptor; + hasRequiredObjectGetOwnPropertyDescriptor = 1; + var DESCRIPTORS = requireDescriptors(); + var call = requireFunctionCall(); + var propertyIsEnumerableModule = requireObjectPropertyIsEnumerable(); + var createPropertyDescriptor = requireCreatePropertyDescriptor(); + var toIndexedObject = requireToIndexedObject(); + var toPropertyKey = requireToPropertyKey(); + var hasOwn = requireHasOwnProperty(); + var IE8_DOM_DEFINE = requireIe8DomDefine(); + + // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe + var $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; + + // `Object.getOwnPropertyDescriptor` method + // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor + objectGetOwnPropertyDescriptor.f = DESCRIPTORS ? $getOwnPropertyDescriptor : function getOwnPropertyDescriptor(O, P) { + O = toIndexedObject(O); + P = toPropertyKey(P); + if (IE8_DOM_DEFINE) try { + return $getOwnPropertyDescriptor(O, P); + } catch (error) { /* empty */ } + if (hasOwn(O, P)) return createPropertyDescriptor(!call(propertyIsEnumerableModule.f, O, P), O[P]); + }; + return objectGetOwnPropertyDescriptor; + } + + var objectDefineProperty = {}; + + var v8PrototypeDefineBug; + var hasRequiredV8PrototypeDefineBug; + + function requireV8PrototypeDefineBug () { + if (hasRequiredV8PrototypeDefineBug) return v8PrototypeDefineBug; + hasRequiredV8PrototypeDefineBug = 1; + var DESCRIPTORS = requireDescriptors(); + var fails = requireFails(); + + // V8 ~ Chrome 36- + // https://bugs.chromium.org/p/v8/issues/detail?id=3334 + v8PrototypeDefineBug = DESCRIPTORS && fails(function () { + // eslint-disable-next-line es/no-object-defineproperty -- required for testing + return Object.defineProperty(function () { /* empty */ }, 'prototype', { + value: 42, + writable: false + }).prototype !== 42; + }); + return v8PrototypeDefineBug; + } + + var anObject; + var hasRequiredAnObject; + + function requireAnObject () { + if (hasRequiredAnObject) return anObject; + hasRequiredAnObject = 1; + var isObject = requireIsObject(); + + var $String = String; + var $TypeError = TypeError; + + // `Assert: Type(argument) is Object` + anObject = function (argument) { + if (isObject(argument)) return argument; + throw new $TypeError($String(argument) + ' is not an object'); + }; + return anObject; + } + + var hasRequiredObjectDefineProperty; + + function requireObjectDefineProperty () { + if (hasRequiredObjectDefineProperty) return objectDefineProperty; + hasRequiredObjectDefineProperty = 1; + var DESCRIPTORS = requireDescriptors(); + var IE8_DOM_DEFINE = requireIe8DomDefine(); + var V8_PROTOTYPE_DEFINE_BUG = requireV8PrototypeDefineBug(); + var anObject = requireAnObject(); + var toPropertyKey = requireToPropertyKey(); + + var $TypeError = TypeError; + // eslint-disable-next-line es/no-object-defineproperty -- safe + var $defineProperty = Object.defineProperty; + // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe + var $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; + var ENUMERABLE = 'enumerable'; + var CONFIGURABLE = 'configurable'; + var WRITABLE = 'writable'; + + // `Object.defineProperty` method + // https://tc39.es/ecma262/#sec-object.defineproperty + objectDefineProperty.f = DESCRIPTORS ? V8_PROTOTYPE_DEFINE_BUG ? function defineProperty(O, P, Attributes) { + anObject(O); + P = toPropertyKey(P); + anObject(Attributes); + if (typeof O === 'function' && P === 'prototype' && 'value' in Attributes && WRITABLE in Attributes && !Attributes[WRITABLE]) { + var current = $getOwnPropertyDescriptor(O, P); + if (current && current[WRITABLE]) { + O[P] = Attributes.value; + Attributes = { + configurable: CONFIGURABLE in Attributes ? Attributes[CONFIGURABLE] : current[CONFIGURABLE], + enumerable: ENUMERABLE in Attributes ? Attributes[ENUMERABLE] : current[ENUMERABLE], + writable: false + }; + } + } return $defineProperty(O, P, Attributes); + } : $defineProperty : function defineProperty(O, P, Attributes) { + anObject(O); + P = toPropertyKey(P); + anObject(Attributes); + if (IE8_DOM_DEFINE) try { + return $defineProperty(O, P, Attributes); + } catch (error) { /* empty */ } + if ('get' in Attributes || 'set' in Attributes) throw new $TypeError('Accessors not supported'); + if ('value' in Attributes) O[P] = Attributes.value; + return O; + }; + return objectDefineProperty; + } + + var createNonEnumerableProperty; + var hasRequiredCreateNonEnumerableProperty; + + function requireCreateNonEnumerableProperty () { + if (hasRequiredCreateNonEnumerableProperty) return createNonEnumerableProperty; + hasRequiredCreateNonEnumerableProperty = 1; + var DESCRIPTORS = requireDescriptors(); + var definePropertyModule = requireObjectDefineProperty(); + var createPropertyDescriptor = requireCreatePropertyDescriptor(); + + createNonEnumerableProperty = DESCRIPTORS ? function (object, key, value) { + return definePropertyModule.f(object, key, createPropertyDescriptor(1, value)); + } : function (object, key, value) { + object[key] = value; + return object; + }; + return createNonEnumerableProperty; + } + + var makeBuiltIn = {exports: {}}; + + var functionName; + var hasRequiredFunctionName; + + function requireFunctionName () { + if (hasRequiredFunctionName) return functionName; + hasRequiredFunctionName = 1; + var DESCRIPTORS = requireDescriptors(); + var hasOwn = requireHasOwnProperty(); + + var FunctionPrototype = Function.prototype; + // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe + var getDescriptor = DESCRIPTORS && Object.getOwnPropertyDescriptor; + + var EXISTS = hasOwn(FunctionPrototype, 'name'); + // additional protection from minified / mangled / dropped function names + var PROPER = EXISTS && (function something() { /* empty */ }).name === 'something'; + var CONFIGURABLE = EXISTS && (!DESCRIPTORS || (DESCRIPTORS && getDescriptor(FunctionPrototype, 'name').configurable)); + + functionName = { + EXISTS: EXISTS, + PROPER: PROPER, + CONFIGURABLE: CONFIGURABLE + }; + return functionName; + } + + var inspectSource; + var hasRequiredInspectSource; + + function requireInspectSource () { + if (hasRequiredInspectSource) return inspectSource; + hasRequiredInspectSource = 1; + var uncurryThis = requireFunctionUncurryThis(); + var isCallable = requireIsCallable(); + var store = requireSharedStore(); + + var functionToString = uncurryThis(Function.toString); + + // this helper broken in `core-js@3.4.1-3.4.4`, so we can't use `shared` helper + if (!isCallable(store.inspectSource)) { + store.inspectSource = function (it) { + return functionToString(it); + }; + } + + inspectSource = store.inspectSource; + return inspectSource; + } + + var weakMapBasicDetection; + var hasRequiredWeakMapBasicDetection; + + function requireWeakMapBasicDetection () { + if (hasRequiredWeakMapBasicDetection) return weakMapBasicDetection; + hasRequiredWeakMapBasicDetection = 1; + var globalThis = requireGlobalThis(); + var isCallable = requireIsCallable(); + + var WeakMap = globalThis.WeakMap; + + weakMapBasicDetection = isCallable(WeakMap) && /native code/.test(String(WeakMap)); + return weakMapBasicDetection; + } + + var sharedKey; + var hasRequiredSharedKey; + + function requireSharedKey () { + if (hasRequiredSharedKey) return sharedKey; + hasRequiredSharedKey = 1; + var shared = requireShared(); + var uid = requireUid(); + + var keys = shared('keys'); + + sharedKey = function (key) { + return keys[key] || (keys[key] = uid(key)); + }; + return sharedKey; + } + + var hiddenKeys; + var hasRequiredHiddenKeys; + + function requireHiddenKeys () { + if (hasRequiredHiddenKeys) return hiddenKeys; + hasRequiredHiddenKeys = 1; + hiddenKeys = {}; + return hiddenKeys; + } + + var internalState; + var hasRequiredInternalState; + + function requireInternalState () { + if (hasRequiredInternalState) return internalState; + hasRequiredInternalState = 1; + var NATIVE_WEAK_MAP = requireWeakMapBasicDetection(); + var globalThis = requireGlobalThis(); + var isObject = requireIsObject(); + var createNonEnumerableProperty = requireCreateNonEnumerableProperty(); + var hasOwn = requireHasOwnProperty(); + var shared = requireSharedStore(); + var sharedKey = requireSharedKey(); + var hiddenKeys = requireHiddenKeys(); + + var OBJECT_ALREADY_INITIALIZED = 'Object already initialized'; + var TypeError = globalThis.TypeError; + var WeakMap = globalThis.WeakMap; + var set, get, has; + + var enforce = function (it) { + return has(it) ? get(it) : set(it, {}); + }; + + var getterFor = function (TYPE) { + return function (it) { + var state; + if (!isObject(it) || (state = get(it)).type !== TYPE) { + throw new TypeError('Incompatible receiver, ' + TYPE + ' required'); + } return state; + }; + }; + + if (NATIVE_WEAK_MAP || shared.state) { + var store = shared.state || (shared.state = new WeakMap()); + /* eslint-disable no-self-assign -- prototype methods protection */ + store.get = store.get; + store.has = store.has; + store.set = store.set; + /* eslint-enable no-self-assign -- prototype methods protection */ + set = function (it, metadata) { + if (store.has(it)) throw new TypeError(OBJECT_ALREADY_INITIALIZED); + metadata.facade = it; + store.set(it, metadata); + return metadata; + }; + get = function (it) { + return store.get(it) || {}; + }; + has = function (it) { + return store.has(it); + }; + } else { + var STATE = sharedKey('state'); + hiddenKeys[STATE] = true; + set = function (it, metadata) { + if (hasOwn(it, STATE)) throw new TypeError(OBJECT_ALREADY_INITIALIZED); + metadata.facade = it; + createNonEnumerableProperty(it, STATE, metadata); + return metadata; + }; + get = function (it) { + return hasOwn(it, STATE) ? it[STATE] : {}; + }; + has = function (it) { + return hasOwn(it, STATE); + }; + } + + internalState = { + set: set, + get: get, + has: has, + enforce: enforce, + getterFor: getterFor + }; + return internalState; + } + + var hasRequiredMakeBuiltIn; + + function requireMakeBuiltIn () { + if (hasRequiredMakeBuiltIn) return makeBuiltIn.exports; + hasRequiredMakeBuiltIn = 1; + var uncurryThis = requireFunctionUncurryThis(); + var fails = requireFails(); + var isCallable = requireIsCallable(); + var hasOwn = requireHasOwnProperty(); + var DESCRIPTORS = requireDescriptors(); + var CONFIGURABLE_FUNCTION_NAME = requireFunctionName().CONFIGURABLE; + var inspectSource = requireInspectSource(); + var InternalStateModule = requireInternalState(); + + var enforceInternalState = InternalStateModule.enforce; + var getInternalState = InternalStateModule.get; + var $String = String; + // eslint-disable-next-line es/no-object-defineproperty -- safe + var defineProperty = Object.defineProperty; + var stringSlice = uncurryThis(''.slice); + var replace = uncurryThis(''.replace); + var join = uncurryThis([].join); + + var CONFIGURABLE_LENGTH = DESCRIPTORS && !fails(function () { + return defineProperty(function () { /* empty */ }, 'length', { value: 8 }).length !== 8; + }); + + var TEMPLATE = String(String).split('String'); + + var makeBuiltIn$1 = makeBuiltIn.exports = function (value, name, options) { + if (stringSlice($String(name), 0, 7) === 'Symbol(') { + name = '[' + replace($String(name), /^Symbol\(([^)]*)\).*$/, '$1') + ']'; + } + if (options && options.getter) name = 'get ' + name; + if (options && options.setter) name = 'set ' + name; + if (!hasOwn(value, 'name') || (CONFIGURABLE_FUNCTION_NAME && value.name !== name)) { + if (DESCRIPTORS) defineProperty(value, 'name', { value: name, configurable: true }); + else value.name = name; + } + if (CONFIGURABLE_LENGTH && options && hasOwn(options, 'arity') && value.length !== options.arity) { + defineProperty(value, 'length', { value: options.arity }); + } + try { + if (options && hasOwn(options, 'constructor') && options.constructor) { + if (DESCRIPTORS) defineProperty(value, 'prototype', { writable: false }); + // in V8 ~ Chrome 53, prototypes of some methods, like `Array.prototype.values`, are non-writable + } else if (value.prototype) value.prototype = undefined; + } catch (error) { /* empty */ } + var state = enforceInternalState(value); + if (!hasOwn(state, 'source')) { + state.source = join(TEMPLATE, typeof name == 'string' ? name : ''); + } return value; + }; + + // add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative + // eslint-disable-next-line no-extend-native -- required + Function.prototype.toString = makeBuiltIn$1(function toString() { + return isCallable(this) && getInternalState(this).source || inspectSource(this); + }, 'toString'); + return makeBuiltIn.exports; + } + + var defineBuiltIn; + var hasRequiredDefineBuiltIn; + + function requireDefineBuiltIn () { + if (hasRequiredDefineBuiltIn) return defineBuiltIn; + hasRequiredDefineBuiltIn = 1; + var isCallable = requireIsCallable(); + var definePropertyModule = requireObjectDefineProperty(); + var makeBuiltIn = requireMakeBuiltIn(); + var defineGlobalProperty = requireDefineGlobalProperty(); + + defineBuiltIn = function (O, key, value, options) { + if (!options) options = {}; + var simple = options.enumerable; + var name = options.name !== undefined ? options.name : key; + if (isCallable(value)) makeBuiltIn(value, name, options); + if (options.global) { + if (simple) O[key] = value; + else defineGlobalProperty(key, value); + } else { + try { + if (!options.unsafe) delete O[key]; + else if (O[key]) simple = true; + } catch (error) { /* empty */ } + if (simple) O[key] = value; + else definePropertyModule.f(O, key, { + value: value, + enumerable: false, + configurable: !options.nonConfigurable, + writable: !options.nonWritable + }); + } return O; + }; + return defineBuiltIn; + } + + var objectGetOwnPropertyNames = {}; + + var mathTrunc; + var hasRequiredMathTrunc; + + function requireMathTrunc () { + if (hasRequiredMathTrunc) return mathTrunc; + hasRequiredMathTrunc = 1; + var ceil = Math.ceil; + var floor = Math.floor; + + // `Math.trunc` method + // https://tc39.es/ecma262/#sec-math.trunc + // eslint-disable-next-line es/no-math-trunc -- safe + mathTrunc = Math.trunc || function trunc(x) { + var n = +x; + return (n > 0 ? floor : ceil)(n); + }; + return mathTrunc; + } + + var toIntegerOrInfinity; + var hasRequiredToIntegerOrInfinity; + + function requireToIntegerOrInfinity () { + if (hasRequiredToIntegerOrInfinity) return toIntegerOrInfinity; + hasRequiredToIntegerOrInfinity = 1; + var trunc = requireMathTrunc(); + + // `ToIntegerOrInfinity` abstract operation + // https://tc39.es/ecma262/#sec-tointegerorinfinity + toIntegerOrInfinity = function (argument) { + var number = +argument; + // eslint-disable-next-line no-self-compare -- NaN check + return number !== number || number === 0 ? 0 : trunc(number); + }; + return toIntegerOrInfinity; + } + + var toAbsoluteIndex; + var hasRequiredToAbsoluteIndex; + + function requireToAbsoluteIndex () { + if (hasRequiredToAbsoluteIndex) return toAbsoluteIndex; + hasRequiredToAbsoluteIndex = 1; + var toIntegerOrInfinity = requireToIntegerOrInfinity(); + + var max = Math.max; + var min = Math.min; + + // Helper for a popular repeating case of the spec: + // Let integer be ? ToInteger(index). + // If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length). + toAbsoluteIndex = function (index, length) { + var integer = toIntegerOrInfinity(index); + return integer < 0 ? max(integer + length, 0) : min(integer, length); + }; + return toAbsoluteIndex; + } + + var toLength; + var hasRequiredToLength; + + function requireToLength () { + if (hasRequiredToLength) return toLength; + hasRequiredToLength = 1; + var toIntegerOrInfinity = requireToIntegerOrInfinity(); + + var min = Math.min; + + // `ToLength` abstract operation + // https://tc39.es/ecma262/#sec-tolength + toLength = function (argument) { + var len = toIntegerOrInfinity(argument); + return len > 0 ? min(len, 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991 + }; + return toLength; + } + + var lengthOfArrayLike; + var hasRequiredLengthOfArrayLike; + + function requireLengthOfArrayLike () { + if (hasRequiredLengthOfArrayLike) return lengthOfArrayLike; + hasRequiredLengthOfArrayLike = 1; + var toLength = requireToLength(); + + // `LengthOfArrayLike` abstract operation + // https://tc39.es/ecma262/#sec-lengthofarraylike + lengthOfArrayLike = function (obj) { + return toLength(obj.length); + }; + return lengthOfArrayLike; + } + + var arrayIncludes; + var hasRequiredArrayIncludes; + + function requireArrayIncludes () { + if (hasRequiredArrayIncludes) return arrayIncludes; + hasRequiredArrayIncludes = 1; + var toIndexedObject = requireToIndexedObject(); + var toAbsoluteIndex = requireToAbsoluteIndex(); + var lengthOfArrayLike = requireLengthOfArrayLike(); + + // `Array.prototype.{ indexOf, includes }` methods implementation + var createMethod = function (IS_INCLUDES) { + return function ($this, el, fromIndex) { + var O = toIndexedObject($this); + var length = lengthOfArrayLike(O); + if (length === 0) return !IS_INCLUDES && -1; + var index = toAbsoluteIndex(fromIndex, length); + var value; + // Array#includes uses SameValueZero equality algorithm + // eslint-disable-next-line no-self-compare -- NaN check + if (IS_INCLUDES && el !== el) while (length > index) { + value = O[index++]; + // eslint-disable-next-line no-self-compare -- NaN check + if (value !== value) return true; + // Array#indexOf ignores holes, Array#includes - not + } else for (;length > index; index++) { + if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0; + } return !IS_INCLUDES && -1; + }; + }; + + arrayIncludes = { + // `Array.prototype.includes` method + // https://tc39.es/ecma262/#sec-array.prototype.includes + includes: createMethod(true), + // `Array.prototype.indexOf` method + // https://tc39.es/ecma262/#sec-array.prototype.indexof + indexOf: createMethod(false) + }; + return arrayIncludes; + } + + var objectKeysInternal; + var hasRequiredObjectKeysInternal; + + function requireObjectKeysInternal () { + if (hasRequiredObjectKeysInternal) return objectKeysInternal; + hasRequiredObjectKeysInternal = 1; + var uncurryThis = requireFunctionUncurryThis(); + var hasOwn = requireHasOwnProperty(); + var toIndexedObject = requireToIndexedObject(); + var indexOf = requireArrayIncludes().indexOf; + var hiddenKeys = requireHiddenKeys(); + + var push = uncurryThis([].push); + + objectKeysInternal = function (object, names) { + var O = toIndexedObject(object); + var i = 0; + var result = []; + var key; + for (key in O) !hasOwn(hiddenKeys, key) && hasOwn(O, key) && push(result, key); + // Don't enum bug & hidden keys + while (names.length > i) if (hasOwn(O, key = names[i++])) { + ~indexOf(result, key) || push(result, key); + } + return result; + }; + return objectKeysInternal; + } + + var enumBugKeys; + var hasRequiredEnumBugKeys; + + function requireEnumBugKeys () { + if (hasRequiredEnumBugKeys) return enumBugKeys; + hasRequiredEnumBugKeys = 1; + // IE8- don't enum bug keys + enumBugKeys = [ + 'constructor', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'toLocaleString', + 'toString', + 'valueOf' + ]; + return enumBugKeys; + } + + var hasRequiredObjectGetOwnPropertyNames; + + function requireObjectGetOwnPropertyNames () { + if (hasRequiredObjectGetOwnPropertyNames) return objectGetOwnPropertyNames; + hasRequiredObjectGetOwnPropertyNames = 1; + var internalObjectKeys = requireObjectKeysInternal(); + var enumBugKeys = requireEnumBugKeys(); + + var hiddenKeys = enumBugKeys.concat('length', 'prototype'); + + // `Object.getOwnPropertyNames` method + // https://tc39.es/ecma262/#sec-object.getownpropertynames + // eslint-disable-next-line es/no-object-getownpropertynames -- safe + objectGetOwnPropertyNames.f = Object.getOwnPropertyNames || function getOwnPropertyNames(O) { + return internalObjectKeys(O, hiddenKeys); + }; + return objectGetOwnPropertyNames; + } + + var objectGetOwnPropertySymbols = {}; + + var hasRequiredObjectGetOwnPropertySymbols; + + function requireObjectGetOwnPropertySymbols () { + if (hasRequiredObjectGetOwnPropertySymbols) return objectGetOwnPropertySymbols; + hasRequiredObjectGetOwnPropertySymbols = 1; + // eslint-disable-next-line es/no-object-getownpropertysymbols -- safe + objectGetOwnPropertySymbols.f = Object.getOwnPropertySymbols; + return objectGetOwnPropertySymbols; + } + + var ownKeys; + var hasRequiredOwnKeys; + + function requireOwnKeys () { + if (hasRequiredOwnKeys) return ownKeys; + hasRequiredOwnKeys = 1; + var getBuiltIn = requireGetBuiltIn(); + var uncurryThis = requireFunctionUncurryThis(); + var getOwnPropertyNamesModule = requireObjectGetOwnPropertyNames(); + var getOwnPropertySymbolsModule = requireObjectGetOwnPropertySymbols(); + var anObject = requireAnObject(); + + var concat = uncurryThis([].concat); + + // all object keys, includes non-enumerable and symbols + ownKeys = getBuiltIn('Reflect', 'ownKeys') || function ownKeys(it) { + var keys = getOwnPropertyNamesModule.f(anObject(it)); + var getOwnPropertySymbols = getOwnPropertySymbolsModule.f; + return getOwnPropertySymbols ? concat(keys, getOwnPropertySymbols(it)) : keys; + }; + return ownKeys; + } + + var copyConstructorProperties; + var hasRequiredCopyConstructorProperties; + + function requireCopyConstructorProperties () { + if (hasRequiredCopyConstructorProperties) return copyConstructorProperties; + hasRequiredCopyConstructorProperties = 1; + var hasOwn = requireHasOwnProperty(); + var ownKeys = requireOwnKeys(); + var getOwnPropertyDescriptorModule = requireObjectGetOwnPropertyDescriptor(); + var definePropertyModule = requireObjectDefineProperty(); + + copyConstructorProperties = function (target, source, exceptions) { + var keys = ownKeys(source); + var defineProperty = definePropertyModule.f; + var getOwnPropertyDescriptor = getOwnPropertyDescriptorModule.f; + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (!hasOwn(target, key) && !(exceptions && hasOwn(exceptions, key))) { + defineProperty(target, key, getOwnPropertyDescriptor(source, key)); + } + } + }; + return copyConstructorProperties; + } + + var isForced_1; + var hasRequiredIsForced; + + function requireIsForced () { + if (hasRequiredIsForced) return isForced_1; + hasRequiredIsForced = 1; + var fails = requireFails(); + var isCallable = requireIsCallable(); + + var replacement = /#|\.prototype\./; + + var isForced = function (feature, detection) { + var value = data[normalize(feature)]; + return value === POLYFILL ? true + : value === NATIVE ? false + : isCallable(detection) ? fails(detection) + : !!detection; + }; + + var normalize = isForced.normalize = function (string) { + return String(string).replace(replacement, '.').toLowerCase(); + }; + + var data = isForced.data = {}; + var NATIVE = isForced.NATIVE = 'N'; + var POLYFILL = isForced.POLYFILL = 'P'; + + isForced_1 = isForced; + return isForced_1; + } + + var _export; + var hasRequired_export; + + function require_export () { + if (hasRequired_export) return _export; + hasRequired_export = 1; + var globalThis = requireGlobalThis(); + var getOwnPropertyDescriptor = requireObjectGetOwnPropertyDescriptor().f; + var createNonEnumerableProperty = requireCreateNonEnumerableProperty(); + var defineBuiltIn = requireDefineBuiltIn(); + var defineGlobalProperty = requireDefineGlobalProperty(); + var copyConstructorProperties = requireCopyConstructorProperties(); + var isForced = requireIsForced(); + + /* + options.target - name of the target object + options.global - target is the global object + options.stat - export as static methods of target + options.proto - export as prototype methods of target + options.real - real prototype method for the `pure` version + options.forced - export even if the native feature is available + options.bind - bind methods to the target, required for the `pure` version + options.wrap - wrap constructors to preventing global pollution, required for the `pure` version + options.unsafe - use the simple assignment of property instead of delete + defineProperty + options.sham - add a flag to not completely full polyfills + options.enumerable - export as enumerable property + options.dontCallGetSet - prevent calling a getter on target + options.name - the .name of the function if it does not match the key + */ + _export = function (options, source) { + var TARGET = options.target; + var GLOBAL = options.global; + var STATIC = options.stat; + var FORCED, target, key, targetProperty, sourceProperty, descriptor; + if (GLOBAL) { + target = globalThis; + } else if (STATIC) { + target = globalThis[TARGET] || defineGlobalProperty(TARGET, {}); + } else { + target = globalThis[TARGET] && globalThis[TARGET].prototype; + } + if (target) for (key in source) { + sourceProperty = source[key]; + if (options.dontCallGetSet) { + descriptor = getOwnPropertyDescriptor(target, key); + targetProperty = descriptor && descriptor.value; + } else targetProperty = target[key]; + FORCED = isForced(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced); + // contained in target + if (!FORCED && targetProperty !== undefined) { + if (typeof sourceProperty == typeof targetProperty) continue; + copyConstructorProperties(sourceProperty, targetProperty); + } + // add a flag to not completely full polyfills + if (options.sham || (targetProperty && targetProperty.sham)) { + createNonEnumerableProperty(sourceProperty, 'sham', true); + } + defineBuiltIn(target, key, sourceProperty, options); + } + }; + return _export; + } + + var isArray; + var hasRequiredIsArray; + + function requireIsArray () { + if (hasRequiredIsArray) return isArray; + hasRequiredIsArray = 1; + var classof = requireClassofRaw(); + + // `IsArray` abstract operation + // https://tc39.es/ecma262/#sec-isarray + // eslint-disable-next-line es/no-array-isarray -- safe + isArray = Array.isArray || function isArray(argument) { + return classof(argument) === 'Array'; + }; + return isArray; + } + + var doesNotExceedSafeInteger; + var hasRequiredDoesNotExceedSafeInteger; + + function requireDoesNotExceedSafeInteger () { + if (hasRequiredDoesNotExceedSafeInteger) return doesNotExceedSafeInteger; + hasRequiredDoesNotExceedSafeInteger = 1; + var $TypeError = TypeError; + var MAX_SAFE_INTEGER = 0x1FFFFFFFFFFFFF; // 2 ** 53 - 1 == 9007199254740991 + + doesNotExceedSafeInteger = function (it) { + if (it > MAX_SAFE_INTEGER) throw $TypeError('Maximum allowed index exceeded'); + return it; + }; + return doesNotExceedSafeInteger; + } + + var createProperty; + var hasRequiredCreateProperty; + + function requireCreateProperty () { + if (hasRequiredCreateProperty) return createProperty; + hasRequiredCreateProperty = 1; + var DESCRIPTORS = requireDescriptors(); + var definePropertyModule = requireObjectDefineProperty(); + var createPropertyDescriptor = requireCreatePropertyDescriptor(); + + createProperty = function (object, key, value) { + if (DESCRIPTORS) definePropertyModule.f(object, key, createPropertyDescriptor(0, value)); + else object[key] = value; + }; + return createProperty; + } + + var toStringTagSupport; + var hasRequiredToStringTagSupport; + + function requireToStringTagSupport () { + if (hasRequiredToStringTagSupport) return toStringTagSupport; + hasRequiredToStringTagSupport = 1; + var wellKnownSymbol = requireWellKnownSymbol(); + + var TO_STRING_TAG = wellKnownSymbol('toStringTag'); + var test = {}; + + test[TO_STRING_TAG] = 'z'; + + toStringTagSupport = String(test) === '[object z]'; + return toStringTagSupport; + } + + var classof; + var hasRequiredClassof; + + function requireClassof () { + if (hasRequiredClassof) return classof; + hasRequiredClassof = 1; + var TO_STRING_TAG_SUPPORT = requireToStringTagSupport(); + var isCallable = requireIsCallable(); + var classofRaw = requireClassofRaw(); + var wellKnownSymbol = requireWellKnownSymbol(); + + var TO_STRING_TAG = wellKnownSymbol('toStringTag'); + var $Object = Object; + + // ES3 wrong here + var CORRECT_ARGUMENTS = classofRaw(function () { return arguments; }()) === 'Arguments'; + + // fallback for IE11 Script Access Denied error + var tryGet = function (it, key) { + try { + return it[key]; + } catch (error) { /* empty */ } + }; + + // getting tag from ES6+ `Object.prototype.toString` + classof = TO_STRING_TAG_SUPPORT ? classofRaw : function (it) { + var O, tag, result; + return it === undefined ? 'Undefined' : it === null ? 'Null' + // @@toStringTag case + : typeof (tag = tryGet(O = $Object(it), TO_STRING_TAG)) == 'string' ? tag + // builtinTag case + : CORRECT_ARGUMENTS ? classofRaw(O) + // ES3 arguments fallback + : (result = classofRaw(O)) === 'Object' && isCallable(O.callee) ? 'Arguments' : result; + }; + return classof; + } + + var isConstructor; + var hasRequiredIsConstructor; + + function requireIsConstructor () { + if (hasRequiredIsConstructor) return isConstructor; + hasRequiredIsConstructor = 1; + var uncurryThis = requireFunctionUncurryThis(); + var fails = requireFails(); + var isCallable = requireIsCallable(); + var classof = requireClassof(); + var getBuiltIn = requireGetBuiltIn(); + var inspectSource = requireInspectSource(); + + var noop = function () { /* empty */ }; + var construct = getBuiltIn('Reflect', 'construct'); + var constructorRegExp = /^\s*(?:class|function)\b/; + var exec = uncurryThis(constructorRegExp.exec); + var INCORRECT_TO_STRING = !constructorRegExp.test(noop); + + var isConstructorModern = function isConstructor(argument) { + if (!isCallable(argument)) return false; + try { + construct(noop, [], argument); + return true; + } catch (error) { + return false; + } + }; + + var isConstructorLegacy = function isConstructor(argument) { + if (!isCallable(argument)) return false; + switch (classof(argument)) { + case 'AsyncFunction': + case 'GeneratorFunction': + case 'AsyncGeneratorFunction': return false; + } + try { + // we can't check .prototype since constructors produced by .bind haven't it + // `Function#toString` throws on some built-it function in some legacy engines + // (for example, `DOMQuad` and similar in FF41-) + return INCORRECT_TO_STRING || !!exec(constructorRegExp, inspectSource(argument)); + } catch (error) { + return true; + } + }; + + isConstructorLegacy.sham = true; + + // `IsConstructor` abstract operation + // https://tc39.es/ecma262/#sec-isconstructor + isConstructor = !construct || fails(function () { + var called; + return isConstructorModern(isConstructorModern.call) + || !isConstructorModern(Object) + || !isConstructorModern(function () { called = true; }) + || called; + }) ? isConstructorLegacy : isConstructorModern; + return isConstructor; + } + + var arraySpeciesConstructor; + var hasRequiredArraySpeciesConstructor; + + function requireArraySpeciesConstructor () { + if (hasRequiredArraySpeciesConstructor) return arraySpeciesConstructor; + hasRequiredArraySpeciesConstructor = 1; + var isArray = requireIsArray(); + var isConstructor = requireIsConstructor(); + var isObject = requireIsObject(); + var wellKnownSymbol = requireWellKnownSymbol(); + + var SPECIES = wellKnownSymbol('species'); + var $Array = Array; + + // a part of `ArraySpeciesCreate` abstract operation + // https://tc39.es/ecma262/#sec-arrayspeciescreate + arraySpeciesConstructor = function (originalArray) { + var C; + if (isArray(originalArray)) { + C = originalArray.constructor; + // cross-realm fallback + if (isConstructor(C) && (C === $Array || isArray(C.prototype))) C = undefined; + else if (isObject(C)) { + C = C[SPECIES]; + if (C === null) C = undefined; + } + } return C === undefined ? $Array : C; + }; + return arraySpeciesConstructor; + } + + var arraySpeciesCreate; + var hasRequiredArraySpeciesCreate; + + function requireArraySpeciesCreate () { + if (hasRequiredArraySpeciesCreate) return arraySpeciesCreate; + hasRequiredArraySpeciesCreate = 1; + var arraySpeciesConstructor = requireArraySpeciesConstructor(); + + // `ArraySpeciesCreate` abstract operation + // https://tc39.es/ecma262/#sec-arrayspeciescreate + arraySpeciesCreate = function (originalArray, length) { + return new (arraySpeciesConstructor(originalArray))(length === 0 ? 0 : length); + }; + return arraySpeciesCreate; + } + + var arrayMethodHasSpeciesSupport; + var hasRequiredArrayMethodHasSpeciesSupport; + + function requireArrayMethodHasSpeciesSupport () { + if (hasRequiredArrayMethodHasSpeciesSupport) return arrayMethodHasSpeciesSupport; + hasRequiredArrayMethodHasSpeciesSupport = 1; + var fails = requireFails(); + var wellKnownSymbol = requireWellKnownSymbol(); + var V8_VERSION = requireEnvironmentV8Version(); + + var SPECIES = wellKnownSymbol('species'); + + arrayMethodHasSpeciesSupport = function (METHOD_NAME) { + // We can't use this feature detection in V8 since it causes + // deoptimization and serious performance degradation + // https://github.com/zloirock/core-js/issues/677 + return V8_VERSION >= 51 || !fails(function () { + var array = []; + var constructor = array.constructor = {}; + constructor[SPECIES] = function () { + return { foo: 1 }; + }; + return array[METHOD_NAME](Boolean).foo !== 1; + }); + }; + return arrayMethodHasSpeciesSupport; + } + + var hasRequiredEs_array_concat; + + function requireEs_array_concat () { + if (hasRequiredEs_array_concat) return es_array_concat; + hasRequiredEs_array_concat = 1; + var $ = require_export(); + var fails = requireFails(); + var isArray = requireIsArray(); + var isObject = requireIsObject(); + var toObject = requireToObject(); + var lengthOfArrayLike = requireLengthOfArrayLike(); + var doesNotExceedSafeInteger = requireDoesNotExceedSafeInteger(); + var createProperty = requireCreateProperty(); + var arraySpeciesCreate = requireArraySpeciesCreate(); + var arrayMethodHasSpeciesSupport = requireArrayMethodHasSpeciesSupport(); + var wellKnownSymbol = requireWellKnownSymbol(); + var V8_VERSION = requireEnvironmentV8Version(); + + var IS_CONCAT_SPREADABLE = wellKnownSymbol('isConcatSpreadable'); + + // We can't use this feature detection in V8 since it causes + // deoptimization and serious performance degradation + // https://github.com/zloirock/core-js/issues/679 + var IS_CONCAT_SPREADABLE_SUPPORT = V8_VERSION >= 51 || !fails(function () { + var array = []; + array[IS_CONCAT_SPREADABLE] = false; + return array.concat()[0] !== array; + }); + + var isConcatSpreadable = function (O) { + if (!isObject(O)) return false; + var spreadable = O[IS_CONCAT_SPREADABLE]; + return spreadable !== undefined ? !!spreadable : isArray(O); + }; + + var FORCED = !IS_CONCAT_SPREADABLE_SUPPORT || !arrayMethodHasSpeciesSupport('concat'); + + // `Array.prototype.concat` method + // https://tc39.es/ecma262/#sec-array.prototype.concat + // with adding support of @@isConcatSpreadable and @@species + $({ target: 'Array', proto: true, arity: 1, forced: FORCED }, { + // eslint-disable-next-line no-unused-vars -- required for `.length` + concat: function concat(arg) { + var O = toObject(this); + var A = arraySpeciesCreate(O, 0); + var n = 0; + var i, k, length, len, E; + for (i = -1, length = arguments.length; i < length; i++) { + E = i === -1 ? O : arguments[i]; + if (isConcatSpreadable(E)) { + len = lengthOfArrayLike(E); + doesNotExceedSafeInteger(n + len); + for (k = 0; k < len; k++, n++) if (k in E) createProperty(A, n, E[k]); + } else { + doesNotExceedSafeInteger(n + 1); + createProperty(A, n++, E); + } + } + A.length = n; + return A; + } + }); + return es_array_concat; + } + + requireEs_array_concat(); + + var es_array_find = {}; + + var functionUncurryThisClause; + var hasRequiredFunctionUncurryThisClause; + + function requireFunctionUncurryThisClause () { + if (hasRequiredFunctionUncurryThisClause) return functionUncurryThisClause; + hasRequiredFunctionUncurryThisClause = 1; + var classofRaw = requireClassofRaw(); + var uncurryThis = requireFunctionUncurryThis(); + + functionUncurryThisClause = function (fn) { + // Nashorn bug: + // https://github.com/zloirock/core-js/issues/1128 + // https://github.com/zloirock/core-js/issues/1130 + if (classofRaw(fn) === 'Function') return uncurryThis(fn); + }; + return functionUncurryThisClause; + } + + var functionBindContext; + var hasRequiredFunctionBindContext; + + function requireFunctionBindContext () { + if (hasRequiredFunctionBindContext) return functionBindContext; + hasRequiredFunctionBindContext = 1; + var uncurryThis = requireFunctionUncurryThisClause(); + var aCallable = requireACallable(); + var NATIVE_BIND = requireFunctionBindNative(); + + var bind = uncurryThis(uncurryThis.bind); + + // optional / simple context binding + functionBindContext = function (fn, that) { + aCallable(fn); + return that === undefined ? fn : NATIVE_BIND ? bind(fn, that) : function (/* ...args */) { + return fn.apply(that, arguments); + }; + }; + return functionBindContext; + } + + var arrayIteration; + var hasRequiredArrayIteration; + + function requireArrayIteration () { + if (hasRequiredArrayIteration) return arrayIteration; + hasRequiredArrayIteration = 1; + var bind = requireFunctionBindContext(); + var uncurryThis = requireFunctionUncurryThis(); + var IndexedObject = requireIndexedObject(); + var toObject = requireToObject(); + var lengthOfArrayLike = requireLengthOfArrayLike(); + var arraySpeciesCreate = requireArraySpeciesCreate(); + + var push = uncurryThis([].push); + + // `Array.prototype.{ forEach, map, filter, some, every, find, findIndex, filterReject }` methods implementation + var createMethod = function (TYPE) { + var IS_MAP = TYPE === 1; + var IS_FILTER = TYPE === 2; + var IS_SOME = TYPE === 3; + var IS_EVERY = TYPE === 4; + var IS_FIND_INDEX = TYPE === 6; + var IS_FILTER_REJECT = TYPE === 7; + var NO_HOLES = TYPE === 5 || IS_FIND_INDEX; + return function ($this, callbackfn, that, specificCreate) { + var O = toObject($this); + var self = IndexedObject(O); + var length = lengthOfArrayLike(self); + var boundFunction = bind(callbackfn, that); + var index = 0; + var create = specificCreate || arraySpeciesCreate; + var target = IS_MAP ? create($this, length) : IS_FILTER || IS_FILTER_REJECT ? create($this, 0) : undefined; + var value, result; + for (;length > index; index++) if (NO_HOLES || index in self) { + value = self[index]; + result = boundFunction(value, index, O); + if (TYPE) { + if (IS_MAP) target[index] = result; // map + else if (result) switch (TYPE) { + case 3: return true; // some + case 5: return value; // find + case 6: return index; // findIndex + case 2: push(target, value); // filter + } else switch (TYPE) { + case 4: return false; // every + case 7: push(target, value); // filterReject + } + } + } + return IS_FIND_INDEX ? -1 : IS_SOME || IS_EVERY ? IS_EVERY : target; + }; + }; + + arrayIteration = { + // `Array.prototype.forEach` method + // https://tc39.es/ecma262/#sec-array.prototype.foreach + forEach: createMethod(0), + // `Array.prototype.map` method + // https://tc39.es/ecma262/#sec-array.prototype.map + map: createMethod(1), + // `Array.prototype.filter` method + // https://tc39.es/ecma262/#sec-array.prototype.filter + filter: createMethod(2), + // `Array.prototype.some` method + // https://tc39.es/ecma262/#sec-array.prototype.some + some: createMethod(3), + // `Array.prototype.every` method + // https://tc39.es/ecma262/#sec-array.prototype.every + every: createMethod(4), + // `Array.prototype.find` method + // https://tc39.es/ecma262/#sec-array.prototype.find + find: createMethod(5), + // `Array.prototype.findIndex` method + // https://tc39.es/ecma262/#sec-array.prototype.findIndex + findIndex: createMethod(6), + // `Array.prototype.filterReject` method + // https://github.com/tc39/proposal-array-filtering + filterReject: createMethod(7) + }; + return arrayIteration; + } + + var objectDefineProperties = {}; + + var objectKeys; + var hasRequiredObjectKeys; + + function requireObjectKeys () { + if (hasRequiredObjectKeys) return objectKeys; + hasRequiredObjectKeys = 1; + var internalObjectKeys = requireObjectKeysInternal(); + var enumBugKeys = requireEnumBugKeys(); + + // `Object.keys` method + // https://tc39.es/ecma262/#sec-object.keys + // eslint-disable-next-line es/no-object-keys -- safe + objectKeys = Object.keys || function keys(O) { + return internalObjectKeys(O, enumBugKeys); + }; + return objectKeys; + } + + var hasRequiredObjectDefineProperties; + + function requireObjectDefineProperties () { + if (hasRequiredObjectDefineProperties) return objectDefineProperties; + hasRequiredObjectDefineProperties = 1; + var DESCRIPTORS = requireDescriptors(); + var V8_PROTOTYPE_DEFINE_BUG = requireV8PrototypeDefineBug(); + var definePropertyModule = requireObjectDefineProperty(); + var anObject = requireAnObject(); + var toIndexedObject = requireToIndexedObject(); + var objectKeys = requireObjectKeys(); + + // `Object.defineProperties` method + // https://tc39.es/ecma262/#sec-object.defineproperties + // eslint-disable-next-line es/no-object-defineproperties -- safe + objectDefineProperties.f = DESCRIPTORS && !V8_PROTOTYPE_DEFINE_BUG ? Object.defineProperties : function defineProperties(O, Properties) { + anObject(O); + var props = toIndexedObject(Properties); + var keys = objectKeys(Properties); + var length = keys.length; + var index = 0; + var key; + while (length > index) definePropertyModule.f(O, key = keys[index++], props[key]); + return O; + }; + return objectDefineProperties; + } + + var html; + var hasRequiredHtml; + + function requireHtml () { + if (hasRequiredHtml) return html; + hasRequiredHtml = 1; + var getBuiltIn = requireGetBuiltIn(); + + html = getBuiltIn('document', 'documentElement'); + return html; + } + + var objectCreate; + var hasRequiredObjectCreate; + + function requireObjectCreate () { + if (hasRequiredObjectCreate) return objectCreate; + hasRequiredObjectCreate = 1; + /* global ActiveXObject -- old IE, WSH */ + var anObject = requireAnObject(); + var definePropertiesModule = requireObjectDefineProperties(); + var enumBugKeys = requireEnumBugKeys(); + var hiddenKeys = requireHiddenKeys(); + var html = requireHtml(); + var documentCreateElement = requireDocumentCreateElement(); + var sharedKey = requireSharedKey(); + + var GT = '>'; + var LT = '<'; + var PROTOTYPE = 'prototype'; + var SCRIPT = 'script'; + var IE_PROTO = sharedKey('IE_PROTO'); + + var EmptyConstructor = function () { /* empty */ }; + + var scriptTag = function (content) { + return LT + SCRIPT + GT + content + LT + '/' + SCRIPT + GT; + }; + + // Create object with fake `null` prototype: use ActiveX Object with cleared prototype + var NullProtoObjectViaActiveX = function (activeXDocument) { + activeXDocument.write(scriptTag('')); + activeXDocument.close(); + var temp = activeXDocument.parentWindow.Object; + // eslint-disable-next-line no-useless-assignment -- avoid memory leak + activeXDocument = null; + return temp; + }; + + // Create object with fake `null` prototype: use iframe Object with cleared prototype + var NullProtoObjectViaIFrame = function () { + // Thrash, waste and sodomy: IE GC bug + var iframe = documentCreateElement('iframe'); + var JS = 'java' + SCRIPT + ':'; + var iframeDocument; + iframe.style.display = 'none'; + html.appendChild(iframe); + // https://github.com/zloirock/core-js/issues/475 + iframe.src = String(JS); + iframeDocument = iframe.contentWindow.document; + iframeDocument.open(); + iframeDocument.write(scriptTag('document.F=Object')); + iframeDocument.close(); + return iframeDocument.F; + }; + + // Check for document.domain and active x support + // No need to use active x approach when document.domain is not set + // see https://github.com/es-shims/es5-shim/issues/150 + // variation of https://github.com/kitcambridge/es5-shim/commit/4f738ac066346 + // avoid IE GC bug + var activeXDocument; + var NullProtoObject = function () { + try { + activeXDocument = new ActiveXObject('htmlfile'); + } catch (error) { /* ignore */ } + NullProtoObject = typeof document != 'undefined' + ? document.domain && activeXDocument + ? NullProtoObjectViaActiveX(activeXDocument) // old IE + : NullProtoObjectViaIFrame() + : NullProtoObjectViaActiveX(activeXDocument); // WSH + var length = enumBugKeys.length; + while (length--) delete NullProtoObject[PROTOTYPE][enumBugKeys[length]]; + return NullProtoObject(); + }; + + hiddenKeys[IE_PROTO] = true; + + // `Object.create` method + // https://tc39.es/ecma262/#sec-object.create + // eslint-disable-next-line es/no-object-create -- safe + objectCreate = Object.create || function create(O, Properties) { + var result; + if (O !== null) { + EmptyConstructor[PROTOTYPE] = anObject(O); + result = new EmptyConstructor(); + EmptyConstructor[PROTOTYPE] = null; + // add "__proto__" for Object.getPrototypeOf polyfill + result[IE_PROTO] = O; + } else result = NullProtoObject(); + return Properties === undefined ? result : definePropertiesModule.f(result, Properties); + }; + return objectCreate; + } + + var addToUnscopables; + var hasRequiredAddToUnscopables; + + function requireAddToUnscopables () { + if (hasRequiredAddToUnscopables) return addToUnscopables; + hasRequiredAddToUnscopables = 1; + var wellKnownSymbol = requireWellKnownSymbol(); + var create = requireObjectCreate(); + var defineProperty = requireObjectDefineProperty().f; + + var UNSCOPABLES = wellKnownSymbol('unscopables'); + var ArrayPrototype = Array.prototype; + + // Array.prototype[@@unscopables] + // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables + if (ArrayPrototype[UNSCOPABLES] === undefined) { + defineProperty(ArrayPrototype, UNSCOPABLES, { + configurable: true, + value: create(null) + }); + } + + // add a key to Array.prototype[@@unscopables] + addToUnscopables = function (key) { + ArrayPrototype[UNSCOPABLES][key] = true; + }; + return addToUnscopables; + } + + var hasRequiredEs_array_find; + + function requireEs_array_find () { + if (hasRequiredEs_array_find) return es_array_find; + hasRequiredEs_array_find = 1; + var $ = require_export(); + var $find = requireArrayIteration().find; + var addToUnscopables = requireAddToUnscopables(); + + var FIND = 'find'; + var SKIPS_HOLES = true; + + // Shouldn't skip holes + // eslint-disable-next-line es/no-array-prototype-find -- testing + if (FIND in []) Array(1)[FIND](function () { SKIPS_HOLES = false; }); + + // `Array.prototype.find` method + // https://tc39.es/ecma262/#sec-array.prototype.find + $({ target: 'Array', proto: true, forced: SKIPS_HOLES }, { + find: function find(callbackfn /* , that = undefined */) { + return $find(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); + } + }); + + // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables + addToUnscopables(FIND); + return es_array_find; + } + + requireEs_array_find(); + + var es_array_slice = {}; + + var arraySlice; + var hasRequiredArraySlice; + + function requireArraySlice () { + if (hasRequiredArraySlice) return arraySlice; + hasRequiredArraySlice = 1; + var uncurryThis = requireFunctionUncurryThis(); + + arraySlice = uncurryThis([].slice); + return arraySlice; + } + + var hasRequiredEs_array_slice; + + function requireEs_array_slice () { + if (hasRequiredEs_array_slice) return es_array_slice; + hasRequiredEs_array_slice = 1; + var $ = require_export(); + var isArray = requireIsArray(); + var isConstructor = requireIsConstructor(); + var isObject = requireIsObject(); + var toAbsoluteIndex = requireToAbsoluteIndex(); + var lengthOfArrayLike = requireLengthOfArrayLike(); + var toIndexedObject = requireToIndexedObject(); + var createProperty = requireCreateProperty(); + var wellKnownSymbol = requireWellKnownSymbol(); + var arrayMethodHasSpeciesSupport = requireArrayMethodHasSpeciesSupport(); + var nativeSlice = requireArraySlice(); + + var HAS_SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('slice'); + + var SPECIES = wellKnownSymbol('species'); + var $Array = Array; + var max = Math.max; + + // `Array.prototype.slice` method + // https://tc39.es/ecma262/#sec-array.prototype.slice + // fallback for not array-like ES3 strings and DOM objects + $({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT }, { + slice: function slice(start, end) { + var O = toIndexedObject(this); + var length = lengthOfArrayLike(O); + var k = toAbsoluteIndex(start, length); + var fin = toAbsoluteIndex(end === undefined ? length : end, length); + // inline `ArraySpeciesCreate` for usage native `Array#slice` where it's possible + var Constructor, result, n; + if (isArray(O)) { + Constructor = O.constructor; + // cross-realm fallback + if (isConstructor(Constructor) && (Constructor === $Array || isArray(Constructor.prototype))) { + Constructor = undefined; + } else if (isObject(Constructor)) { + Constructor = Constructor[SPECIES]; + if (Constructor === null) Constructor = undefined; + } + if (Constructor === $Array || Constructor === undefined) { + return nativeSlice(O, k, fin); + } + } + result = new (Constructor === undefined ? $Array : Constructor)(max(fin - k, 0)); + for (n = 0; k < fin; k++, n++) if (k in O) createProperty(result, n, O[k]); + result.length = n; + return result; + } + }); + return es_array_slice; + } + + requireEs_array_slice(); + + var es_object_assign = {}; + + var objectAssign; + var hasRequiredObjectAssign; + + function requireObjectAssign () { + if (hasRequiredObjectAssign) return objectAssign; + hasRequiredObjectAssign = 1; + var DESCRIPTORS = requireDescriptors(); + var uncurryThis = requireFunctionUncurryThis(); + var call = requireFunctionCall(); + var fails = requireFails(); + var objectKeys = requireObjectKeys(); + var getOwnPropertySymbolsModule = requireObjectGetOwnPropertySymbols(); + var propertyIsEnumerableModule = requireObjectPropertyIsEnumerable(); + var toObject = requireToObject(); + var IndexedObject = requireIndexedObject(); + + // eslint-disable-next-line es/no-object-assign -- safe + var $assign = Object.assign; + // eslint-disable-next-line es/no-object-defineproperty -- required for testing + var defineProperty = Object.defineProperty; + var concat = uncurryThis([].concat); + + // `Object.assign` method + // https://tc39.es/ecma262/#sec-object.assign + objectAssign = !$assign || fails(function () { + // should have correct order of operations (Edge bug) + if (DESCRIPTORS && $assign({ b: 1 }, $assign(defineProperty({}, 'a', { + enumerable: true, + get: function () { + defineProperty(this, 'b', { + value: 3, + enumerable: false + }); + } + }), { b: 2 })).b !== 1) return true; + // should work with symbols and should have deterministic property order (V8 bug) + var A = {}; + var B = {}; + // eslint-disable-next-line es/no-symbol -- safe + var symbol = Symbol('assign detection'); + var alphabet = 'abcdefghijklmnopqrst'; + A[symbol] = 7; + alphabet.split('').forEach(function (chr) { B[chr] = chr; }); + return $assign({}, A)[symbol] !== 7 || objectKeys($assign({}, B)).join('') !== alphabet; + }) ? function assign(target, source) { // eslint-disable-line no-unused-vars -- required for `.length` + var T = toObject(target); + var argumentsLength = arguments.length; + var index = 1; + var getOwnPropertySymbols = getOwnPropertySymbolsModule.f; + var propertyIsEnumerable = propertyIsEnumerableModule.f; + while (argumentsLength > index) { + var S = IndexedObject(arguments[index++]); + var keys = getOwnPropertySymbols ? concat(objectKeys(S), getOwnPropertySymbols(S)) : objectKeys(S); + var length = keys.length; + var j = 0; + var key; + while (length > j) { + key = keys[j++]; + if (!DESCRIPTORS || call(propertyIsEnumerable, S, key)) T[key] = S[key]; + } + } return T; + } : $assign; + return objectAssign; + } + + var hasRequiredEs_object_assign; + + function requireEs_object_assign () { + if (hasRequiredEs_object_assign) return es_object_assign; + hasRequiredEs_object_assign = 1; + var $ = require_export(); + var assign = requireObjectAssign(); + + // `Object.assign` method + // https://tc39.es/ecma262/#sec-object.assign + // eslint-disable-next-line es/no-object-assign -- required for testing + $({ target: 'Object', stat: true, arity: 2, forced: Object.assign !== assign }, { + assign: assign + }); + return es_object_assign; + } + + requireEs_object_assign(); + + var es_object_toString = {}; + + var objectToString; + var hasRequiredObjectToString; + + function requireObjectToString () { + if (hasRequiredObjectToString) return objectToString; + hasRequiredObjectToString = 1; + var TO_STRING_TAG_SUPPORT = requireToStringTagSupport(); + var classof = requireClassof(); + + // `Object.prototype.toString` method implementation + // https://tc39.es/ecma262/#sec-object.prototype.tostring + objectToString = TO_STRING_TAG_SUPPORT ? {}.toString : function toString() { + return '[object ' + classof(this) + ']'; + }; + return objectToString; + } + + var hasRequiredEs_object_toString; + + function requireEs_object_toString () { + if (hasRequiredEs_object_toString) return es_object_toString; + hasRequiredEs_object_toString = 1; + var TO_STRING_TAG_SUPPORT = requireToStringTagSupport(); + var defineBuiltIn = requireDefineBuiltIn(); + var toString = requireObjectToString(); + + // `Object.prototype.toString` method + // https://tc39.es/ecma262/#sec-object.prototype.tostring + if (!TO_STRING_TAG_SUPPORT) { + defineBuiltIn(Object.prototype, 'toString', toString, { unsafe: true }); + } + return es_object_toString; + } + + requireEs_object_toString(); + + /** + * @author: Dustin Utecht + * @github: https://github.com/UtechtDustin + */ + + var Utils = $.fn.bootstrapTable.utils; + Object.assign($.fn.bootstrapTable.defaults, { + customView: false, + showCustomView: false, + customViewDefaultView: false + }); + Utils.assignIcons($.fn.bootstrapTable.icons, 'customViewOn', { + glyphicon: 'glyphicon-list', + fa: 'fa-list', + bi: 'bi-list', + icon: 'list', + 'material-icons': 'list' + }); + Utils.assignIcons($.fn.bootstrapTable.icons, 'customViewOff', { + glyphicon: 'glyphicon-thumbnails', + fa: 'fa-th', + bi: 'bi-grid', + icon: 'grid_on', + 'material-icons': 'grid_on' + }); + Object.assign($.fn.bootstrapTable.defaults, { + onCustomViewPostBody: function onCustomViewPostBody() { + return false; + }, + onCustomViewPreBody: function onCustomViewPreBody() { + return false; + }, + onToggleCustomView: function onToggleCustomView() { + return false; + } + }); + Object.assign($.fn.bootstrapTable.locales, { + formatToggleCustomViewOn: function formatToggleCustomViewOn() { + return 'Show custom view'; + }, + formatToggleCustomViewOff: function formatToggleCustomViewOff() { + return 'Hide custom view'; + } + }); + Object.assign($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales); + $.fn.bootstrapTable.methods.push('toggleCustomView'); + Object.assign($.fn.bootstrapTable.events, { + 'custom-view-post-body.bs.table': 'onCustomViewPostBody', + 'custom-view-pre-body.bs.table': 'onCustomViewPreBody', + 'toggle-custom-view.bs.table': 'onToggleCustomView' + }); + $.BootstrapTable = /*#__PURE__*/function (_$$BootstrapTable) { + function _class() { + _classCallCheck(this, _class); + return _callSuper(this, _class, arguments); + } + _inherits(_class, _$$BootstrapTable); + return _createClass(_class, [{ + key: "init", + value: function init() { + this.customViewDefaultView = this.options.customViewDefaultView; + _superPropGet(_class, "init", this)([]); + } + }, { + key: "initToolbar", + value: function initToolbar() { + if (this.options.customView && this.options.showCustomView) { + this.buttons = Object.assign(this.buttons, { + customView: { + text: this.options.customViewDefaultView ? this.options.formatToggleCustomViewOff() : this.options.formatToggleCustomViewOn(), + icon: this.options.customViewDefaultView ? this.options.icons.customViewOn : this.options.icons.customViewOff, + event: this.toggleCustomView, + attributes: { + 'aria-label': this.options.customViewDefaultView ? this.options.formatToggleCustomViewOff() : this.options.formatToggleCustomViewOn(), + title: this.options.customViewDefaultView ? this.options.formatToggleCustomViewOff() : this.options.formatToggleCustomViewOn() + } + } + }); + } + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + _superPropGet(_class, "initToolbar", this)(args); + } + }, { + key: "initBody", + value: function initBody() { + _superPropGet(_class, "initBody", this)([]); + if (!this.options.customView) { + return; + } + var $table = this.$el; + var $customViewContainer = this.$container.find('.fixed-table-custom-view'); + $table.hide(); + $customViewContainer.hide(); + if (!this.options.customView || !this.customViewDefaultView) { + $table.show(); + return; + } + var data = this.getData().slice(this.pageFrom - 1, this.pageTo); + var value = Utils.calculateObjectValue(this, this.options.customView, [data], ''); + this.trigger('custom-view-pre-body', data, value); + if ($customViewContainer.length === 1) { + $customViewContainer.show().html(value); + } else { + this.$tableBody.after("
".concat(value, "
")); + } + this.trigger('custom-view-post-body', data, value); + } + }, { + key: "toggleCustomView", + value: function toggleCustomView() { + this.customViewDefaultView = !this.customViewDefaultView; + var icon = this.options.showButtonIcons ? this.customViewDefaultView ? this.options.icons.customViewOn : this.options.icons.customViewOff : ''; + var text = this.options.showButtonText ? this.customViewDefaultView ? this.options.formatToggleCustomViewOff() : this.options.formatToggleCustomViewOn() : ''; + this.$toolbar.find('button[name="customView"]').html("".concat(Utils.sprintf(this.constants.html.icon, this.options.iconsPrefix, icon), " ").concat(text)).attr('aria-label', text).attr('title', text); + this.initBody(); + this.trigger('toggle-custom-view', this.customViewDefaultView); + } + }]); + }($.BootstrapTable); + +})); + jQuery.base64 = (function($) { // private property diff --git a/public/mix-manifest.json b/public/mix-manifest.json index b79a63c956fe..eac9add5c0d7 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -110,6 +110,6 @@ "/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=7b315b9612b8fde8f9c5b0ddb6bba690", "/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=54d676a6ea8677dd48f6c4b3041292cf", "/js/build/vendor.js": "/js/build/vendor.js?id=89dffa552c6e3abe3a2aac6c9c7b466b", - "/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=783d3a8076337744f0176d60e1041ea4", + "/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=03bcd02b9ca5e53e1390ee840cd0a561", "/js/dist/all.js": "/js/dist/all.js?id=8c6d7286f667eeb62a0a28a09851a6c3" } diff --git a/resources/lang/en-US/general.php b/resources/lang/en-US/general.php index d2e16aafbbc1..995e322afded 100644 --- a/resources/lang/en-US/general.php +++ b/resources/lang/en-US/general.php @@ -626,4 +626,23 @@ ], ], + 'file_upload_status' => [ + + 'upload' => [ + 'success' => 'File successfully uploaded |:count files successfully uploaded', + 'error' => 'File upload failed |:count file uploads failed', + ], + + 'delete' => [ + 'success' => 'File successfully deleted |:count files successfully deleted', + 'error' => 'File deletion failed |:count file deletions failed', + ], + + 'file_not_found' => 'The selected file was not found on server', + 'invalid_id' => 'That file ID is invalid', + 'invalid_object' => 'That object ID is invalid', + 'nofiles' => 'No files were included for upload', + 'confirm_delete' => 'Are you sure you want to delete this file?', + ], + ]; diff --git a/resources/views/accessories/view.blade.php b/resources/views/accessories/view.blade.php index e4af3d6e7887..85dffea63e83 100644 --- a/resources/views/accessories/view.blade.php +++ b/resources/views/accessories/view.blade.php @@ -154,6 +154,7 @@ class="table table-striped snipe-table" filepath="private_uploads/accessories/" showfile_routename="show.accessoryfile" deletefile_routename="delete/accessoryfile" + object_type="accessories" :object="$accessory" /> diff --git a/resources/views/blade/confirm-modal.blade.php b/resources/views/blade/confirm-modal.blade.php new file mode 100644 index 000000000000..bee7a3c03a76 --- /dev/null +++ b/resources/views/blade/confirm-modal.blade.php @@ -0,0 +1,32 @@ +{{-- IMPORTANT!!! Make sure there is no newline at the end of this file, or it will break the loaders for the tables --}} + +@props([ + 'route', + 'id', + 'method', +]) +
merge() }} id="dataConfirmModal" tabindex="-1" role="dialog" + aria-labelledby="myModalLabel" aria-hidden="true"> + +
\ No newline at end of file diff --git a/resources/views/blade/filestable.blade.php b/resources/views/blade/filestable.blade.php index 6c807984f403..fd64dda00dbf 100644 --- a/resources/views/blade/filestable.blade.php +++ b/resources/views/blade/filestable.blade.php @@ -4,139 +4,74 @@ 'object', 'showfile_routename', 'deletefile_routename', + 'object_type', ]) -
+ $object_type, 'id' => $object->id]) }}" data-export-options='{ - "fileName": "export-license-uploads-{{ str_slug($object->name) }}-{{ date('Y-m-d') }}", + "fileName": "export-uploads-{{ str_slug($object->name) }}-{{ date('Y-m-d') }}", "ignoreColumn": ["actions","image","change","checkbox","checkincheckout","delete","download","icon"] }'> +
- - - - {{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') }} - - - - - @foreach ($object->uploads as $file) - - - {{ $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(str_plural(strtolower(class_basename(get_class($object))))))) - - - - @else - {{ trans('general.preview_not_available') }} - @endif - @else - - {{ trans('general.file_not_found') }} - @endif + + - - @endforeach - - -
\ No newline at end of file diff --git a/resources/views/components/view.blade.php b/resources/views/components/view.blade.php index f090b9566563..df161205583b 100644 --- a/resources/views/components/view.blade.php +++ b/resources/views/components/view.blade.php @@ -146,6 +146,7 @@ class="table table-striped snipe-table" filepath="private_uploads/components/" showfile_routename="show.componentfile" deletefile_routename="delete/componentfile" + object_type="components" :object="$component" /> diff --git a/resources/views/consumables/view.blade.php b/resources/views/consumables/view.blade.php index 4ef82efc8e5e..db6f6bf49f31 100644 --- a/resources/views/consumables/view.blade.php +++ b/resources/views/consumables/view.blade.php @@ -437,6 +437,7 @@ class="table table-striped snipe-table" filepath="private_uploads/consumables/" showfile_routename="show.consumablefile" deletefile_routename="delete/consumablefile" + object_type="consumables" :object="$consumable" /> diff --git a/resources/views/hardware/view.blade.php b/resources/views/hardware/view.blade.php index a869f9930889..b691d2ed7153 100755 --- a/resources/views/hardware/view.blade.php +++ b/resources/views/hardware/view.blade.php @@ -1479,6 +1479,7 @@ class="table table-striped snipe-table" filepath="private_uploads/assets/" showfile_routename="show/assetfile" deletefile_routename="delete/assetfile" + object_type="assets" :object="$asset" /> @@ -1494,6 +1495,7 @@ class="table table-striped snipe-table" filepath="private_uploads/assetmodels/" showfile_routename="show/modelfile" deletefile_routename="delete/modelfile" + object_type="models" :object="$asset->model" /> diff --git a/resources/views/layouts/default.blade.php b/resources/views/layouts/default.blade.php index d83a7f0e33c9..a1fbd58a6fd8 100644 --- a/resources/views/layouts/default.blade.php +++ b/resources/views/layouts/default.blade.php @@ -962,55 +962,8 @@ - - - - - + + {{-- Javascript files --}} diff --git a/resources/views/licenses/view.blade.php b/resources/views/licenses/view.blade.php index 6d7b406df9b2..a0f2a3cc7892 100755 --- a/resources/views/licenses/view.blade.php +++ b/resources/views/licenses/view.blade.php @@ -469,6 +469,7 @@ class="table table-striped snipe-table" filepath="private_uploads/licenses/" showfile_routename="show.licensefile" deletefile_routename="delete/licensefile" + object_type="licenses" :object="$license" /> diff --git a/resources/views/locations/view.blade.php b/resources/views/locations/view.blade.php index 469a08ad1ad4..7793a2e93b36 100644 --- a/resources/views/locations/view.blade.php +++ b/resources/views/locations/view.blade.php @@ -414,6 +414,7 @@ class="table table-striped snipe-table" filepath="private_uploads/locations/" showfile_routename="show/locationsfile" deletefile_routename="delete/locationsfile" + object_type="locations" :object="$location" /> diff --git a/resources/views/models/view.blade.php b/resources/views/models/view.blade.php index f5383f0af1fa..abb0c79ad825 100755 --- a/resources/views/models/view.blade.php +++ b/resources/views/models/view.blade.php @@ -121,6 +121,7 @@ class="table table-striped snipe-table" filepath="private_uploads/assetmodels/" showfile_routename="show/modelfile" deletefile_routename="delete/modelfile" + object_type="models" :object="$model" /> diff --git a/resources/views/partials/bootstrap-table.blade.php b/resources/views/partials/bootstrap-table.blade.php index fc9e752e5115..515b8e62df09 100644 --- a/resources/views/partials/bootstrap-table.blade.php +++ b/resources/views/partials/bootstrap-table.blade.php @@ -344,6 +344,48 @@ function hardwareAuditFormatter(value, row) { return '{{ trans('general.audit') }} '; } + function deleteUploadFormatter(value, row) { + + var item_type = row.item.type; + if (item_type == 'assetmodel') { + item_type = 'models' + } + if ((row.available_actions) && (row.available_actions.delete === true)) { + return '' + + '{{ trans('general.delete') }} '; + } + } + + + // This handles the custom view for the filestable blade component + window.fileGalleryFormatter = data => { + const template = $('#fileGalleryTemplate').html() + let view = '' + + $.each(data, function (i, row) { + + view += template.replace('%ID%', row.id) + .replace('%ICON%', row.icon) + .replace('%FILETYPE%', row.filetype) + .replace('%FILE_URL%', row.url) + .replace('%LINK_URL%', row.url) + .replace('%FILENAME%', (row.exists_on_disk === true) ? row.filename : ' ' + row.filename + '') + .replace('%CREATED_AT%', row.created_at.formatted) + .replace('%CREATED_BY%', row.created_by.name) + .replace('%NOTE%', row.note) + .replace('%PANEL_CLASS%', (row.exists_on_disk === true) ? 'default' : 'danger') + .replace('%INLINE_IMAGE%', (row.inline === true) ? '' : '') + .replace('%DOWNLOAD_BUTTON%', (row.exists_on_disk === true) ? ' ' : '') + .replace('%NEW_WINDOW_BUTTON%', (row.exists_on_disk === true) ? ' ' : '') + .replace('%DELETE_BUTTON%', ((row.exists_on_disk === true) && row.available_actions.delete === true) ? ' ' : ''); + }) + + return `
${view}
` + } // Make the edit/delete buttons function genericActionsFormatter(owner_name, element_name) { @@ -378,7 +420,8 @@ function genericActionsFormatter(owner_name, element_name) { if ((row.available_actions) && (row.available_actions.update === true)) { actions += '{{ trans('general.update') }} '; } else { - if ((row.available_actions) && (row.available_actions.update != true)) { + // check that row.available_actions.update is set in the API response - if not, don't even show the button + if ((row.available_actions) && (row.available_actions.update) && (row.available_actions.update != true)) { actions += ' '; } } @@ -785,9 +828,13 @@ function dateDisplayFormatter(value) { } } - function iconFormatter(value) { + function iconFormatter(value, row) { if (value) { + if (row.filetype) { + return ''; + } return ''; + } } @@ -850,6 +897,12 @@ function auditImageFormatter(value){ } } + function inlineImageFormatter(value, row){ + if (row.inline) { + return '' + } + } + function imageFormatter(value, row) { @@ -868,25 +921,51 @@ function imageFormatter(value, row) { return '' + altName + ''; } } - function downloadFormatter(value) { - if (value) { - return ''; + + + function downloadOrOpenInNewWindowFormatter(value, row) { + + if (row && row.url) + { + if (row.exists_on_disk) { + return ' '; + } else { + return ' '; + } } + } function fileUploadFormatter(value) { if ((value) && (value.url) && (value.inlineable)) { - return ''; + return ''; } else if ((value) && (value.url)) { return ''; } } - function fileUploadNameFormatter(value) { - console.dir(value); + function fileUploadNameFormatter(row, value) { + if ((value) && (value.filename) && (value.url)) { - return '' + value.filename + ''; + if (value.exists_on_disk) { + return '' + value.filename + ''; + } else { + return ' ' + value.filename + ''; + } + + } + } + + function fileUploadDeleteFormatter(row, value) { + + if ((value) && (value.filename) && (value.url)) { + if (value.exists_on_disk) { + return '' + value.filename + ''; + } else { + return ' ' + value.filename + ''; + } + } } diff --git a/resources/views/users/view.blade.php b/resources/views/users/view.blade.php index e77ab3bd2392..4d287d24da67 100755 --- a/resources/views/users/view.blade.php +++ b/resources/views/users/view.blade.php @@ -963,6 +963,8 @@ class="table table-striped snipe-table table-hover" filepath="private_uploads/users/" showfile_routename="show/userfile" deletefile_routename="userfile.destroy" + object_type="users" + data_route="api.files.index" :object="$user" /> diff --git a/routes/api.php b/routes/api.php index 60e7c3cddf1e..56d630592e34 100644 --- a/routes/api.php +++ b/routes/api.php @@ -549,34 +549,6 @@ ] )->name('api.assets.restore'); - Route::post('{asset}/files', - [ - Api\AssetFilesController::class, - 'store' - ] - )->name('api.assets.files.store'); - - Route::get('{asset}/files', - [ - Api\AssetFilesController::class, - 'list' - ] - )->name('api.assets.files.index'); - - Route::get('{asset_id}/file/{file_id}', - [ - Api\AssetFilesController::class, - 'show' - ] - )->name('api.assets.files.show'); - - Route::delete('{asset_id}/file/{file_id}', - [ - Api\AssetFilesController::class, - 'destroy' - ] - )->name('api.assets.files.destroy'); - /** Begin assigned routes */ @@ -767,6 +739,8 @@ ] )->name('api.locations.assigned_accessories'); /** End assigned routes */ + + }); Route::resource('locations', @@ -846,33 +820,6 @@ ] )->name('api.models.restore'); - Route::post('{model_id}/files', - [ - Api\AssetModelFilesController::class, - 'store' - ] - )->name('api.models.files.store'); - - Route::get('{model_id}/files', - [ - Api\AssetModelFilesController::class, - 'list' - ] - )->name('api.models.files.index'); - - Route::get('{model_id}/file/{file_id}', - [ - Api\AssetModelFilesController::class, - 'show' - ] - )->name('api.models.files.show'); - - Route::delete('{model_id}/file/{file_id}', - [ - Api\AssetModelFilesController::class, - 'destroy' - ] - )->name('api.models.files.destroy'); }); Route::resource('models', @@ -1129,12 +1076,6 @@ ] )->name('api.users.licenselist'); - Route::post('{user}/upload', - [ - Api\UsersController::class, - 'postUpload' - ] - )->name('api.users.uploads'); Route::post('{user}/restore', [ @@ -1143,6 +1084,8 @@ ] )->name('api.users.restore'); + + }); Route::resource('users', @@ -1347,4 +1290,46 @@ // end generate label routes + + /** + * Uploaded files API routes + */ + + // List files + Route::get('{object_type}/{id}/files', + [ + Api\UploadedFilesController::class, + 'index' + ] + )->name('api.files.index') + ->where(['object_type' => 'assets|models|users|locations|accessories|consumables|licenses|components']); + + // Get a file + Route::get('{object_type}/{id}/files/{file_id}', + [ + Api\UploadedFilesController::class, + 'show' + ] + )->name('api.files.show') + ->where(['object_type' => 'assets|models|users|locations|accessories|consumables|licenses|components']); + + // Upload files(s) + Route::post('{object_type}/{id}/files', + [ + Api\UploadedFilesController::class, + 'store' + ] + )->name('api.files.store') + ->where(['object_type' => 'assets|models|users|locations|accessories|consumables|licenses|components']); + + // Delete files(s) + Route::delete('{object_type}/{id}/files/{file_id}/delete', + [ + Api\UploadedFilesController::class, + 'destroy' + ] + )->name('api.files.destroy') + ->where(['object_type' => 'assets|models|users|locations|accessories|consumables|licenses|components']); + + }); // end API routes diff --git a/routes/web/hardware.php b/routes/web/hardware.php index 21f4f40d4148..b0bc5a3bfe76 100644 --- a/routes/web/hardware.php +++ b/routes/web/hardware.php @@ -5,7 +5,6 @@ use App\Http\Controllers\Assets\BulkAssetsController; use App\Http\Controllers\Assets\AssetCheckoutController; use App\Http\Controllers\Assets\AssetCheckinController; -use App\Http\Controllers\Assets\AssetFilesController; use App\Models\Setting; use Tabuna\Breadcrumbs\Trail; use Illuminate\Support\Facades\Route; @@ -141,17 +140,6 @@ function () { [AssetsController::class, 'getRestore'] )->name('restore/hardware')->withTrashed(); - Route::post('{asset}/upload', - [AssetFilesController::class, 'store'] - )->name('upload/asset')->withTrashed(); - - Route::get('{asset}/showfile/{fileId}/{download?}', - [AssetFilesController::class, 'show'] - )->name('show/assetfile')->withTrashed(); - - Route::delete('{asset}/showfile/{fileId}/delete', - [AssetFilesController::class, 'destroy'] - )->name('delete/assetfile')->withTrashed(); Route::post( 'bulkedit', diff --git a/routes/web/models.php b/routes/web/models.php index 8c003ba66227..e3262ab502c4 100644 --- a/routes/web/models.php +++ b/routes/web/models.php @@ -11,6 +11,10 @@ Route::group(['prefix' => 'models', 'middleware' => ['auth']], function () { + Route::delete('{model}/showfile/{fileId}/delete', + [AssetModelsFilesController::class, 'destroy'] + )->name('delete/modelfile')->withTrashed(); + Route::post('{model}/upload', [AssetModelsFilesController::class, 'store'] )->name('upload/models')->withTrashed(); @@ -19,9 +23,7 @@ [AssetModelsFilesController::class, 'show'] )->name('show/modelfile')->withTrashed(); - Route::delete('{model}/showfile/{fileId}/delete', - [AssetModelsFilesController::class, 'destroy'] - )->name('delete/modelfile')->withTrashed(); + Route::get( '{model}/clone', diff --git a/tests/Feature/AssetModels/Api/AssetModelFilesTest.php b/tests/Feature/AssetModels/Api/AssetModelFilesTest.php index d29ea4f002ee..b9dee9b4ab54 100644 --- a/tests/Feature/AssetModels/Api/AssetModelFilesTest.php +++ b/tests/Feature/AssetModels/Api/AssetModelFilesTest.php @@ -14,17 +14,17 @@ public function testAssetModelApiAcceptsFileUpload() // Upload a file to a model // Create a model to work with - $model = AssetModel::factory()->count(1)->create(); + $model = AssetModel::factory()->create(); - // Create a superuser to run this as - $user = User::factory()->superuser()->create(); + // Create a superuser to run this as + $user = User::factory()->superuser()->create(); - //Upload a file - $this->actingAsForApi($user) - ->post( - route('api.models.files.store', ['model_id' => $model[0]["id"]]), [ - 'file' => [UploadedFile::fake()->create("test.jpg", 100)] - ]) + //Upload a file + $this->actingAsForApi($user) + ->post( + route('api.files.store', ['object_type' => 'models', 'id' => $model->id]), [ + 'file' => [UploadedFile::fake()->create("test.jpg", 100)] + ]) ->assertOk(); } @@ -33,122 +33,156 @@ public function testAssetModelApiListsFiles() // List all files on a model // Create an model to work with - $model = AssetModel::factory()->count(1)->create(); + $model = AssetModel::factory()->create(); + + // Create a superuser to run this as + $user = User::factory()->superuser()->create(); + + // List the files + $this->actingAsForApi($user) + ->getJson( + route('api.files.index', ['object_type' => 'models', 'id' => $model->id])) + ->assertOk() + ->assertJsonStructure([ + 'rows', + 'total', + ]); + } + + public function testAssetModelFailsIfInvalidTypePassedInUrl() + { + // List all files on a model + + // Create an model to work with + $model = AssetModel::factory()->create(); - // Create a superuser to run this as - $user = User::factory()->superuser()->create(); + // Create a superuser to run this as + $user = User::factory()->superuser()->create(); - // List the files - $this->actingAsForApi($user) + // List the files + $this->actingAsForApi($user) ->getJson( - route('api.models.files.index', ['model_id' => $model[0]["id"]])) - ->assertOk() - ->assertJsonStructure([ - 'rows', - 'total', - ]); + route('api.files.index', ['object_type' => 'shibboleeeeeet', 'id' => $model->id])) + ->assertStatus(404); } - public function testAssetModelApiDownloadsFile() + public function testAssetModelFailsIfInvalidIdPassedInUrl() { - // Download a file from a model - - // Create a model to work with - $model = AssetModel::factory()->count(1)->create(); + // List all files on a model - // Create a superuser to run this as - $user = User::factory()->superuser()->create(); + // Create an model to work with + $model = AssetModel::factory()->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() - ->assertJsonStructure([ - 'status', - 'messages', - ]); - - // Upload a file with notes - $this->actingAsForApi($user) - ->post( - route('api.models.files.store', ['model_id' => $model[0]["id"]]), [ - 'file' => [UploadedFile::fake()->create("test.jpg", 100)], - 'notes' => 'manual' - ]) - ->assertOk() - ->assertJsonStructure([ - 'status', - 'messages', - ]); + // Create a superuser to run this as + $user = User::factory()->superuser()->create(); - // List the files to get the file ID - $result = $this->actingAsForApi($user) + // List the files + $this->actingAsForApi($user) ->getJson( - route('api.models.files.index', ['model_id' => $model[0]["id"]])) + route('api.files.index', ['object_type' => 'models', 'id' => 100000])) ->assertOk() - ->assertJsonStructure([ - 'total', - 'rows'=>[ - '*' => [ - 'id', - 'filename', - 'url', - 'created_by', - 'created_at', - 'updated_at', - 'deleted_at', - 'note', - 'available_actions' - ] - ] - ]) - ->assertJsonPath('rows.0.note','') - ->assertJsonPath('rows.1.note','manual'); - - - - // Get the file - $this->actingAsForApi($user) - ->get( - route('api.models.files.show', [ - 'model_id' => $model[0]["id"], - 'file_id' => $result->decodeResponseJson()->json()["rows"][0]["id"], - ])) - ->assertOk(); + ->assertStatusMessageIs('error'); } - public function testAssetModelApiDeletesFile() + public function testAssetModelApiDownloadsFile() { - // Delete a file from a model + // Download a file from a model // Create a model to work with - $model = AssetModel::factory()->count(1)->create(); + $model = AssetModel::factory()->create(); - // Create a superuser to run this as - $user = User::factory()->superuser()->create(); + // Create a superuser to run this as + $user = User::factory()->superuser()->create(); - //Upload a file - $this->actingAsForApi($user) - ->post( - route('api.models.files.store', ['model_id' => $model[0]["id"]]), [ - 'file' => [UploadedFile::fake()->create("test.jpg", 100)] - ]) - ->assertOk(); + // Upload a file + $this->actingAsForApi($user) + ->post( + route('api.files.store', ['object_type' => 'models', 'id' => $model->id]), [ + 'file' => [UploadedFile::fake()->create("test.jpg", 100)], + ]) + ->assertOk() + ->assertJsonStructure([ + 'status', + 'messages', + ]); - // List the files to get the file ID - $result = $this->actingAsForApi($user) - ->getJson( - route('api.models.files.index', ['model_id' => $model[0]["id"]])) - ->assertOk(); + // Upload a file with notes + $this->actingAsForApi($user) + ->post( + route('api.files.store', ['object_type' => 'models', 'id' => $model->id]), [ + 'file' => [UploadedFile::fake()->create("test.jpg", 100)], + 'notes' => 'manual' + ]) + ->assertOk() + ->assertJsonStructure([ + 'status', + 'messages', + ]); + + // List the files to get the file ID + $result = $this->actingAsForApi($user) + ->getJson( + route('api.files.index', ['object_type' => 'models', 'id' => $model->id])) + ->assertOk() + ->assertJsonStructure([ + 'total', + 'rows'=>[ + '*' => [ + 'id', + 'filename', + 'url', + 'created_by', + 'created_at', + 'deleted_at', + 'note', + 'available_actions' + ] + ] + ]) + ->assertJsonPath('rows.0.note',null) + ->assertJsonPath('rows.1.note','manual'); + + // Get the file + $this->actingAsForApi($user) + ->get( + route('api.files.show', [ + 'object_type' => 'models', + 'id' => $model->id, + 'file_id' => $result->decodeResponseJson()->json()["rows"][0]["id"], + ])) + ->assertOk(); + } - // Delete the file - $this->actingAsForApi($user) + public function testAssetModelApiDeletesFile() + { + // Delete a file from a model + + // Create a model to work with + $model = AssetModel::factory()->create(); + + // Create a superuser to run this as + $user = User::factory()->superuser()->create(); + + //Upload a file + $this->actingAsForApi($user) + ->post( + route('api.files.store', ['object_type' => 'models', 'id' => $model->id]), [ + 'file' => [UploadedFile::fake()->create("test.jpg", 100)] + ]) + ->assertOk(); + + // List the files to get the file ID + $result = $this->actingAsForApi($user) + ->getJson( + route('api.files.index', ['object_type' => 'models', 'id' => $model->id])) + ->assertOk(); + + // Delete the file + $this->actingAsForApi($user) ->delete( - route('api.models.files.destroy', [ - 'model_id' => $model[0]["id"], + route('api.files.destroy', [ + 'object_type' => 'models', + 'id' => $model->id, 'file_id' => $result->decodeResponseJson()->json()["rows"][0]["id"], ])) ->assertOk() diff --git a/tests/Feature/Assets/Api/AssetFilesTest.php b/tests/Feature/Assets/Api/AssetFilesTest.php index 3e3e576c25cf..d0069517177a 100644 --- a/tests/Feature/Assets/Api/AssetFilesTest.php +++ b/tests/Feature/Assets/Api/AssetFilesTest.php @@ -14,7 +14,7 @@ public function testAssetApiAcceptsFileUpload() // Upload a file to an asset // Create an asset to work with - $asset = Asset::factory()->count(1)->create(); + $asset = Asset::factory()->create(); // Create a superuser to run this as $user = User::factory()->superuser()->create(); @@ -22,7 +22,7 @@ public function testAssetApiAcceptsFileUpload() //Upload a file $this->actingAsForApi($user) ->post( - route('api.assets.files.store', $asset), [ + route('api.files.store', ['object_type' => 'assets', 'id' => $asset->id]), [ 'file' => [UploadedFile::fake()->create("test.jpg", 100)] ]) ->assertOk(); @@ -33,14 +33,14 @@ public function testAssetApiListsFiles() // List all files on an asset // Create an asset to work with - $asset = Asset::factory()->count(1)->create(); + $asset = Asset::factory()->create(); // Create a superuser to run this as $user = User::factory()->superuser()->create(); // List the files $this->actingAsForApi($user) - ->getJson(route('api.assets.files.index', $asset)) + ->getJson(route('api.files.index', ['object_type' => 'assets', 'id' => $asset->id])) ->assertOk() ->assertJsonStructure([ 'rows', @@ -53,21 +53,21 @@ public function testAssetApiDownloadsFile() // Download a file from an asset // Create an asset to work with - $asset = Asset::factory()->count(1)->create(); + $asset = Asset::factory()->create(); // Create a superuser to run this as $user = User::factory()->superuser()->create(); //Upload a file $this->actingAsForApi($user) - ->post(route('api.assets.files.store', $asset), [ + ->post(route('api.files.store', ['object_type' => 'assets', 'id' => $asset->id]), [ 'file' => [UploadedFile::fake()->create("test.jpg", 100)] ]) ->assertOk(); // List the files to get the file ID $result = $this->actingAsForApi($user) - ->getJson(route('api.assets.files.index', $asset)) + ->getJson(route('api.files.index', ['object_type' => 'assets', 'id' => $asset->id])) ->assertOk(); } @@ -76,7 +76,7 @@ public function testAssetApiDeletesFile() // Delete a file from an asset // Create an asset to work with - $asset = Asset::factory()->count(1)->create(); + $asset = Asset::factory()->create(); // Create a superuser to run this as $user = User::factory()->superuser()->create(); @@ -84,7 +84,7 @@ public function testAssetApiDeletesFile() //Upload a file $this->actingAsForApi($user) ->post( - route('api.assets.files.store', $asset), [ + route('api.files.store', ['object_type' => 'assets', 'id' => $asset->id]), [ 'file' => [UploadedFile::fake()->create("test.jpg", 100)] ]) ->assertOk(); @@ -92,7 +92,7 @@ public function testAssetApiDeletesFile() // List the files to get the file ID $result = $this->actingAsForApi($user) ->getJson( - route('api.assets.files.index', $asset)) + route('api.files.index', ['object_type' => 'assets', 'id' => $asset->id])) ->assertOk(); } diff --git a/webpack.mix.js b/webpack.mix.js index f4e0e69d5d71..d3cef9c0dfbf 100644 --- a/webpack.mix.js +++ b/webpack.mix.js @@ -128,7 +128,7 @@ mix.combine( "./node_modules/ekko-lightbox/dist/ekko-lightbox.js", "./resources/assets/js/extensions/pGenerator.jquery.js", "./node_modules/chart.js/dist/Chart.js", - "./resources/assets/js/signature_pad.js", //dupe? + "./resources/assets/js/signature_pad.js", //dupe? "./node_modules/jquery-validation/dist/jquery.validate.js", "./node_modules/list.js/dist/list.js", "./node_modules/clipboard/dist/clipboard.js", @@ -149,6 +149,7 @@ mix './node_modules/bootstrap-table/dist/extensions/cookie/bootstrap-table-cookie.js', './node_modules/bootstrap-table/dist/extensions/sticky-header/bootstrap-table-sticky-header.js', './node_modules/bootstrap-table/dist/extensions/addrbar/bootstrap-table-addrbar.js', + './node_modules/bootstrap-table/dist/extensions/custom-view/bootstrap-table-custom-view.js', './resources/assets/js/extensions/jquery.base64.js', './node_modules/tableexport.jquery.plugin/tableExport.min.js', './node_modules/tableexport.jquery.plugin/libs/jsPDF/jspdf.umd.min.js',