diff --git a/app/Http/Controllers/Assets/BulkAssetsController.php b/app/Http/Controllers/Assets/BulkAssetsController.php index 8419098936..a744db5788 100644 --- a/app/Http/Controllers/Assets/BulkAssetsController.php +++ b/app/Http/Controllers/Assets/BulkAssetsController.php @@ -14,6 +14,7 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Session; use App\Http\Requests\AssetCheckoutRequest; +use App\Models\CustomField; class BulkAssetsController extends Controller { @@ -31,7 +32,7 @@ class BulkAssetsController extends Controller public function edit(Request $request) { $this->authorize('view', Asset::class); - + if (! $request->filled('ids')) { return redirect()->back()->with('error', trans('admin/hardware/message.update.no_assets_selected')); } @@ -41,6 +42,17 @@ class BulkAssetsController extends Controller session(['bulk_back_url' => $bulk_back_url]); $asset_ids = array_values(array_unique($request->input('ids'))); + + //custom fields logic + $asset_custom_field = Asset::with(['model.fieldset.fields', 'model'])->whereIn('id', $asset_ids)->whereHas('model', function ($query) { + return $query->where('fieldset_id', '!=', null); + })->get(); + + $models = $asset_custom_field->unique('model_id'); + $modelNames = []; + foreach($models as $model) { + $modelNames[] = $model->model->name; + } if ($request->filled('bulk_actions')) { switch ($request->input('bulk_actions')) { @@ -74,7 +86,9 @@ class BulkAssetsController extends Controller $this->authorize('update', Asset::class); return view('hardware/bulk') ->with('assets', $asset_ids) - ->with('statuslabel_list', Helper::statusLabelList()); + ->with('statuslabel_list', Helper::statusLabelList()) + ->with('models', $models->pluck(['model'])) + ->with('modelNames', $modelNames); } } @@ -92,6 +106,7 @@ class BulkAssetsController extends Controller public function update(Request $request) { $this->authorize('update', Asset::class); + $error_bag = []; // Get the back url from the session and then destroy the session $bulk_back_url = route('hardware.index'); @@ -100,12 +115,21 @@ class BulkAssetsController extends Controller } - if (! $request->filled('ids') || count($request->input('ids')) <= 0) { + $custom_field_columns = CustomField::all()->pluck('db_column')->toArray(); + + if(Session::exists('ids')) { + $assets = Session::get('ids'); + } elseif (! $request->filled('ids') || count($request->input('ids')) <= 0) { return redirect($bulk_back_url)->with('error', trans('admin/hardware/message.update.no_assets_selected')); } - + $assets = array_keys($request->input('ids')); - + + if ($request->anyFilled($custom_field_columns)) { + $custom_fields_present = true; + } else { + $custom_fields_present = false; + } if (($request->filled('purchase_date')) || ($request->filled('expected_checkin')) || ($request->filled('purchase_cost')) @@ -121,6 +145,7 @@ class BulkAssetsController extends Controller || ($request->filled('null_purchase_date')) || ($request->filled('null_expected_checkin_date')) || ($request->filled('null_next_audit_date')) + || ($request->anyFilled($custom_field_columns)) ) { foreach ($assets as $assetId) { @@ -136,6 +161,9 @@ class BulkAssetsController extends Controller ->conditionallyAddItem('supplier_id') ->conditionallyAddItem('warranty_months') ->conditionallyAddItem('next_audit_date'); + foreach ($custom_field_columns as $key => $custom_field_column) { + $this->conditionallyAddItem($custom_field_column); + } if ($request->input('null_purchase_date')=='1') { $this->update_array['purchase_date'] = null; @@ -168,11 +196,11 @@ class BulkAssetsController extends Controller } $changed = []; - $asset = Asset::where('id' ,$assetId)->get(); + $assetCollection = Asset::where('id' ,$assetId)->get(); foreach ($this->update_array as $key => $value) { - if ($this->update_array[$key] != $asset->toArray()[0][$key]) { - $changed[$key]['old'] = $asset->toArray()[0][$key]; + if ($this->update_array[$key] != $assetCollection->toArray()[0][$key]) { + $changed[$key]['old'] = $assetCollection->toArray()[0][$key]; $changed[$key]['new'] = $this->update_array[$key]; } } @@ -184,17 +212,47 @@ class BulkAssetsController extends Controller $logAction->user_id = Auth::id(); $logAction->log_meta = json_encode($changed); $logAction->logaction('update'); - - DB::table('assets') - ->where('id', $assetId) - ->update($this->update_array); - } // endforeach - + + if($custom_fields_present) { + $asset = Asset::find($assetId); + $assetCustomFields = $asset->model()->first()->fieldset; + if($assetCustomFields && $assetCustomFields->fields) { + foreach ($assetCustomFields->fields as $field) { + if (array_key_exists($field->db_column, $this->update_array)) { + $asset->{$field->db_column} = $this->update_array[$field->db_column]; + $saved = $asset->save(); + if(!$saved) { + $error_bag[] = $asset->getErrors(); + } + continue; + } else { + $array = $this->update_array; + array_except($array, $field->db_column); + $asset->save($array); + } + if (!$asset->save()) { + $error_bag[] = $asset->getErrors(); + } + } + } + } else { + Asset::find($assetId)->update($this->update_array); + } + } + if(!empty($error_bag)) { + $errors = []; + //find the customfield name from the name of the messagebag items + foreach ($error_bag as $key => $bag) { + foreach($bag->keys() as $key => $value) { + CustomField::where('db_column', $value)->get()->map(function($item) use (&$errors) { + $errors[] = $item->name; + }); + } + } + return redirect($bulk_back_url)->with('bulk_errors', array_unique($errors)); + } return redirect($bulk_back_url)->with('success', trans('admin/hardware/message.update.success')); - - } - // no values given, nothing to update return redirect($bulk_back_url)->with('warning', trans('admin/hardware/message.update.nothing_updated')); } diff --git a/app/Http/Transformers/ActionlogsTransformer.php b/app/Http/Transformers/ActionlogsTransformer.php index 7b7a7fc011..2a592e9c83 100644 --- a/app/Http/Transformers/ActionlogsTransformer.php +++ b/app/Http/Transformers/ActionlogsTransformer.php @@ -4,6 +4,10 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\Actionlog; use App\Models\Setting; +use App\Models\Company; +use App\Models\Supplier; +use App\Models\Location; +use App\Models\AssetModel; use Illuminate\Database\Eloquent\Collection; class ActionlogsTransformer @@ -53,6 +57,7 @@ class ActionlogsTransformer } } + $clean_meta= $this->changedInfo($clean_meta); } $file_url = ''; @@ -132,6 +137,49 @@ class ActionlogsTransformer } return (new DatatablesTransformer)->transformDatatables($array, $total); } + /** + * This takes the ids of the changed attributes and returns the names instead for the history view of an Asset + * + * @param array $clean_meta + * @return array + */ + public function changedInfo(array $clean_meta) + { + + if(array_key_exists('rtd_location_id',$clean_meta)) { + $clean_meta['rtd_location_id']['old'] = $clean_meta['rtd_location_id']['old'] ? "[id: ".$clean_meta['rtd_location_id']['old']."] ". Location::find($clean_meta['rtd_location_id']['old'])->name : trans('general.unassigned'); + $clean_meta['rtd_location_id']['new'] = $clean_meta['rtd_location_id']['new'] ? "[id: ".$clean_meta['rtd_location_id']['new']."] ". Location::find($clean_meta['rtd_location_id']['new'])->name : trans('general.unassigned'); + $clean_meta['Default Location'] = $clean_meta['rtd_location_id']; + unset($clean_meta['rtd_location_id']); + } + if(array_key_exists('location_id', $clean_meta)) { + $clean_meta['location_id']['old'] = $clean_meta['location_id']['old'] ? "[id: ".$clean_meta['location_id']['old']."] ".Location::find($clean_meta['location_id']['old'])->name : trans('general.unassigned'); + $clean_meta['location_id']['new'] = $clean_meta['location_id']['new'] ? "[id: ".$clean_meta['location_id']['new']."] ".Location::find($clean_meta['location_id']['new'])->name : trans('general.unassigned'); + $clean_meta['Current Location'] = $clean_meta['location_id']; + unset($clean_meta['location_id']); + } + if(array_key_exists('model_id', $clean_meta)) { + $clean_meta['model_id']['old'] = "[id: ".$clean_meta['model_id']['old']."] ".AssetModel::find($clean_meta['model_id']['old'])->name; + $clean_meta['model_id']['new'] = "[id: ".$clean_meta['model_id']['new']."] ".AssetModel::find($clean_meta['model_id']['new'])->name; /* model is required at asset creation */ + $clean_meta['Model'] = $clean_meta['model_id']; + unset($clean_meta['model_id']); + } + if(array_key_exists('company_id', $clean_meta)) { + $clean_meta['company_id']['old'] = $clean_meta['company_id']['old'] ? "[id: ".$clean_meta['company_id']['old']."]".Company::find($clean_meta['company_id']['old'])->name : trans('general.unassigned'); + $clean_meta['company_id']['new'] = $clean_meta['company_id']['new'] ? "[id: ".$clean_meta['company_id']['new']."] ".Company::find($clean_meta['company_id']['new'])->name : trans('general.unassigned'); + $clean_meta['Company'] = $clean_meta['company_id']; + unset($clean_meta['company_id']); + } + if(array_key_exists('supplier_id', $clean_meta)) { + $clean_meta['supplier_id']['old'] = $clean_meta['supplier_id']['old'] ? "[id: ".$clean_meta['supplier_id']['old']."] ".Supplier::find($clean_meta['supplier_id']['old'])->name : trans('general.unassigned'); + $clean_meta['supplier_id']['new'] = $clean_meta['supplier_id']['new'] ? "[id: ".$clean_meta['supplier_id']['new']."] ".Supplier::find($clean_meta['supplier_id']['new'])->name : trans('general.unassigned'); + $clean_meta['Supplier'] = $clean_meta['supplier_id']; + unset($clean_meta['supplier_id']); + } + + return $clean_meta; + + } diff --git a/app/Models/AssetModel.php b/app/Models/AssetModel.php index e4e5ac720a..aed1b925ef 100755 --- a/app/Models/AssetModel.php +++ b/app/Models/AssetModel.php @@ -150,6 +150,11 @@ class AssetModel extends SnipeModel { return $this->belongsTo(\App\Models\CustomFieldset::class, 'fieldset_id'); } + + public function customFields() + { + return $this->fieldset()->first()->fields(); + } /** * Establishes the model -> custom field default values relationship diff --git a/app/Models/CustomField.php b/app/Models/CustomField.php index e7fddd1aad..c98dbe637c 100644 --- a/app/Models/CustomField.php +++ b/app/Models/CustomField.php @@ -9,7 +9,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Validation\Rule; use Schema; use Watson\Validating\ValidatingTrait; - class CustomField extends Model { use HasFactory; @@ -182,6 +181,11 @@ class CustomField extends Model { return $this->belongsToMany(\App\Models\CustomFieldset::class); } + + public function assetModels() + { + return $this->fieldset()->with('models')->get()->pluck('models')->flatten()->unique('id'); + } /** * Establishes the customfield -> admin user relationship diff --git a/composer.lock b/composer.lock index 6221937a3d..54d3e556b6 100644 --- a/composer.lock +++ b/composer.lock @@ -2948,16 +2948,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.4.0", + "version": "2.4.5", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "13388f00956b1503577598873fffb5ae994b5737" + "reference": "0454e12ef0cd597ccd2adb036f7bda4e7fface66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/13388f00956b1503577598873fffb5ae994b5737", - "reference": "13388f00956b1503577598873fffb5ae994b5737", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/0454e12ef0cd597ccd2adb036f7bda4e7fface66", + "reference": "0454e12ef0cd597ccd2adb036f7bda4e7fface66", "shasum": "" }, "require": { @@ -2971,17 +2971,18 @@ "psr/http-message-implementation": "1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", + "bamarni/composer-bin-plugin": "^1.8.1", "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.8 || ^9.3.10" + "phpunit/phpunit": "^8.5.29 || ^9.5.23" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "2.4-dev" + "bamarni-bin": { + "bin-links": true, + "forward-command": false } }, "autoload": { @@ -3043,7 +3044,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.4.0" + "source": "https://github.com/guzzle/psr7/tree/2.4.5" }, "funding": [ { @@ -3059,7 +3060,7 @@ "type": "tidelift" } ], - "time": "2022-06-20T21:43:11+00:00" + "time": "2023-04-17T16:00:45+00:00" }, { "name": "intervention/image", @@ -6130,16 +6131,16 @@ }, { "name": "nyholm/psr7", - "version": "1.5.1", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/Nyholm/psr7.git", - "reference": "f734364e38a876a23be4d906a2a089e1315be18a" + "reference": "e874c8c4286a1e010fb4f385f3a55ac56a05cc93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Nyholm/psr7/zipball/f734364e38a876a23be4d906a2a089e1315be18a", - "reference": "f734364e38a876a23be4d906a2a089e1315be18a", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/e874c8c4286a1e010fb4f385f3a55ac56a05cc93", + "reference": "e874c8c4286a1e010fb4f385f3a55ac56a05cc93", "shasum": "" }, "require": { @@ -6149,6 +6150,7 @@ "psr/http-message": "^1.0" }, "provide": { + "php-http/message-factory-implementation": "1.0", "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, @@ -6161,7 +6163,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.6-dev" } }, "autoload": { @@ -6191,7 +6193,7 @@ ], "support": { "issues": "https://github.com/Nyholm/psr7/issues", - "source": "https://github.com/Nyholm/psr7/tree/1.5.1" + "source": "https://github.com/Nyholm/psr7/tree/1.6.1" }, "funding": [ { @@ -6203,7 +6205,7 @@ "type": "github" } ], - "time": "2022-06-22T07:13:36+00:00" + "time": "2023-04-17T16:03:48+00:00" }, { "name": "onelogin/php-saml", @@ -16736,5 +16738,5 @@ "ext-pdo": "*" }, "platform-dev": [], - "plugin-api-version": "2.0.0" + "plugin-api-version": "2.3.0" } diff --git a/resources/lang/en/admin/hardware/form.php b/resources/lang/en/admin/hardware/form.php index 6bcb884bab..ef877c8377 100644 --- a/resources/lang/en/admin/hardware/form.php +++ b/resources/lang/en/admin/hardware/form.php @@ -10,6 +10,9 @@ return [ 'bulk_update' => 'Bulk Update Assets', 'bulk_update_help' => 'This form allows you to update multiple assets at once. Only fill in the fields you need to change. Any fields left blank will remain unchanged. ', 'bulk_update_warn' => 'You are about to edit the properties of a single asset.|You are about to edit the properties of :asset_count assets.', + 'bulk_update_with_custom_field' => 'Note the assets are :asset_model_count different types of models.', + 'bulk_update_model_prefix' => 'On Models', + 'bulk_update_custom_field_unique' => 'This is a unique field and can not be bulk edited.', 'checkedout_to' => 'Checked Out To', 'checkout_date' => 'Checkout Date', 'checkin_date' => 'Checkin Date', diff --git a/resources/lang/en/general.php b/resources/lang/en/general.php index 4831845721..9a78092179 100644 --- a/resources/lang/en/general.php +++ b/resources/lang/en/general.php @@ -283,6 +283,7 @@ return [ 'user' => 'User', 'accepted' => 'accepted', 'declined' => 'declined', + 'unassigned' => 'Unassigned', 'unaccepted_asset_report' => 'Unaccepted Assets', 'users' => 'Users', 'viewall' => 'View All', @@ -364,6 +365,7 @@ return [ 'licenses_count' => 'Licenses Count', 'notification_error' => 'Error:', 'notification_error_hint' => 'Please check the form below for errors', + 'notification_bulk_error_hint' => 'The following fields had validation errors and were not edited:', 'notification_success' => 'Success:', 'notification_warning' => 'Warning:', 'notification_info' => 'Info:', diff --git a/resources/views/hardware/bulk.blade.php b/resources/views/hardware/bulk.blade.php index c2fe105a2d..667126ec99 100755 --- a/resources/views/hardware/bulk.blade.php +++ b/resources/views/hardware/bulk.blade.php @@ -21,6 +21,9 @@
{{ trans_choice('admin/hardware/form.bulk_update_warn', count($assets), ['asset_count' => count($assets)]) }} + @if (count($models) > 0) + {{ trans_choice('admin/hardware/form.bulk_update_with_custom_field', count($models), ['asset_model_count' => count($models)]) }} + @endif
@@ -182,6 +185,8 @@ + @include("models/custom_fields_form_bulk_edit",["models" => $models]) + @foreach ($assets as $key => $value) @endforeach diff --git a/resources/views/models/custom_fields_form_bulk_edit.blade.php b/resources/views/models/custom_fields_form_bulk_edit.blade.php new file mode 100644 index 0000000000..e974c3f6eb --- /dev/null +++ b/resources/views/models/custom_fields_form_bulk_edit.blade.php @@ -0,0 +1,119 @@ +@php +//set array up before loop so it doesn't get wiped at every iteration + $fields = []; +@endphp +@foreach($models as $model) +@if (($model) && ($model->fieldset)) + @foreach($model->fieldset->fields AS $field) + @php + //prevents some duplicate queries - open to a better way of skipping dupes in output + //its ugly, but if we'd rather deal with duplicate queries we can get rid of this. + if (in_array($field->db_column_name(), $fields)) { + $duplicate = true; + continue; + } else { + $duplicate = false; + } + $fields[] = $field->db_column_name(); + @endphp + +
+ +
+ + @if ($field->element!='text') + + @if ($field->element=='listbox') + {{ Form::select($field->db_column_name(), $field->formatFieldValuesAsArray(), + Request::old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, htmlspecialchars($item->{$field->db_column_name()}, ENT_QUOTES)) : $field->defaultValue($model->id))), ['class'=>'format select2 form-control']) }} + + @elseif ($field->element=='textarea') + @if($field->is_unique) + + @endif + @if(!$field->is_unique) + + @endif + @elseif ($field->element=='checkbox') + + @foreach ($field->formatFieldValuesAsArray() as $key => $value) +
+ +
+ @endforeach + @elseif ($field->element=='radio') + @foreach ($field->formatFieldValuesAsArray() as $value) +
+ +
+ @endforeach + + @endif + + @else + + + @if ($field->format=='DATE') + +
+
+ + +
+
+ + + @else + + @if (($field->field_encrypted=='0') || (Gate::allows('admin'))) + @if ($field->is_unique) + + @endif + @if(!$field->is_unique) + + @endif + @else + + @endif + + @endif + + @endif + + @if ($field->help_text!='') +

{{ $field->help_text }}

+ @endif + +

{{ trans('admin/hardware/form.bulk_update_model_prefix') }}: + {{$field->assetModels()->pluck('name')->intersect($modelNames)->implode(', ')}} +

+ + + + + first($field->db_column_name()); + if ($errormessage) { + $errormessage=preg_replace('/ snipeit /', '', $errormessage); + print(''); + } + ?> +
+ + @if ($field->field_encrypted) +
+ +
+ @endif + + +
+ @endforeach +@endif + @endforeach diff --git a/resources/views/notifications.blade.php b/resources/views/notifications.blade.php index 0935c28582..ca7945d563 100755 --- a/resources/views/notifications.blade.php +++ b/resources/views/notifications.blade.php @@ -115,6 +115,23 @@ @endif +@if ($messages = Session::get('bulk_errors')) +
+
+ + + {{ trans('general.notification_error') }} + {{ trans('general.notification_bulk_error_hint') }} + @foreach($messages as $message) +
    +
  • {{ $message }}
  • +
+ @endforeach +
+
+@endif + + @if ($message = Session::get('warning'))