diff --git a/app/Helpers/CustomFieldHelper.php b/app/Helpers/CustomFieldHelper.php new file mode 100644 index 000000000000..b6b5a5b71915 --- /dev/null +++ b/app/Helpers/CustomFieldHelper.php @@ -0,0 +1,77 @@ +fields->count() > 0)) { + $fields_array = []; + + foreach ($fieldset->fields as $field) { + if ($field->isFieldDecryptable($item->{$field->db_column})) { + $decrypted = Helper::gracefulDecrypt($field, $item->{$field->db_column}); + $value = (Gate::allows('assets.view.encrypted_custom_fields')) ? $decrypted : strtoupper(trans('admin/custom_fields/general.encrypted')); + + if ($field->format == 'DATE'){ + if (Gate::allows('assets.view.encrypted_custom_fields')){ + $value = Helper::getFormattedDateObject($value, 'date', false); + } else { + $value = strtoupper(trans('admin/custom_fields/general.encrypted')); + } + } + + $fields_array[$field->name] = [ + 'field' => e($field->db_column), + 'value' => e($value), + 'field_format' => $field->format, + 'element' => $field->element, + ]; + + } else { + $value = $item->{$field->db_column}; + + if (($field->format == 'DATE') && (!is_null($value)) && ($value!='')){ + $value = Helper::getFormattedDateObject($value, 'date', false); + } + + $fields_array[$field->name] = [ + 'field' => e($field->db_column), + 'value' => e($value), + 'field_format' => $field->format, + 'element' => $field->element, + ]; + } + + return $fields_array; + } + } else { + return new \stdClass; // HACK to force generation of empty object instead of empty list + } + } + + static function present($field) { + return [ + 'field' => 'custom_fields.'.$field->db_column, + 'searchable' => true, + 'sortable' => true, + 'switchable' => true, + 'title' => $field->name, + 'formatter'=> 'customFieldsFormatter', + 'escape' => true, + 'class' => ($field->field_encrypted == '1') ? 'css-padlock' : '', + 'visible' => ($field->show_in_listview == '1') ? true : false, + ]; + } +} \ No newline at end of file diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php index 95a344dce9e7..b9030e7c8eb1 100644 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -653,6 +653,17 @@ public static function customFieldsetList() return $customfields; } + /** + * Get all of the different types of custom fields there are + * TODO - how to make this more general? Or more useful? or more dynamic? + * idea - key of classname, *value* of trans? (thus having to make this a method, which is fine) + */ + static $itemtypes_having_custom_fields = [ + 0 => \App\Models\Asset::class, + 1 => \App\Models\User::class, + // 2 => \App\Models\Accessory::class + ]; + /** * Get the list of custom field formats in an array to make a dropdown menu * diff --git a/app/Http/Controllers/Api/AssetModelsController.php b/app/Http/Controllers/Api/AssetModelsController.php index 33a00d1d3f52..09b02a629acf 100644 --- a/app/Http/Controllers/Api/AssetModelsController.php +++ b/app/Http/Controllers/Api/AssetModelsController.php @@ -70,7 +70,7 @@ public function index(Request $request) : JsonResponse | array 'models.deleted_at', 'models.updated_at', ]) - ->with('category', 'depreciation', 'manufacturer', 'fieldset.fields.defaultValues', 'adminuser') + ->with('category', 'depreciation', 'manufacturer', 'adminuser') ->withCount('assets as assets_count'); if ($request->input('status')=='deleted') { diff --git a/app/Http/Controllers/Api/AssetsController.php b/app/Http/Controllers/Api/AssetsController.php index da400d216ecb..bb81a55e8e90 100644 --- a/app/Http/Controllers/Api/AssetsController.php +++ b/app/Http/Controllers/Api/AssetsController.php @@ -121,7 +121,7 @@ public function index(Request $request, $action = null, $upcoming_status = null) $filter = json_decode($request->input('filter'), true); } - $all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load + $all_custom_fields = CustomField::where('type', Asset::class); //used as a 'cache' of custom fields throughout this page load foreach ($all_custom_fields as $field) { $allowed_columns[] = $field->db_column_name(); } @@ -618,48 +618,8 @@ public function store(StoreAssetRequest $request): JsonResponse $asset = $request->handleImages($asset); - // Update custom fields in the database. - $model = AssetModel::find($request->input('model_id')); - - // Check that it's an object and not a collection - // (Sometimes people send arrays here and they shouldn't - if (($model) && ($model instanceof AssetModel) && ($model->fieldset)) { - foreach ($model->fieldset->fields as $field) { - - // Set the field value based on what was sent in the request - $field_val = $request->input($field->db_column, null); - - // If input value is null, use custom field's default value - if ($field_val == null) { - Log::debug('Field value for ' . $field->db_column . ' is null'); - $field_val = $field->defaultValue($request->get('model_id')); - Log::debug('Use the default fieldset value of ' . $field->defaultValue($request->get('model_id'))); - } - - // if the field is set to encrypted, make sure we encrypt the value - if ($field->field_encrypted == '1') { - Log::debug('This model field is encrypted in this fieldset.'); - - if (Gate::allows('assets.view.encrypted_custom_fields')) { - - // If input value is null, use custom field's default value - if (($field_val == null) && ($request->has('model_id') != '')) { - $field_val = Crypt::encrypt($field->defaultValue($request->get('model_id'))); - } else { - $field_val = Crypt::encrypt($request->input($field->db_column)); - } - } - } - if ($field->element == 'checkbox') { - if (is_array($field_val)) { - $field_val = implode(',', $field_val); - } - } - + $asset->customFill($request, Auth::user(), true); - $asset->{$field->db_column} = $field_val; - } - } if ($asset->save()) { if ($request->get('assigned_user')) { @@ -696,6 +656,7 @@ public function update(UpdateAssetRequest $request, Asset $asset): JsonResponse { $asset->fill($request->validated()); +<<<<<<< HEAD if ($request->has('model_id')) { $asset->model()->associate(AssetModel::find($request->validated()['model_id'])); } @@ -707,6 +668,61 @@ public function update(UpdateAssetRequest $request, Asset $asset): JsonResponse } if ($request->input('last_audit_date')) { $asset->last_audit_date = Carbon::parse($request->input('last_audit_date'))->startOfDay()->format('Y-m-d H:i:s'); +======= + if ($asset = Asset::find($id)) { + $asset->fill($request->all()); + + ($request->filled('model_id')) ? + $asset->model()->associate(AssetModel::find($request->get('model_id'))) : null; + ($request->filled('rtd_location_id')) ? + $asset->location_id = $request->get('rtd_location_id') : ''; + ($request->filled('company_id')) ? + $asset->company_id = Company::getIdForCurrentUser($request->get('company_id')) : ''; + + ($request->filled('rtd_location_id')) ? + $asset->location_id = $request->get('rtd_location_id') : null; + + /** + * this is here just legacy reasons. Api\AssetController + * used image_source once to allow encoded image uploads. + */ + if ($request->has('image_source')) { + $request->offsetSet('image', $request->offsetGet('image_source')); + } + + $asset = $request->handleImages($asset); + + $problems_updating_encrypted_custom_fields = !$asset->customFill($request, Auth::user()); + + if ($asset->save()) { + if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) { + $location = $target->location_id; + } elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) { + $location = $target->location_id; + + Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $id) + ->update(['location_id' => $target->location_id]); + } elseif (($request->filled('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) { + $location = $target->id; + } + + if (isset($target)) { + $asset->checkOut($target, Auth::user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->get('name')), $location); + } + + if ($asset->image) { + $asset->image = $asset->getImageUrl(); + } + + if ($problems_updating_encrypted_custom_fields) { + return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.encrypted_warning'))); + } else { + return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success'))); + } + } + + return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200); +>>>>>>> d2b7828569 (This is a squashed branch of all of the various commits that make up the new HasCustomFields trait.) } /** diff --git a/app/Http/Controllers/Api/CustomFieldsetsController.php b/app/Http/Controllers/Api/CustomFieldsetsController.php index 5dbd507917a5..dbfd87c237a9 100644 --- a/app/Http/Controllers/Api/CustomFieldsetsController.php +++ b/app/Http/Controllers/Api/CustomFieldsetsController.php @@ -33,7 +33,7 @@ class CustomFieldsetsController extends Controller public function index() : array { $this->authorize('index', CustomField::class); - $fieldsets = CustomFieldset::withCount('fields as fields_count', 'models as models_count')->get(); + $fieldsets = CustomFieldset::withCount('fields as fields_count')->get(); return (new CustomFieldsetsTransformer)->transformCustomFieldsets($fieldsets, $fieldsets->count()); } @@ -119,7 +119,7 @@ public function destroy($id) : JsonResponse $this->authorize('delete', CustomField::class); $fieldset = CustomFieldset::findOrFail($id); - $modelsCount = $fieldset->models->count(); + $modelsCount = $fieldset->customizables()->count(); $fieldsCount = $fieldset->fields->count(); if (($modelsCount > 0) || ($fieldsCount > 0)) { diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index 1d34f90a39e5..d7fddf94cc12 100644 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -16,6 +16,7 @@ use App\Models\Accessory; use App\Models\Company; use App\Models\Consumable; +use App\Models\CustomField; use App\Models\License; use App\Models\User; use App\Notifications\CurrentInventory; @@ -42,7 +43,7 @@ public function index(Request $request) : array { $this->authorize('view', User::class); - $users = User::select([ + $allowed_columns = [ 'users.activated', 'users.address', 'users.avatar', @@ -80,7 +81,12 @@ public function index(Request $request) : array 'users.autoassign_licenses', 'users.website', - ])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy', 'managesUsers', 'managedLocations') + ]; + + foreach (CustomField::where('type', User::class)->get() as $field) { + $allowed_columns[] = $field->db_column_name(); + } + $users = User::select($allowed_columns)->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy', 'managesUsers', 'managedLocations') ->withCount([ 'assets as assets_count' => function(Builder $query) { $query->withoutTrashed(); @@ -404,7 +410,9 @@ public function store(SaveUserRequest $request) : JsonResponse } app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'image', 'avatars', 'avatar'); - + + $user->customFill($request,Auth::user()); + if ($user->save()) { if ($request->filled('groups')) { $user->groups()->sync($request->input('groups')); @@ -477,6 +485,12 @@ public function update(SaveUserRequest $request, User $user): JsonResponse $user->password = bcrypt($request->input('password')); } + app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'image', 'avatars', 'avatar'); + + $user->customFill($request,Auth::user()); + + if ($user->save()) { + // We need to use has() instead of filled() // here because we need to overwrite permissions // if someone needs to null them out diff --git a/app/Http/Controllers/AssetModelsController.php b/app/Http/Controllers/AssetModelsController.php index aa083e1ae329..4c38545bfccf 100755 --- a/app/Http/Controllers/AssetModelsController.php +++ b/app/Http/Controllers/AssetModelsController.php @@ -6,10 +6,12 @@ use App\Http\Requests\ImageUploadRequest; use App\Http\Requests\StoreAssetModelRequest; use App\Models\Actionlog; +use App\Models\Asset; use App\Models\AssetModel; use App\Models\CustomField; use App\Models\SnipeModel; use App\Models\User; +use App\Models\DefaultValuesForCustomFields; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Validator; @@ -153,6 +155,8 @@ public function update(StoreAssetModelRequest $request, $modelId) : RedirectResp $model->notes = $request->input('notes'); $model->requestable = $request->input('requestable', '0'); + DefaultValuesForCustomFields::forPivot($model, Asset::class)->delete(); + $model->fieldset_id = $request->input('fieldset_id'); if ($model->save()) { @@ -456,7 +460,7 @@ private function shouldAddDefaultValues(array $input) : bool } /** - * Adds default values to a model (as long as they are truthy) + * Adds default values to a model (as long as they are truthy) (does this mean I cannot set a default value of 0?) * * @param AssetModel $model * @param array $defaultValues @@ -496,11 +500,10 @@ private function assignCustomFieldsDefaultValues(AssetModel|SnipeModel $model, a } foreach ($defaultValues as $customFieldId => $defaultValue) { - if(is_array($defaultValue)){ - $model->defaultValues()->attach($customFieldId, ['default_value' => implode(', ', $defaultValue)]); - }elseif ($defaultValue) { - $model->defaultValues()->attach($customFieldId, ['default_value' => $defaultValue]); + if (is_array($defaultValue)) { + $defaultValue = implode(', ', $defaultValue); } + DefaultValuesForCustomFields::updateOrCreate(['custom_field_id' => $customFieldId, 'item_pivot_id' => $model->id], ['default_value' => $defaultValue]); } return true; } diff --git a/app/Http/Controllers/Assets/AssetsController.php b/app/Http/Controllers/Assets/AssetsController.php index 6fd27ed03c2d..93e521999cc7 100755 --- a/app/Http/Controllers/Assets/AssetsController.php +++ b/app/Http/Controllers/Assets/AssetsController.php @@ -160,29 +160,7 @@ public function store(ImageUploadRequest $request) : RedirectResponse $asset = $request->handleImages($asset); } - // Update custom fields in the database. - // Validation for these fields is handled through the AssetRequest form request - $model = AssetModel::find($request->get('model_id')); - - if (($model) && ($model->fieldset)) { - foreach ($model->fieldset->fields as $field) { - if ($field->field_encrypted == '1') { - if (Gate::allows('assets.view.encrypted_custom_fields')) { - if (is_array($request->input($field->db_column))) { - $asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column))); - } else { - $asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column)); - } - } - } else { - if (is_array($request->input($field->db_column))) { - $asset->{$field->db_column} = implode(', ', $request->input($field->db_column)); - } else { - $asset->{$field->db_column} = $request->input($field->db_column); - } - } - } - } + $asset->customFill($request, Auth::user()); // Update custom fields in the database. // Validate the asset before saving if ($asset->isValid() && $asset->save()) { diff --git a/app/Http/Controllers/CustomFieldsController.php b/app/Http/Controllers/CustomFieldsController.php index 53c30b88be3c..1409526cfd36 100644 --- a/app/Http/Controllers/CustomFieldsController.php +++ b/app/Http/Controllers/CustomFieldsController.php @@ -6,11 +6,13 @@ use App\Http\Requests\CustomFieldRequest; use App\Models\CustomField; use App\Models\CustomFieldset; +use App\Models\User; use Illuminate\Support\Facades\Auth; use Illuminate\Http\Request; use Illuminate\Http\RedirectResponse; use \Illuminate\Contracts\View\View; + /** * This controller handles all actions related to Custom Asset Fields for * the Snipe-IT Asset Management application. @@ -22,18 +24,23 @@ */ class CustomFieldsController extends Controller { + /** * Returns a view with a listing of custom fields. * * @author [Brady Wetherington] [] * @since [v1.8] */ - public function index() : View + public function index(Request $request): View { $this->authorize('view', CustomField::class); + if ($request->input('tab') == 1) { + // Users section, make sure to auto-create the first fieldset if so + CustomFieldset::firstOrCreate(['type' => Helper::$itemtypes_having_custom_fields[1]], ['name' => 'default']); + } - $fieldsets = CustomFieldset::with('fields', 'models')->get(); - $fields = CustomField::with('fieldset')->get(); + $fieldsets = CustomFieldset::with('fields')->where("type", Helper::$itemtypes_having_custom_fields[$request->get('tab', 0)])->get(); //cannot eager-load 'customizable' because it's not a relation + $fields = CustomField::with('fieldset')->where("type", Helper::$itemtypes_having_custom_fields[$request->get('tab', 0)])->get(); return view('custom_fields.index')->with('custom_fieldsets', $fieldsets)->with('custom_fields', $fields); } @@ -62,7 +69,7 @@ public function show() : RedirectResponse public function create(Request $request) : View { $this->authorize('create', CustomField::class); - $fieldsets = CustomFieldset::get(); + $fieldsets = CustomFieldset::where('type', Helper::$itemtypes_having_custom_fields[$request->get('tab')])->get(); return view('custom_fields.fields.edit', [ 'predefinedFormats' => Helper::predefined_formats(), @@ -106,6 +113,8 @@ public function store(CustomFieldRequest $request) : RedirectResponse "show_in_requestable_list" => $request->get("show_in_requestable_list", 0), "created_by" => auth()->id() ]); + // not mass-assignable; must be manual + $field->type = Helper::$itemtypes_having_custom_fields[$request->get('tab')]; if ($request->filled('custom_format')) { @@ -118,14 +127,17 @@ public function store(CustomFieldRequest $request) : RedirectResponse // Sync fields with fieldsets $fieldset_array = $request->input('associate_fieldsets'); - if ($request->has('associate_fieldsets') && (is_array($fieldset_array))) { + if ($request->get('tab') == 1) { + $fieldset_array = [CustomFieldset::firstOrCreate(['type' => User::class], ['name' => 'default'])->id => true]; + } + if (($request->has('associate_fieldsets') || $request->get('tab') == 1) && (is_array($fieldset_array))) { $field->fieldset()->sync(array_keys($fieldset_array)); } else { $field->fieldset()->sync([]); } - return redirect()->route('fields.index')->with('success', trans('admin/custom_fields/message.field.create.success')); + return redirect()->route('fields.index', ['tab' => $request->get('tab', 0)])->with('success', trans('admin/custom_fields/message.field.create.success')); } return redirect()->back()->with('selected_fieldsets', $request->input('associate_fieldsets'))->withInput() @@ -174,12 +186,16 @@ public function destroy($field_id) : RedirectResponse if ($field = CustomField::find($field_id)) { $this->authorize('delete', $field); + if ($field->type == User::class) { + $field->fieldset()->detach(); // remove from 'default' group (and others, if they exist in the future!) + } if (($field->fieldset) && ($field->fieldset->count() > 0)) { return redirect()->back()->withErrors(['message' => 'Field is in-use']); } + $type = $field->type; $field->delete(); - return redirect()->route("fields.index") - ->with("success", trans('admin/custom_fields/message.field.delete.success')); + return redirect()->route('fields.index', ['tab' => array_search($type, Helper::$itemtypes_having_custom_fields)]) + ->with('success', trans('admin/custom_fields/message.field.delete.success')); } return redirect()->back()->withErrors(['message' => 'Field does not exist']); @@ -278,7 +294,7 @@ public function update(CustomFieldRequest $request, $id) : RedirectResponse $field->fieldset()->sync([]); } - return redirect()->route('fields.index')->with('success', trans('admin/custom_fields/message.field.update.success')); + return redirect()->route('fields.index', ['tab' => $request->get('tab', 0)])->with('success', trans('admin/custom_fields/message.field.update.success')); } return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.field.update.error')); diff --git a/app/Http/Controllers/CustomFieldsetsController.php b/app/Http/Controllers/CustomFieldsetsController.php index 1d887db29a8f..174f2bbcdce1 100644 --- a/app/Http/Controllers/CustomFieldsetsController.php +++ b/app/Http/Controllers/CustomFieldsetsController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\Helpers\Helper; use App\Models\AssetModel; use App\Models\CustomField; use App\Models\CustomFieldset; @@ -43,7 +44,7 @@ public function show($id) : View | RedirectResponse $this->authorize('view', $cfset); if ($cfset) { - $custom_fields_list = ['' => 'Add New Field to Fieldset'] + CustomField::pluck('name', 'id')->toArray(); + $custom_fields_list = ['' => 'Add New Field to Fieldset'] + CustomField::where('type', $cfset->type)->pluck('name', 'id')->toArray(); $maxid = 0; foreach ($cfset->fields as $field) { @@ -91,6 +92,8 @@ public function store(Request $request) : RedirectResponse $fieldset = new CustomFieldset([ 'name' => $request->get('name'), 'created_by' => auth()->id(), + 'type' => Helper::$itemtypes_having_custom_fields[$request->get('tab')] + // 'sub' => ]); $validator = Validator::make($request->all(), $fieldset->rules); diff --git a/app/Http/Controllers/Users/UsersController.php b/app/Http/Controllers/Users/UsersController.php index 397bfd16d8b7..3759cd8266df 100755 --- a/app/Http/Controllers/Users/UsersController.php +++ b/app/Http/Controllers/Users/UsersController.php @@ -134,6 +134,8 @@ public function store(SaveUserRequest $request) app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar'); session()->put(['redirect_option' => $request->get('redirect_option')]); + \Log::info("About to call customFill, in the 'store' controller!!!"); + $user->customFill($request, Auth::user()); if ($user->save()) { if ($request->filled('groups')) { @@ -301,6 +303,14 @@ public function update(SaveUserRequest $request, User $user) $permissions_array['superuser'] = $orig_superuser; } + $permissions_array = $request->input('permission'); + + // Strip out the superuser permission if the user isn't a superadmin + if (!Auth::user()->isSuperUser()) { + unset($permissions_array['superuser']); + $permissions_array['superuser'] = $orig_superuser; + } + $user->permissions = json_encode($permissions_array); // Handle uploaded avatar @@ -312,6 +322,18 @@ public function update(SaveUserRequest $request, User $user) return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users')) ->with('success', trans('admin/users/message.success.update')); } + + \Log::debug("calling custom fill from the UPDATE method!"); + $user->customFill($request, Auth::user()); + //\Log::debug(print_r($user, true)); + + // Was the user updated? + if ($user->save()) { + // Redirect to the user page + return redirect()->route('users.index') + ->with('success', trans('admin/users/message.success.update')); + } + return redirect()->back()->withInput()->withErrors($user->getErrors()); } diff --git a/app/Http/Requests/CustomFieldRequest.php b/app/Http/Requests/CustomFieldRequest.php index 0c2ec0ae60bd..c8da020803d5 100644 --- a/app/Http/Requests/CustomFieldRequest.php +++ b/app/Http/Requests/CustomFieldRequest.php @@ -34,12 +34,14 @@ public function rules(Request $request) case 'POST': { $rules['name'] = 'required|unique:custom_fields'; + $rules['tab'] = 'required'; break; } // Save all fields case 'PUT': $rules['name'] = 'required'; + $rules['tab'] = 'required'; break; // Save only what's passed diff --git a/app/Http/Transformers/AssetsTransformer.php b/app/Http/Transformers/AssetsTransformer.php index c5110ac8ee6b..b53ad718871e 100644 --- a/app/Http/Transformers/AssetsTransformer.php +++ b/app/Http/Transformers/AssetsTransformer.php @@ -2,6 +2,7 @@ namespace App\Http\Transformers; +use App\Helpers\CustomFieldHelper; use App\Helpers\Helper; use App\Models\Accessory; use App\Models\AccessoryCheckout; @@ -105,49 +106,7 @@ public function transformAsset(Asset $asset) ]; - if (($asset->model) && ($asset->model->fieldset) && ($asset->model->fieldset->fields->count() > 0)) { - $fields_array = []; - - foreach ($asset->model->fieldset->fields as $field) { - if ($field->isFieldDecryptable($asset->{$field->db_column})) { - $decrypted = Helper::gracefulDecrypt($field, $asset->{$field->db_column}); - $value = (Gate::allows('assets.view.encrypted_custom_fields')) ? $decrypted : strtoupper(trans('admin/custom_fields/general.encrypted')); - - if ($field->format == 'DATE'){ - if (Gate::allows('assets.view.encrypted_custom_fields')){ - $value = Helper::getFormattedDateObject($value, 'date', false); - } else { - $value = strtoupper(trans('admin/custom_fields/general.encrypted')); - } - } - - $fields_array[$field->name] = [ - 'field' => e($field->db_column), - 'value' => e($value), - 'field_format' => $field->format, - 'element' => $field->element, - ]; - - } else { - $value = $asset->{$field->db_column}; - - if (($field->format == 'DATE') && (!is_null($value)) && ($value!='')){ - $value = Helper::getFormattedDateObject($value, 'date', false); - } - - $fields_array[$field->name] = [ - 'field' => e($field->db_column), - 'value' => e($value), - 'field_format' => $field->format, - 'element' => $field->element, - ]; - } - - $array['custom_fields'] = $fields_array; - } - } else { - $array['custom_fields'] = new \stdClass; // HACK to force generation of empty object instead of empty list - } + $array['custom_fields'] = CustomFieldHelper::transform($asset->model->fieldset,$asset); $permissions_array['available_actions'] = [ 'checkout' => ($asset->deleted_at=='' && Gate::allows('checkout', Asset::class)) ? true : false, diff --git a/app/Http/Transformers/CustomFieldsetsTransformer.php b/app/Http/Transformers/CustomFieldsetsTransformer.php index 61e42486ab43..18f1f79632e2 100644 --- a/app/Http/Transformers/CustomFieldsetsTransformer.php +++ b/app/Http/Transformers/CustomFieldsetsTransformer.php @@ -3,6 +3,8 @@ namespace App\Http\Transformers; use App\Helpers\Helper; +use App\Models\Asset; +use App\Models\AssetModel; use App\Models\CustomFieldset; use Illuminate\Database\Eloquent\Collection; @@ -21,8 +23,13 @@ public function transformCustomFieldsets(Collection $fieldsets, $total) public function transformCustomFieldset(CustomFieldset $fieldset) { $fields = $fieldset->fields; - $models = $fieldset->models; + $models = []; $modelsArray = []; + if ($fieldset->type == Asset::class) { + \Log::debug("Item pivot id is: ".$fieldset->item_pivot_id); + $models = AssetModel::where('fieldset_id', $fieldset->id)->get(); + \Log::debug("And the models object count is: ".$models->count()); + } foreach ($models as $model) { $modelsArray[] = [ @@ -30,15 +37,21 @@ public function transformCustomFieldset(CustomFieldset $fieldset) 'name' => e($model->name), ]; } + \Log::debug("Models array is: ".print_r($modelsArray,true)); $array = [ 'id' => (int) $fieldset->id, 'name' => e($fieldset->name), 'fields' => (new CustomFieldsTransformer)->transformCustomFields($fields, $fieldset->fields_count), - 'models' => (new DatatablesTransformer)->transformDatatables($modelsArray, $fieldset->models_count), + 'customizables' => (new DatatablesTransformer)->transformDatatables($fieldset->customizables(),count($fieldset->customizables())), 'created_at' => Helper::getFormattedDateObject($fieldset->created_at, 'datetime'), 'updated_at' => Helper::getFormattedDateObject($fieldset->updated_at, 'datetime'), + 'type' => $fieldset->type, ]; + if ($fieldset->type == Asset::class) { + // TODO - removeme - legacy column just for Assets? + $array['models'] = (new DatatablesTransformer)->transformDatatables($modelsArray, count($modelsArray)); + } return $array; } diff --git a/app/Http/Transformers/UsersTransformer.php b/app/Http/Transformers/UsersTransformer.php index 3bf3ee970265..8692825dc496 100644 --- a/app/Http/Transformers/UsersTransformer.php +++ b/app/Http/Transformers/UsersTransformer.php @@ -2,7 +2,10 @@ namespace App\Http\Transformers; +use App\Helpers\CustomFieldHelper; use App\Helpers\Helper; +use App\Models\CustomField; +use App\Models\CustomFieldset; use App\Models\User; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Gate; @@ -80,6 +83,8 @@ public function transformUser(User $user) 'deleted_at' => ($user->deleted_at) ? Helper::getFormattedDateObject($user->deleted_at, 'datetime') : null, ]; + $array['custom_fields'] = CustomFieldHelper::transform(CustomFieldset::where('type',User::class)->first(), $user); + $permissions_array['available_actions'] = [ 'update' => (Gate::allows('update', User::class) && ($user->deleted_at == '')), 'delete' => $user->isDeletable(), diff --git a/app/Importer/AssetImporter.php b/app/Importer/AssetImporter.php index 1112a04e3508..0fdc370f662d 100644 --- a/app/Importer/AssetImporter.php +++ b/app/Importer/AssetImporter.php @@ -28,9 +28,10 @@ protected function handle($row) // ItemImporter handles the general fetching. parent::handle($row); + // FIXME : YUP!!!!! This shit needs to go (?) Yeah? if ($this->customFields) { foreach ($this->customFields as $customField) { - $customFieldValue = $this->array_smart_custom_field_fetch($row, $customField); + $customFieldValue = $this->array_smart_custom_field_fetch($row, $customField); // TODO/FIXME - this might require a new 'mode' on customFill()? if ($customFieldValue) { if ($customField->field_encrypted == 1) { @@ -40,7 +41,7 @@ protected function handle($row) $this->item['custom_fields'][$customField->db_column_name()] = $customFieldValue; $this->log('Custom Field '.$customField->name.': '.$customFieldValue); } - } else { + } else { // FIXME - think this through? Do we want to blank this? Is that how other stuff works? // Clear out previous data. $this->item['custom_fields'][$customField->db_column_name()] = null; } diff --git a/app/Importer/Importer.php b/app/Importer/Importer.php index 907c8b72c55c..be5f53ba9172 100644 --- a/app/Importer/Importer.php +++ b/app/Importer/Importer.php @@ -182,7 +182,7 @@ abstract protected function handle($row); * @author Daniel Meltzer * @since 5.0 */ - protected function populateCustomFields($headerRow) + protected function populateCustomFields($headerRow) // FIXME - what in the actual fuck is this. { // Stolen From https://adamwathan.me/2016/07/14/customizing-keys-when-mapping-collections/ // This 'inverts' the fields such that we have a collection of fields indexed by name. diff --git a/app/Livewire/Importer.php b/app/Livewire/Importer.php index 75b707b8ad24..30ba84e435d7 100644 --- a/app/Livewire/Importer.php +++ b/app/Livewire/Importer.php @@ -108,7 +108,7 @@ private function getColumns($type) if ($type == "asset") { // add Custom Fields after a horizontal line $results['-'] = "———" . trans('admin/custom_fields/general.custom_fields') . "———’"; - foreach (CustomField::orderBy('name')->get() as $field) { + foreach (CustomField::where('type', \App\Models\Asset::class)->orderBy('name')->get() as $field) { // TODO - generalize? $results[$field->db_column_name()] = $field->name; } } diff --git a/app/Models/Asset.php b/app/Models/Asset.php index ce8b870eb2e0..3016f3d6f031 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -7,6 +7,8 @@ use App\Helpers\Helper; use App\Http\Traits\UniqueUndeletedTrait; use App\Models\Traits\Acceptable; +use App\Models\Traits\Customizable; +use App\Models\Traits\HasCustomFields; use App\Models\Traits\Searchable; use App\Presenters\Presentable; use App\Presenters\AssetPresenter; @@ -16,6 +18,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Storage; use Watson\Validating\ValidatingTrait; use Illuminate\Database\Eloquent\Casts\Attribute; @@ -39,8 +42,21 @@ class Asset extends Depreciable public const ASSET = 'asset'; public const USER = 'user'; - use Acceptable; + use Acceptable, HasCustomFields; + public function getFieldsetKey(): object|int|null + { + return $this->model; + } + + public static function getFieldsetUsers(int $fieldset_id): array + { + $models = []; + foreach (AssetModel::where("fieldset_id", $fieldset_id)->get() as $model) { + $models[route('models.show', $model->id)] = $model->name . (($model->model_number) ? ' (' . $model->model_number . ')' : ''); + } + return $models; + } /** * Run after the checkout acceptance was declined by the user * @@ -213,40 +229,6 @@ public function setExpectedCheckinAttribute($value) $this->attributes['expected_checkin'] = $value; } - /** - * This handles the custom field validation for assets - * - * @var array - */ - public function save(array $params = []) - { - if ($this->model_id != '') { - $model = AssetModel::find($this->model_id); - - if (($model) && ($model->fieldset)) { - - foreach ($model->fieldset->fields as $field){ - if($field->format == 'BOOLEAN'){ - $this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN); - } - } - - $this->rules += $model->fieldset->validation_rules(); - - if ($this->model->fieldset){ - foreach ($this->model->fieldset->fields as $field){ - if($field->format == 'BOOLEAN'){ - $this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN); - } - } - } - } - } - - return parent::save($params); - } - - public function getDisplayNameAttribute() { return $this->present()->name(); diff --git a/app/Models/AssetModel.php b/app/Models/AssetModel.php index 02b5df40d13b..a5114510d19b 100755 --- a/app/Models/AssetModel.php +++ b/app/Models/AssetModel.php @@ -156,6 +156,7 @@ public function manufacturer() */ public function fieldset() { + // this is actually OK - we don't *need* to do this, but it's okay to make references from Model to fieldset return $this->belongsTo(\App\Models\CustomFieldset::class, 'fieldset_id'); } @@ -164,18 +165,6 @@ public function customFields() return $this->fieldset()->first()->fields(); } - /** - * Establishes the model -> custom field default values relationship - * - * @author hannah tinkler - * @since [v4.3] - * @return \Illuminate\Database\Eloquent\Relations\Relation - */ - public function defaultValues() - { - return $this->belongsToMany(\App\Models\CustomField::class, 'models_custom_fields')->withPivot('default_value'); - } - /** * Gets the full url for the image * diff --git a/app/Models/CustomField.php b/app/Models/CustomField.php index dfa497136766..2161b7fbb6b3 100644 --- a/app/Models/CustomField.php +++ b/app/Models/CustomField.php @@ -16,7 +16,7 @@ class CustomField extends Model UniqueUndeletedTrait; /** - * Custom field predfined formats + * Custom field predefined formats * * @var array */ @@ -82,30 +82,19 @@ class CustomField extends Model 'show_in_requestable_list', ]; - /** - * This is confusing, since it's actually the custom fields table that - * we're usually modifying, but since we alter the assets table, we have to - * say that here, otherwise the new fields get added onto the custom fields - * table instead of the assets table. - * - * @author [Brady Wetherington] [] - * @since [v3.0] - */ - public static $table_name = 'assets'; - /** * Convert the custom field's name property to a db-safe string. * * We could probably have used str_slug() here but not sure what it would * do with previously existing values. - @snipe * - * @author [A. Gianotto] [] - * @since [v3.4] * @return string + * @since [v3.4] + * @author [A. Gianotto] [] */ public static function name_to_db_name($name) { - return '_snipeit_'.preg_replace('/[^a-zA-Z0-9]/', '_', strtolower($name)); + return '_snipeit_' . preg_replace('/[^a-zA-Z0-9]/', '_', strtolower($name)); } /** @@ -116,23 +105,22 @@ public static function name_to_db_name($name) * if they have changed, so we handle that here so that we don't have to remember * to do it in the controllers. * - * @author [A. Gianotto] [] - * @since [v3.4] * @return bool + * @since [v3.4] + * @author [A. Gianotto] [] */ public static function boot() { parent::boot(); self::created(function ($custom_field) { - // Column already exists on the assets table - nothing to do here. // This *shouldn't* happen in the wild. - if (Schema::hasColumn(self::$table_name, $custom_field->db_column)) { + if (Schema::hasColumn($custom_field->getTableName(), $custom_field->db_column)) { return false; } // Update the column name in the assets table - Schema::table(self::$table_name, function ($table) use ($custom_field) { + Schema::table($custom_field->getTableName(), function ($table) use ($custom_field) { $table->text($custom_field->convertUnicodeDbSlug())->nullable(); }); @@ -145,7 +133,7 @@ public static function boot() // Column already exists on the assets table - nothing to do here. if ($custom_field->isDirty('name')) { - if (Schema::hasColumn(self::$table_name, $custom_field->convertUnicodeDbSlug())) { + if (Schema::hasColumn($custom_field->getTableName(), $custom_field->convertUnicodeDbSlug())) { return true; } @@ -155,7 +143,7 @@ public static function boot() $platform->registerDoctrineTypeMapping('enum', 'string'); // Rename the field if the name has changed - Schema::table(self::$table_name, function ($table) use ($custom_field) { + Schema::table($custom_field->getTableName(), function ($table) use ($custom_field) { $table->renameColumn($custom_field->convertUnicodeDbSlug($custom_field->getOriginal('name')), $custom_field->convertUnicodeDbSlug()); }); @@ -171,12 +159,19 @@ public static function boot() // Drop the assets column if we've deleted it from custom fields self::deleting(function ($custom_field) { - return Schema::table(self::$table_name, function ($table) use ($custom_field) { + return Schema::table($custom_field->getTableName(), function ($table) use ($custom_field) { $table->dropColumn($custom_field->db_column); }); }); } + public function getTableName() + { + $type = $this->type; + $instance = new $type(); + return $instance->getTable(); + } + /** * Establishes the customfield -> fieldset relationship * @@ -207,31 +202,23 @@ public function user() } /** - * Establishes the customfield -> default values relationship - * - * @author Hannah Tinkler - * @since [v3.0] - * @return \Illuminate\Database\Eloquent\Relations\Relation - */ - public function defaultValues() - { - return $this->belongsToMany(\App\Models\AssetModel::class, 'models_custom_fields')->withPivot('default_value'); - } - - /** - * Returns the default value for a given model using the defaultValues + * Returns the default value for a given 'item' using the defaultValues * relationship * * @param int $modelId * @return string */ - public function defaultValue($modelId) + public function defaultValue($pivot_id) { - return $this->defaultValues->filter(function ($item) use ($modelId) { - return $item->pivot->asset_model_id == $modelId; - })->map(function ($item) { - return $item->pivot->default_value; - })->first(); + /* + below, you might think you need to add: + + where('type', $this->type), + + but the type can be inferred from by the custom_field itself (which also has a type) + can't use forPivot() here because we don't have an object yet. (TODO?) + */ + DefaultValuesForCustomFields::where('item_pivot_id', $pivot_id)->where('custom_field_id', $this->id)->first()?->default_value; //TODO - php8-only operator! } /** diff --git a/app/Models/CustomFieldset.php b/app/Models/CustomFieldset.php index d6bd7a1bef9d..42a9fa9f9625 100644 --- a/app/Models/CustomFieldset.php +++ b/app/Models/CustomFieldset.php @@ -24,6 +24,7 @@ class CustomFieldset extends Model */ public $rules = [ 'name' => 'required|unique:custom_fieldsets', + '' ]; /** @@ -52,11 +53,13 @@ public function fields() * * @author [Brady Wetherington] [] * @since [v3.0] - * @return \Illuminate\Database\Eloquent\Relations\Relation + * @return \Illuminate\Database\Eloquent\Collection */ - public function models() + public function customizables() // TODO - I don't like this name, but I can't think of anything better { - return $this->hasMany(\App\Models\AssetModel::class, 'fieldset_id'); + $customizable_class_name = $this->type; //TODO - copypasta from Customizable trait? + \Log::debug("Customizable Class name is: ".$customizable_class_name); + return $customizable_class_name::getFieldsetUsers($this->id); } /** @@ -81,6 +84,7 @@ public function user() */ public function validation_rules() { + \Log::debug("CALLING validation_rules FOR customfiledsets!"); $rules = []; foreach ($this->fields as $field) { $rule = []; @@ -94,7 +98,12 @@ public function validation_rules() $rule[] = 'unique_undeleted'; } - array_push($rule, $field->attributes['format']); + \Log::debug("Field Format for".$field->name." is: ".$field->format); + if($field->format == 'DATE') { //we do a weird mutator thing, it's confusing - but, yes, it's all-caps + $rule[] = 'date_format:Y-m-d'; + } else { + array_push($rule, $field->attributes['format']); + } $rules[$field->db_column_name()] = $rule; diff --git a/app/Models/DefaultValuesForCustomFields.php b/app/Models/DefaultValuesForCustomFields.php new file mode 100644 index 000000000000..884ff9b3b27b --- /dev/null +++ b/app/Models/DefaultValuesForCustomFields.php @@ -0,0 +1,34 @@ + 'required' + ]; + + public $timestamps = false; + + public function field() { + return $this->belongsTo('custom_fields'); + } + + // There is, effectively, another 'relation' here, but it's weirdly polymorphic + // and impossible to represent in Laravel. + // we have a 'type', and we have an 'item_pivot_id' - + // For example, in Assets the 'type' would be App\Models\Asset, and the 'item_pivot_id' would be a model_id + // I can't come up with any way to represent this in Laravel/Eloquent + + // TODO: might be getting overly-fancy here; maybe just want to do an ID? Instead of an Eloquent Model? + public function scopeForPivot(Builder $query, Model $item, string $class) { + return $query->where('item_pivot_id', $item->id)->where('type', $class); + } +} diff --git a/app/Models/Traits/HasCustomFields.php b/app/Models/Traits/HasCustomFields.php new file mode 100644 index 000000000000..87b400e1daf1 --- /dev/null +++ b/app/Models/Traits/HasCustomFields.php @@ -0,0 +1,150 @@ +fieldset` or `->id`. + */ + public function getFieldset(): ?CustomFieldset { + $pivot = $this->getFieldsetKey(); + if(is_int($pivot)) { //why does this look just like the other thing? (below, look for is_int() + return CustomFieldset::find($pivot); + } + return $pivot?->fieldset; //this is bonkers, why is this even firing?! + } + + /********************** + * @return Object|int|null + * (if this is in PHP 8.0, can we just put that as the signature?) + * + * This is the main method you have to override. It should either return an + * Object who you can call `->fieldset` on and get a fieldset object, and also + * be able to call `->id` on to get a unique key to be able to show custom fields. + * For example, for Assets, the element that is returned is the 'model' for the Asset. + * For something like Users, which will probably have only one universal set of custom fields, + * it should just return the Fieldset ID for it. Or, if there are no custom fields, it should + * return null + */ + abstract public function getFieldsetKey(): Object|int|null; // php v8 minimum, GOOD. TODO + + /*********************** + * @param int $fieldset_id + * @return Collection + * + * This is the main method you need to override to return a list of things that are *using* this fieldset + * The format is an array with keys: a URL, and values. So, for assets, it might return + * { + * "models/14" => "MacBook Pro 13 (model no: 12345)" + * } + */ + abstract public static function getFieldsetUsers(int $fieldset_id): array; + + public static function augmentValidationRulesForCustomFields($model) { + \Log::debug("Augmenting validation rules for custom fields!!!!!!"); + $fieldset = $model->getFieldset(); + if ($fieldset) { + foreach ($fieldset->fields as $field){ + if($field->format == 'BOOLEAN'){ // TODO - this 'feels' like entanglement of concerns? + $model->{$field->db_column} = filter_var($model->{$model->db_column}, FILTER_VALIDATE_BOOLEAN); + } + } + + if(!$model->rules) { + $model->rules = []; + } + $model->rules += $model->getFieldset()->validation_rules(); + \Log::debug("FINAL RULES ARE: ".print_r($model->rules,true)); + } + + } + + public function getDefaultValue(CustomField $field) + { + $pivot = $this->getFieldsetKey(); // TODO - feels copypasta-ish? + $key_id = null; + + if( is_int($pivot) ) { // TODO: *WHY* does this code repeat?! + $key_id = $pivot; // now we're done + } elseif( is_object($pivot) ) { + $key_id = $pivot?->id; + } + if(is_null($key_id)) { + return; + } + + // TODO - begninng to think my custom scope really should be just an integer :/ + return DefaultValuesForCustomFields::where('type',self::class) + ->where('custom_field_id',$field->id) + ->where('item_pivot_id',$key_id)->first()?->default_value; + } + + public function customFill(Request $request, User $user, bool $shouldSetDefaults = false) { + $success = true; + if ($this->getFieldset()) { + foreach ($this->getFieldset()->fields as $field) { + if (is_array($request->input($field->db_column))) { + $field_value = implode(', ', $request->input($field->db_column)); + } else { + $field_value = $request->input($field->db_column); + } + + if ($shouldSetDefaults && (is_null($field_value) || $field_value === '')) { + $field_value = $this->getDefaultValue($field); + } + if ($field->field_encrypted == '1') { + if ($user->can('admin')) { + $this->{$field->db_column} = Crypt::encrypt($field_value); + } else { + $success = false; + continue; //may not be necessary? I'm not sure. I like the other way of doing this TODO + } + } else { + $this->{$field->db_column} = $request->input($field->db_column); + } + } + } + return $success; + } +} \ No newline at end of file diff --git a/app/Models/User.php b/app/Models/User.php index e48b8bf074f8..4aee883f6f88 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -3,6 +3,7 @@ namespace App\Models; use App\Http\Traits\UniqueUndeletedTrait; +use App\Models\Traits\HasCustomFields; use App\Models\Traits\Searchable; use App\Presenters\Presentable; use Illuminate\Auth\Authenticatable; @@ -33,6 +34,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo use Notifiable; use Presentable; use Searchable; + use HasCustomFields; protected $hidden = ['password', 'remember_token', 'permissions', 'reset_password_code', 'persist_code']; protected $table = 'users'; @@ -139,6 +141,20 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo 'manager' => ['first_name', 'last_name', 'username'], ]; + public function getFieldsetKey(): object|int|null + { + // TODO/FIXME - that's hardcoded text, but what language should you use?! I don't know. + // also TODO - is this going to beat on the DB too hard? + return CustomFieldset::where('type', User::class)->first()?->id; + } + + public static function getFieldsetUsers(int $fieldset_id): array + { + return [ + 'no_idea_what_id_to_put' => 'No idea what string to put?' // FIXME obvs. + ]; + } + /** * Internally check the user permission for the given section * diff --git a/app/Presenters/AssetPresenter.php b/app/Presenters/AssetPresenter.php index 19bd2985e7aa..90fe44e6ddcc 100644 --- a/app/Presenters/AssetPresenter.php +++ b/app/Presenters/AssetPresenter.php @@ -2,8 +2,11 @@ namespace App\Presenters; +use App\Helpers\CustomFieldHelper; +use App\Models\Asset; use App\Models\CustomField; use Carbon\CarbonImmutable; +use App\Models\CustomFieldset; use DateTime; /** @@ -309,25 +312,21 @@ public static function dataTableLayout() // models. We only pass the fieldsets that pertain to each asset (via their model) so that we // don't junk up the REST API with tons of custom fields that don't apply - $fields = CustomField::whereHas('fieldset', function ($query) { - $query->whereHas('models'); - })->get(); + //only get fieldsets that have fields + $fieldsets = CustomFieldset::where("type", Asset::class)->whereHas('fields')->get(); + $ids = []; + foreach($fieldsets as $fieldset) { + if (count($fieldset->customizables()) > 0) { //only get fieldsets that are 'in use' + $ids[] = $fieldset->id; + } + } + $fields = CustomField::whereIn('id',$ids)->get(); // FIXME: d'oh! this is wrong. We just got fieldsets, above. Now we're getting fields? // Note: We do not need to e() escape the field names here, as they are already escaped when // they are presented in the blade view. If we escape them here, custom fields with quotes in their // name can break the listings page. - snipe foreach ($fields as $field) { - $layout[] = [ - 'field' => 'custom_fields.'.$field->db_column, - 'searchable' => true, - 'sortable' => true, - 'switchable' => true, - 'title' => $field->name, - 'formatter'=> 'customFieldsFormatter', - 'escape' => true, - 'class' => ($field->field_encrypted == '1') ? 'css-padlock' : '', - 'visible' => ($field->show_in_listview == '1') ? true : false, - ]; + $layout[] = CustomFieldHelper::present($field); } $layout[] = [ diff --git a/app/Presenters/UserPresenter.php b/app/Presenters/UserPresenter.php index 7ee05da0cba1..63ad8e391b39 100644 --- a/app/Presenters/UserPresenter.php +++ b/app/Presenters/UserPresenter.php @@ -2,8 +2,14 @@ namespace App\Presenters; +use App\Helpers\CustomFieldHelper; use App\Helpers\Helper; +use App\Models\Asset; +use App\Models\CustomField; +use App\Models\CustomFieldset; use App\Models\Setting; +use App\Models\User; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Storage; @@ -408,6 +414,30 @@ public static function dataTableLayout() ], ]; + // TODO - FIXME - this is all copy-pasta'ed from the AssetPresenter! + //only get fieldsets that have fields + $fieldsets = CustomFieldset::where("type", User::class)->whereHas('fields')->get(); + $ids = []; + foreach($fieldsets as $fieldset) { + if (count($fieldset->customizables()) > 0) { //only get fieldsets that are 'in use' + \Log::debug("Found a fieldset! It's: ".$fieldset->id); + $ids[] = $fieldset->id; + } else { + \Log::debug("Didn't find fieldset: ".$fieldset->id); + } + } + + $fields = CustomField::whereHas('fieldset', function (Builder $query) use($ids) { + $query->whereIn('custom_fieldsets.id', $ids); + })->get(); + // Note: We do not need to e() escape the field names here, as they are already escaped when + // they are presented in the blade view. If we escape them here, custom fields with quotes in their + // name can break the listings page. - snipe + foreach ($fields as $field) { + \Log::debug("iterating through fields!"); + $layout[] = CustomFieldHelper::present($field); + } + return json_encode($layout); } diff --git a/config/trustedproxy.php b/config/trustedproxy.php index 106a13d4f599..cf011faa9336 100644 --- a/config/trustedproxy.php +++ b/config/trustedproxy.php @@ -1,5 +1,7 @@ Illuminate\Http\Request::HEADER_X_FORWARDED_ALL, //this is mostly handled already - + 'headers' => Request::HEADER_X_FORWARDED_FOR | + Request::HEADER_X_FORWARDED_HOST | + Request::HEADER_X_FORWARDED_PORT | + Request::HEADER_X_FORWARDED_PROTO | + Request::HEADER_X_FORWARDED_AWS_ELB, ]; diff --git a/database/factories/AssetFactory.php b/database/factories/AssetFactory.php index 4d6d20651c8d..1c936170655c 100644 --- a/database/factories/AssetFactory.php +++ b/database/factories/AssetFactory.php @@ -398,4 +398,15 @@ public function canBeInvalidUponCreation() $asset->setValidating(true); }); } + + public function withComplicatedCustomFields() + { + return $this->state(function () { + return [ + 'model_id' => function () { + return AssetModel::where('name', 'complicated')->first() ?? AssetModel::factory()->complicated(); + } + ]; + }); + } } diff --git a/database/factories/AssetModelFactory.php b/database/factories/AssetModelFactory.php index 8acecd55d707..992f2b607c98 100644 --- a/database/factories/AssetModelFactory.php +++ b/database/factories/AssetModelFactory.php @@ -448,4 +448,13 @@ public function hasMultipleCustomFields(array $fields = null) ]; }); } + + public function complicated() + { + return $this->state(function () { + return [ + 'name' => 'Complicated fieldset' + ]; + })->for(CustomFieldSet::factory()->complicated(), 'fieldset'); + } } diff --git a/database/factories/CustomFieldFactory.php b/database/factories/CustomFieldFactory.php index 44ab0707e0f3..05247c013bc4 100644 --- a/database/factories/CustomFieldFactory.php +++ b/database/factories/CustomFieldFactory.php @@ -27,6 +27,7 @@ public function definition() 'element' => 'text', 'auto_add_to_fieldsets' => '0', 'show_in_requestable_list' => '0', + 'type' => 'App\\Models\\Asset' ]; } @@ -76,7 +77,7 @@ public function macAddress() { return $this->state(function () { return [ - 'name' => 'MAC Address', + 'name' => 'MAC Address EXPLICIT', 'format' => 'regex:/^([0-9a-fA-F]{2}[:-]){5}[0-9a-fA-F]{2}$/', ]; }); @@ -93,6 +94,15 @@ public function testEncrypted() }); } + public function plainText() + { + return $this->state(function () { + return [ + 'name' => 'plain_text', + ]; + }); + } + public function testCheckbox() { return $this->state(function () { @@ -117,4 +127,13 @@ public function testRadio() }); } + public function date() + { + return $this->state(function () { + return [ + 'name' => 'date', + 'format' => 'date' + ]; + }); + } } diff --git a/database/factories/CustomFieldsetFactory.php b/database/factories/CustomFieldsetFactory.php index a9e8b9ae127b..7ac31ce38e91 100644 --- a/database/factories/CustomFieldsetFactory.php +++ b/database/factories/CustomFieldsetFactory.php @@ -2,8 +2,8 @@ namespace Database\Factories; -use App\Models\CustomFieldset; use App\Models\CustomField; +use App\Models\CustomFieldset; use Illuminate\Database\Eloquent\Factories\Factory; class CustomFieldsetFactory extends Factory @@ -58,13 +58,13 @@ public function hasMultipleCustomFields(array $fields = null): self { return $this->afterCreating(function (CustomFieldset $fieldset) use ($fields) { if (empty($fields)) { - $mac_address = CustomField::factory()->macAddress()->create(); - $ram = CustomField::factory()->ram()->create(); - $cpu = CustomField::factory()->cpu()->create(); + $mac_address = CustomField::factory()->macAddress()->create(); + $ram = CustomField::factory()->ram()->create(); + $cpu = CustomField::factory()->cpu()->create(); - $fieldset->fields()->attach($mac_address, ['order' => '1', 'required' => false]); - $fieldset->fields()->attach($ram, ['order' => '2', 'required' => false]); - $fieldset->fields()->attach($cpu, ['order' => '3', 'required' => false]); + $fieldset->fields()->attach($mac_address, ['order' => '1', 'required' => false]); + $fieldset->fields()->attach($ram, ['order' => '2', 'required' => false]); + $fieldset->fields()->attach($cpu, ['order' => '3', 'required' => false]); } else { foreach ($fields as $field) { $fieldset->fields()->attach($field, ['order' => '1', 'required' => false]); @@ -72,4 +72,16 @@ public function hasMultipleCustomFields(array $fields = null): self } }); } + + public function complicated() + { + //$mac = CustomField::factory()->macAddress()->create(); + return $this->state(function () { + return [ + 'name' => 'complicated' + ]; + })->hasAttached(CustomField::factory()->macAddress(), ['required' => false, 'order' => 0], 'fields') + ->hasAttached(CustomField::factory()->plainText(), ['required' => true, 'order' => 1], 'fields') + ->hasAttached(CustomField::factory()->date(), ['required' => false, 'order' => 2], 'fields'); + } } diff --git a/database/migrations/2017_01_25_063357_fix_utf8_custom_field_column_names.php b/database/migrations/2017_01_25_063357_fix_utf8_custom_field_column_names.php index 4725cccfe101..2aa036bc0980 100644 --- a/database/migrations/2017_01_25_063357_fix_utf8_custom_field_column_names.php +++ b/database/migrations/2017_01_25_063357_fix_utf8_custom_field_column_names.php @@ -23,8 +23,8 @@ function updateLegacyColumnName($customfield) $name_to_db_name = CustomField::name_to_db_name($customfield->name); //\Log::debug('Trying to rename '.$name_to_db_name." to ".$customfield->convertUnicodeDbSlug()."...\n"); - if (Schema::hasColumn(CustomField::$table_name, $name_to_db_name)) { - return Schema::table(CustomField::$table_name, + if (Schema::hasColumn('assets', $name_to_db_name)) { + return Schema::table('assets', function ($table) use ($name_to_db_name, $customfield) { $table->renameColumn($name_to_db_name, $customfield->convertUnicodeDbSlug()); } @@ -81,8 +81,8 @@ public function down() // "_snipeit_imei_1" becomes "_snipeit_imei" $legacyColumnName = (string) Str::of($currentColumnName)->replaceMatches('/_(\d)+$/', ''); - if (Schema::hasColumn(CustomField::$table_name, $currentColumnName)) { - Schema::table(CustomField::$table_name, function (Blueprint $table) use ($currentColumnName, $legacyColumnName) { + if (Schema::hasColumn('assets', $currentColumnName)) { + Schema::table('assets', function (Blueprint $table) use ($currentColumnName, $legacyColumnName) { $table->renameColumn( $currentColumnName, $legacyColumnName diff --git a/database/migrations/2021_06_29_212602_add_type_to_custom_fields.php b/database/migrations/2021_06_29_212602_add_type_to_custom_fields.php new file mode 100644 index 000000000000..e583d6d4136a --- /dev/null +++ b/database/migrations/2021_06_29_212602_add_type_to_custom_fields.php @@ -0,0 +1,38 @@ +text('type')->default('App\\Models\\Asset'); // TODO this default is needed for a not-nullable column, I guess? I don't like this because it will silently 'fix' errors we should properly 'fix' + }); + CustomField::query()->update(['type' => Asset::class]); // TODO - is this still necessary with that 'default'? + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('custom_fields', function (Blueprint $table) { + // + $table->dropColumn('type'); + }); + } +} diff --git a/database/migrations/2021_06_29_212612_add_type_to_custom_fieldsets.php b/database/migrations/2021_06_29_212612_add_type_to_custom_fieldsets.php new file mode 100644 index 000000000000..e9302abb43cd --- /dev/null +++ b/database/migrations/2021_06_29_212612_add_type_to_custom_fieldsets.php @@ -0,0 +1,38 @@ +text('type')->default('App\\Models\\Asset'); + }); + CustomFieldset::query()->update(['type' => Asset::class]); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('custom_fieldsets', function (Blueprint $table) { + // + $table->dropColumn('type'); + }); + } +} diff --git a/database/migrations/2023_08_10_121812_generalize_default_values_for_custom_fields.php b/database/migrations/2023_08_10_121812_generalize_default_values_for_custom_fields.php new file mode 100644 index 000000000000..46d1ad2a2a02 --- /dev/null +++ b/database/migrations/2023_08_10_121812_generalize_default_values_for_custom_fields.php @@ -0,0 +1,28 @@ +renameColumn('asset_model_id','item_pivot_id'); //this one works. okay. that's someting. + }); + Schema::table('default_values_for_custom_fields', function (Blueprint $table) { + $table->string('type')->nullable(); + }); + DefaultValuesForCustomFields::query()->update(['type' => Asset::class]); + Schema::table('default_values_for_custom_fields', function (Blueprint $table) { + //$table->renameColumn('asset_model_id','item_pivot_id'); //this one works. okay. that's someting. + $table->string('type')->nullable(false)->change(); + }); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('default_values_for_custom_fields', function (Blueprint $table) { + $table->dropColumn('type'); + $table->renameColumn('item_pivot_id','asset_model_id'); + }); + } +} diff --git a/resources/views/custom_fields/fields/edit.blade.php b/resources/views/custom_fields/fields/edit.blade.php index 12c3a3fb735e..8ba8f73d0b49 100644 --- a/resources/views/custom_fields/fields/edit.blade.php +++ b/resources/views/custom_fields/fields/edit.blade.php @@ -30,6 +30,8 @@ @endif @csrf + +
@@ -151,12 +153,16 @@ +
+ @if (Request::query('tab') != 1)
+ @endif
@@ -208,7 +214,7 @@
- @if ($fieldsets->count() > 0) + @if ($fieldsets->count() > 0 && Request::query('tab') != 1)
diff --git a/resources/views/custom_fields/fieldsets/edit.blade.php b/resources/views/custom_fields/fieldsets/edit.blade.php index 7a35ca146f66..4520b27776f7 100644 --- a/resources/views/custom_fields/fieldsets/edit.blade.php +++ b/resources/views/custom_fields/fieldsets/edit.blade.php @@ -12,6 +12,7 @@ @section('inputFields') @include ('partials.forms.edit.name', ['translated_name' => trans('general.name')]) + @stop diff --git a/resources/views/custom_fields/index.blade.php b/resources/views/custom_fields/index.blade.php index 5f892b272fcc..c16852b16fab 100644 --- a/resources/views/custom_fields/index.blade.php +++ b/resources/views/custom_fields/index.blade.php @@ -13,6 +13,29 @@ @section('content') @can('view', \App\Models\CustomFieldset::class) +
+
+
+
+ + +
+
+
+
+{{-- Do not show fieldsets for Users' customf ields --}} +@if(Request::query('tab') != 1) +
@@ -21,7 +44,9 @@

{{ trans('admin/custom_fields/general.fieldsets') }}

@@ -47,7 +72,7 @@ class="table table-striped snipe-table" {{ trans('general.name') }} {{ trans('admin/custom_fields/general.qty_fields') }} - {{ trans('admin/custom_fields/general.used_by_models') }} + {{ trans('admin/custom_fields/general.used_by_models') }}{{-- FIXME --}} {{ trans('table.actions') }} @@ -63,9 +88,9 @@ class="table table-striped snipe-table" {{ $fieldset->fields->count() }} - @foreach($fieldset->models as $model) - {{ $model->name }}{{ ($model->model_number) ? ' ('.$model->model_number.')' : '' }} - + @foreach($fieldset->customizables() as $url => $name) + {{ $name }} + {{-- get_class($customizable) }}: {{ $customizable->name
--}} @endforeach @@ -88,10 +113,13 @@ class="table table-striped snipe-table" @can('delete', $fieldset) {{ Form::open(['route' => array('fieldsets.destroy', $fieldset->id), 'method' => 'delete','style' => 'display:inline-block']) }} - @if($fieldset->models->count() > 0) - + @if(count($fieldset->customizables()) > 0 /* TODO - hate 'customizables' */) + @else - + @endif {{ Form::close() }} @endcan @@ -109,6 +137,7 @@ class="table table-striped snipe-table"
+@endif @endcan @can('view', \App\Models\CustomField::class)
@@ -118,7 +147,9 @@ class="table table-striped snipe-table"

{{ trans('admin/custom_fields/general.custom_fields') }}

@@ -194,7 +225,8 @@ class="table table-striped snipe-table" {{ Form::open(array('route' => array('fields.destroy', $field->id), 'method' => 'delete', 'style' => 'display:inline-block')) }} @can('update', $field) - + {{ trans('button.edit') }} @@ -202,7 +234,7 @@ class="table table-striped snipe-table" @can('delete', $field) - @if($field->fieldset->count()>0) + @if($field->fieldset->count()>0 && Request::query('tab') != 1 ) diff --git a/resources/views/hardware/edit.blade.php b/resources/views/hardware/edit.blade.php index efd5d24b9cd0..d5faa17fe919 100755 --- a/resources/views/hardware/edit.blade.php +++ b/resources/views/hardware/edit.blade.php @@ -84,15 +84,15 @@ @endif @if (old('model_id')) @php - $model = \App\Models\AssetModel::find(old('model_id')); + $item->model = \App\Models\AssetModel::find(old('model_id')); @endphp @elseif (isset($selected_model)) @php - $model = $selected_model; + $item->model_id = $selected_model; @endphp @endif - @if (isset($model) && $model) - @include("models/custom_fields_form",["model" => $model]) + @if ($item->getFieldset()) + @include("models/custom_fields_form",["item" => $item]) @endif
diff --git a/resources/views/hardware/view.blade.php b/resources/views/hardware/view.blade.php index 06edd36b8d52..c9f43636e53d 100755 --- a/resources/views/hardware/view.blade.php +++ b/resources/views/hardware/view.blade.php @@ -676,74 +676,8 @@
- -
-
- {{ trans('admin/hardware/general.requestable') }} -
-
- {!! ($asset->requestable=='1') ? ' '.trans('general.yes') : ' '.trans('general.no') !!} -
-
- - @if (($asset->model) && ($asset->model->fieldset)) - @foreach($asset->model->fieldset->fields as $field) -
-
- - {{ $field->name }} - -
-
- @if (($field->field_encrypted=='1') && ($asset->{$field->db_column_name()}!='')) - - - @endif - - @if ($field->isFieldDecryptable($asset->{$field->db_column_name()} )) - @can('assets.view.encrypted_custom_fields') - @php - $fieldSize=strlen(Helper::gracefulDecrypt($field, $asset->{$field->db_column_name()})) - @endphp - @if ($fieldSize>0) - {{ str_repeat('*', $fieldSize) }} - - @if (($field->format=='URL') && ($asset->{$field->db_column_name()}!='')) - {{ Helper::gracefulDecrypt($field, $asset->{$field->db_column_name()}) }} - @elseif (($field->format=='DATE') && ($asset->{$field->db_column_name()}!='')) - {{ \App\Helpers\Helper::gracefulDecrypt($field, \App\Helpers\Helper::getFormattedDateObject($asset->{$field->db_column_name()}, 'date', false)) }} - @else - {{ Helper::gracefulDecrypt($field, $asset->{$field->db_column_name()}) }} - @endif - - - @endif - @else - {{ strtoupper(trans('admin/custom_fields/general.encrypted')) }} - @endcan - - @else - @if (($field->format=='BOOLEAN') && ($asset->{$field->db_column_name()}!='')) - {!! ($asset->{$field->db_column_name()} == 1) ? "" : "" !!} - @elseif (($field->format=='URL') && ($asset->{$field->db_column_name()}!='')) - {{ $asset->{$field->db_column_name()} }} - @elseif (($field->format=='DATE') && ($asset->{$field->db_column_name()}!='')) - {{ \App\Helpers\Helper::getFormattedDateObject($asset->{$field->db_column_name()}, 'date', false) }} - @else - {!! nl2br(e($asset->{$field->db_column_name()})) !!} - @endif - - @endif - @if ($asset->{$field->db_column_name()}=='') -   - @endif -
-
- @endforeach - @endif + @include('partials.custom-fields-view', ['item' => $asset,'width' => 2]) @if ($asset->purchase_date) diff --git a/resources/views/models/custom_fields_form.blade.php b/resources/views/models/custom_fields_form.blade.php index 4949fae71c6e..b85a004f6fba 100644 --- a/resources/views/models/custom_fields_form.blade.php +++ b/resources/views/models/custom_fields_form.blade.php @@ -1,5 +1,28 @@ -@if (($model) && ($model->fieldset)) - @foreach($model->fieldset->fields AS $field) +{{-- + +Okay, now how am I going to work *this* out. I think it's less bad than I think. + +I think we can pass the $asset or the $user to this partial. + +This partial can be aware of the HasCustomFields trait, and call getFieldset() to get the appropriate fieldset; that's good. + +But we also need the 'discriminator' so that we can use defaultValuesForCustomFields, right? + +Well, that should be easy enough, right? Just call getFieldsetKey(), right? Or maybe we don't even have to do that - + +We can call $item->getDefaultValue($field), right? + +So the old way - already on this page - is: + +$item->defaultValue($field)) + +And we just do a simple 'replace' to make it be: + +$item->defaultValue($field) +--}} + +@if ($item->getFieldset()) + @foreach($item->getFieldset()->fields AS $field)
@@ -14,14 +37,16 @@ @elseif ($field->element=='textarea') - + @elseif ($field->element=='checkbox') @foreach ($field->formatFieldValuesAsArray() as $key => $value)
@@ -37,6 +62,7 @@
@endforeach + @endif @@ -48,13 +74,19 @@
pivot->required=='1') ? ' required' : '' }}> + + tho)
@else @if (($field->field_encrypted=='0') || (Gate::allows('assets.view.encrypted_custom_fields'))) - pivot->required=='1') ? ' required' : '' }}> + @else @endif diff --git a/resources/views/partials/custom-fields-view.blade.php b/resources/views/partials/custom-fields-view.blade.php new file mode 100644 index 000000000000..ecfb5f292bb5 --- /dev/null +++ b/resources/views/partials/custom-fields-view.blade.php @@ -0,0 +1,47 @@ +{{-- FIXME - this doesn't work for Assets (crap!) --}} +@if ($item->getFieldset()) + @foreach($item->getFieldset()->fields as $field) +
+
+ + {{ $field->name }} + +
+
+ @if ($field->field_encrypted=='1') + + @endif + + @if ($field->isFieldDecryptable($item->{$field->db_column_name()} )) + @can('assets.view.encrypted_custom_fields') + @if (($field->format=='URL') && ($item->{$field->db_column_name()}!='')) + {{ Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) }} + @elseif (($field->format=='DATE') && ($item->{$field->db_column_name()}!='')) + {{ \App\Helpers\Helper::gracefulDecrypt($field, \App\Helpers\Helper::getFormattedDateObject($item->{$field->db_column_name()}, 'date', false)) }} + @else + {{ Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) }} + @endif + @else + {{ strtoupper(trans('admin/custom_fields/general.encrypted')) }} + @endcan + + @else + @if (($field->format=='BOOLEAN') && ($item->{$field->db_column_name()}!='')) + {!! ($item->{$field->db_column_name()} == 1) ? "" : "" !!} + @elseif (($field->format=='URL') && ($item->{$field->db_column_name()}!='')) + {{ $item->{$field->db_column_name()} }} + @elseif (($field->format=='DATE') && ($item->{$field->db_column_name()}!='')) + {{ \App\Helpers\Helper::getFormattedDateObject($item->{$field->db_column_name()}, 'date', false) }} + @else + {!! nl2br(e($item->{$field->db_column_name()})) !!} + @endif + + @endif + + @if ($item->{$field->db_column_name()}=='') +   + @endif +
+
+ @endforeach +@endif \ No newline at end of file diff --git a/resources/views/users/edit.blade.php b/resources/views/users/edit.blade.php index 223f9992cff2..35a017a04d0f 100755 --- a/resources/views/users/edit.blade.php +++ b/resources/views/users/edit.blade.php @@ -272,6 +272,15 @@ class="form-control" @include ('partials.forms.edit.image-upload', ['fieldname' => 'avatar', 'image_path' => app('users_upload_path')]) + {{-- FIXME - copypasta from hardware/edit.blade.php --}} +
+ + @if ($user->getFieldset()) + @include("models/custom_fields_form",["item" => $user]) + @endif +
+ {{-- FIXME - copypasts from hardware/edit.blade.php --}} +
diff --git a/resources/views/users/view.blade.php b/resources/views/users/view.blade.php index 0d53c7ad2b4d..096e82a92760 100755 --- a/resources/views/users/view.blade.php +++ b/resources/views/users/view.blade.php @@ -765,7 +765,60 @@
@endif -
+ {{-- FIXME - copypasta from hardware/view.blade.php! --}} + @if (($user->getFieldsetKey()) && (App\Models\CustomFieldset::find($user->getFieldsetKey()))) + @foreach(App\Models\CustomFieldset::find($user->getFieldsetKey())->fields as $field) +
+
+ + {{ $field->name }} + +
+
+ @if ($field->field_encrypted=='1') + + @endif + + @if ($field->isFieldDecryptable($user->{$field->db_column_name()} )) + @can('assets.view.encrypted_custom_fields') + @if (($field->format=='URL') && ($user->{$field->db_column_name()}!='')) + {{ Helper::gracefulDecrypt($field, $user->{$field->db_column_name()}) }} + @elseif (($field->format=='DATE') && ($user->{$field->db_column_name()}!='')) + {{ \App\Helpers\Helper::gracefulDecrypt($field, \App\Helpers\Helper::getFormattedDateObject($user->{$field->db_column_name()}, 'date', false)) }} + @else + {{ Helper::gracefulDecrypt($field, $user->{$field->db_column_name()}) }} + @endif + @else + {{ strtoupper(trans('admin/custom_fields/general.encrypted')) }} + @endcan + + @else + @if (($field->format=='BOOLEAN') && ($user->{$field->db_column_name()}!='')) + {!! ($user->{$field->db_column_name()} == 1) ? "" : "" !!} + @elseif (($field->format=='URL') && ($user->{$field->db_column_name()}!='')) + {{ $user->{$field->db_column_name()} }} + @elseif (($field->format=='DATE') && ($user->{$field->db_column_name()}!='')) + {{ \App\Helpers\Helper::getFormattedDateObject($user->{$field->db_column_name()}, 'date', false) }} + @else + {!! nl2br(e($user->{$field->db_column_name()})) !!} + @endif + + @endif + + @if ($user->{$field->db_column_name()}=='') +   + @endif +
+
+ @endforeach + @endif + {{-- FIXME copypasta from hardware/view.blade.php --}} + @include('partials.custom-fields-view',['item' => $user,'width' => 3]) + +
diff --git a/tests/Feature/Assets/Api/UpdateAssetTest.php b/tests/Feature/Assets/Api/UpdateAssetTest.php index df4448a2db5c..a1b588a70eec 100644 --- a/tests/Feature/Assets/Api/UpdateAssetTest.php +++ b/tests/Feature/Assets/Api/UpdateAssetTest.php @@ -369,12 +369,13 @@ public function testEncryptedCustomFieldCanBeUpdated() $asset = Asset::factory()->hasEncryptedCustomField($field)->create(); $superuser = User::factory()->superuser()->create(); - $this->actingAsForApi($superuser) + $results = $this->actingAsForApi($superuser) ->patchJson(route('api.assets.update', $asset->id), [ $field->db_column_name() => 'This is encrypted field' ]) ->assertStatusMessageIs('success') ->assertOk(); + \Log::error(print_r($results, true)); $asset->refresh(); $this->assertEquals('This is encrypted field', Crypt::decrypt($asset->{$field->db_column_name()})); diff --git a/tests/Unit/HasCustomFieldsTraitTest.php b/tests/Unit/HasCustomFieldsTraitTest.php new file mode 100644 index 000000000000..cff8b3d8789c --- /dev/null +++ b/tests/Unit/HasCustomFieldsTraitTest.php @@ -0,0 +1,83 @@ +withComplicatedCustomFields()->create(); + + $this->assertEquals($asset->model->fieldset->fields->count(), 3,'Custom Fieldset should have exactly 3 custom fields'); + $this->assertTrue(Schema::hasColumn('assets','_snipeit_mac_address_explicit_2'),'Assets table should have MAC address column'); + $this->assertTrue(Schema::hasColumn('assets','_snipeit_plain_text_3'),'Assets table should have MAC address column'); + $this->assertTrue(Schema::hasColumn('assets','_snipeit_date_4'),'Assets table should have MAC address column'); + } + public function testRequired() + { + $asset = Asset::factory()->withComplicatedCustomFields()->create(); + $this->assertFalse($asset->save(),'save() should fail due to required text field'); + } + + public function testFormat() + { + $asset = Asset::factory()->withComplicatedCustomFields()->make(); + $asset->_snipeit_plain_text_3 = 'something'; + $asset->_snipeit_mac_address_explicit_2 = 'fartsssssss'; + $this->assertFalse($asset->save(), 'should fail due to bad MAC address'); + } + + public function testDate() + { +// \Log::error("uh, what the heck is going on here?!"); + $asset = Asset::factory()->withComplicatedCustomFields()->make(); + $asset->_snipeit_plain_text_3 = 'some text'; + $asset->_snipeit_date_4 = '1/2/2023'; +// $asset->save(); +// dd($asset); + $this->assertFalse($asset->save(),'Should fail due to incorrectly formatted date.'); + } + + public function testSaveMinimal() + { + $asset = Asset::factory()->withComplicatedCustomFields()->make(); + $asset->_snipeit_plain_text_3 = "some text"; + $this->assertTrue($asset->save(),"Asset should've saved okay, the one required field was filled out"); + } + + public function testSaveMaximal() + { + $asset = Asset::factory()->withComplicatedCustomFields()->make(); + $asset->_snipeit_plain_text_3 = "some text"; + $asset->_snipeit_date_4 = "2023-01-02"; + $asset->_snipeit_mac_address_explicit_2 = "ff:ff:ff:ff:ff:ff"; + $this->assertTrue($asset->save(),"Asset should've saved okay, the one required field was filled out, and so were the others"); + } + + public function testJsonPost() + { + //FIXME - this is in the wrong place and it might just be genuinley wrong? + $this->markTestIncomplete(); + $asset = Asset::factory()->withComplicatedCustomFields()->make(); + $response = $this->postJson('/api/v1/hardware', [ + + ]); + $response->assertStatus(200); + } + +}