diff --git a/app/Console/Commands/LdapSync.php b/app/Console/Commands/LdapSync.php index c6f8dd379a..ea99e74d1f 100755 --- a/app/Console/Commands/LdapSync.php +++ b/app/Console/Commands/LdapSync.php @@ -303,17 +303,18 @@ class LdapSync extends Command $user->activated = 0; } */ $enabled_accounts = [ - '512', // 0x200 NORMAL_ACCOUNT - '544', // 0x220 NORMAL_ACCOUNT, PASSWD_NOTREQD - '66048', // 0x10200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD - '66080', // 0x10220 NORMAL_ACCOUNT, PASSWD_NOTREQD, DONT_EXPIRE_PASSWORD - '262656', // 0x40200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED - '262688', // 0x40220 NORMAL_ACCOUNT, PASSWD_NOTREQD, SMARTCARD_REQUIRED - '328192', // 0x50200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD - '328224', // 0x50220 NORMAL_ACCOUNT, PASSWD_NOT_REQD, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD - '4194816',// 0x400200 NORMAL_ACCOUNT, DONT_REQ_PREAUTH + '512', // 0x200 NORMAL_ACCOUNT + '544', // 0x220 NORMAL_ACCOUNT, PASSWD_NOTREQD + '66048', // 0x10200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD + '66080', // 0x10220 NORMAL_ACCOUNT, PASSWD_NOTREQD, DONT_EXPIRE_PASSWORD + '262656', // 0x40200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED + '262688', // 0x40220 NORMAL_ACCOUNT, PASSWD_NOTREQD, SMARTCARD_REQUIRED + '328192', // 0x50200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD + '328224', // 0x50220 NORMAL_ACCOUNT, PASSWD_NOT_REQD, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD + '4194816',// 0x400200 NORMAL_ACCOUNT, DONT_REQ_PREAUTH '4260352', // 0x410200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, DONT_REQ_PREAUTH '1049088', // 0x100200 NORMAL_ACCOUNT, NOT_DELEGATED + '1114624', // 0x110200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, NOT_DELEGATED, ]; $user->activated = (in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts)) ? 1 : 0; diff --git a/app/Http/Controllers/Accessories/AccessoriesController.php b/app/Http/Controllers/Accessories/AccessoriesController.php index d1af79adf1..f0b54a9498 100755 --- a/app/Http/Controllers/Accessories/AccessoriesController.php +++ b/app/Http/Controllers/Accessories/AccessoriesController.php @@ -63,6 +63,7 @@ class AccessoriesController extends Controller public function store(ImageUploadRequest $request) { $this->authorize(Accessory::class); + // create a new model instance $accessory = new Accessory(); @@ -82,7 +83,6 @@ class AccessoriesController extends Controller $accessory->supplier_id = request('supplier_id'); $accessory->notes = request('notes'); - $accessory = $request->handleImages($accessory); // Was the accessory created? @@ -127,45 +127,47 @@ class AccessoriesController extends Controller */ public function update(ImageUploadRequest $request, $accessoryId = null) { - if (is_null($accessory = Accessory::find($accessoryId))) { + if ($accessory = Accessory::withCount('users as users_count')->find($accessoryId)) { + + $this->authorize($accessory); + + $validator = Validator::make($request->all(), [ + "qty" => "required|numeric|min:$accessory->users_count" + ]); + + if ($validator->fails()) { + return redirect()->back() + ->withErrors($validator) + ->withInput(); + } + + + + // Update the accessory data + $accessory->name = request('name'); + $accessory->location_id = request('location_id'); + $accessory->min_amt = request('min_amt'); + $accessory->category_id = request('category_id'); + $accessory->company_id = Company::getIdForCurrentUser(request('company_id')); + $accessory->manufacturer_id = request('manufacturer_id'); + $accessory->order_number = request('order_number'); + $accessory->model_number = request('model_number'); + $accessory->purchase_date = request('purchase_date'); + $accessory->purchase_cost = Helper::ParseCurrency(request('purchase_cost')); + $accessory->qty = request('qty'); + $accessory->supplier_id = request('supplier_id'); + $accessory->notes = request('notes'); + + $accessory = $request->handleImages($accessory); + + // Was the accessory updated? + if ($accessory->save()) { + return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.update.success')); + } + } else { return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist')); } - $min = $accessory->numCheckedOut(); - $validator = Validator::make($request->all(), [ - "qty" => "required|numeric|min:$min" - ]); - - if ($validator->fails()) { - return redirect()->back() - ->withErrors($validator) - ->withInput(); - } - - $this->authorize($accessory); - - // Update the accessory data - $accessory->name = request('name'); - $accessory->location_id = request('location_id'); - $accessory->min_amt = request('min_amt'); - $accessory->category_id = request('category_id'); - $accessory->company_id = Company::getIdForCurrentUser(request('company_id')); - $accessory->manufacturer_id = request('manufacturer_id'); - $accessory->order_number = request('order_number'); - $accessory->model_number = request('model_number'); - $accessory->purchase_date = request('purchase_date'); - $accessory->purchase_cost = Helper::ParseCurrency(request('purchase_cost')); - $accessory->qty = request('qty'); - $accessory->supplier_id = request('supplier_id'); - $accessory->notes = request('notes'); - - $accessory = $request->handleImages($accessory); - - // Was the accessory updated? - if ($accessory->save()) { - return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.update.success')); - } - return redirect()->back()->withInput()->withErrors($accessory->getErrors()); } @@ -217,7 +219,7 @@ class AccessoriesController extends Controller */ public function show($accessoryID = null) { - $accessory = Accessory::find($accessoryID); + $accessory = Accessory::withCount('users as users_count')->find($accessoryID); $this->authorize('view', $accessory); if (isset($accessory->id)) { return view('accessories/view', compact('accessory')); diff --git a/app/Http/Controllers/Account/AcceptanceController.php b/app/Http/Controllers/Account/AcceptanceController.php index 59c8f88430..726e164ba8 100644 --- a/app/Http/Controllers/Account/AcceptanceController.php +++ b/app/Http/Controllers/Account/AcceptanceController.php @@ -222,8 +222,8 @@ class AcceptanceController extends Controller 'item_model' => $display_model, 'item_serial' => $item->serial, 'eula' => $item->getEula(), - 'check_out_date' => Carbon::parse($acceptance->created_at)->format($branding_settings->date_display_format), - 'accepted_date' => Carbon::parse($acceptance->accepted_at)->format($branding_settings->date_display_format), + 'check_out_date' => Carbon::parse($acceptance->created_at)->format('Y-m-d'), + 'accepted_date' => Carbon::parse($acceptance->accepted_at)->format('Y-m-d'), 'assigned_to' => $assigned_to, 'company_name' => $branding_settings->site_name, 'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null, @@ -273,7 +273,7 @@ class AcceptanceController extends Controller 'item_tag' => $item->asset_tag, 'item_model' => $display_model, 'item_serial' => $item->serial, - 'declined_date' => Carbon::parse($acceptance->accepted_at)->format($branding_settings->date_display_format), + 'declined_date' => Carbon::parse($acceptance->declined_at)->format('Y-m-d'), 'assigned_to' => $assigned_to, 'company_name' => $branding_settings->site_name, 'date_settings' => $branding_settings->date_display_format, diff --git a/app/Http/Controllers/Api/AccessoriesController.php b/app/Http/Controllers/Api/AccessoriesController.php index a894dc3760..fd21ebaf3a 100644 --- a/app/Http/Controllers/Api/AccessoriesController.php +++ b/app/Http/Controllers/Api/AccessoriesController.php @@ -41,10 +41,13 @@ class AccessoriesController extends Controller 'min_amt', 'company_id', 'notes', + 'users_count', + 'qty', ]; - $accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'users', 'location', 'supplier'); + $accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'users', 'location', 'supplier') + ->withCount('users as users_count'); if ($request->filled('search')) { $accessories = $accessories->TextSearch($request->input('search')); diff --git a/app/Http/Controllers/Api/CustomFieldsetsController.php b/app/Http/Controllers/Api/CustomFieldsetsController.php index 18da1b67c2..27da7733cd 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() { - $this->authorize('index', CustomFieldset::class); + $this->authorize('index', CustomField::class); $fieldsets = CustomFieldset::withCount('fields as fields_count', 'models as models_count')->get(); return (new CustomFieldsetsTransformer)->transformCustomFieldsets($fieldsets, $fieldsets->count()); @@ -49,7 +49,7 @@ class CustomFieldsetsController extends Controller */ public function show($id) { - $this->authorize('view', CustomFieldset::class); + $this->authorize('view', CustomField::class); if ($fieldset = CustomFieldset::find($id)) { return (new CustomFieldsetsTransformer)->transformCustomFieldset($fieldset); } @@ -68,7 +68,7 @@ class CustomFieldsetsController extends Controller */ public function update(Request $request, $id) { - $this->authorize('update', CustomFieldset::class); + $this->authorize('update', CustomField::class); $fieldset = CustomFieldset::findOrFail($id); $fieldset->fill($request->all()); @@ -89,7 +89,7 @@ class CustomFieldsetsController extends Controller */ public function store(Request $request) { - $this->authorize('create', CustomFieldset::class); + $this->authorize('create', CustomField::class); $fieldset = new CustomFieldset; $fieldset->fill($request->all()); @@ -109,7 +109,7 @@ class CustomFieldsetsController extends Controller */ public function destroy($id) { - $this->authorize('delete', CustomFieldset::class); + $this->authorize('delete', CustomField::class); $fieldset = CustomFieldset::findOrFail($id); $modelsCount = $fieldset->models->count(); @@ -136,7 +136,7 @@ class CustomFieldsetsController extends Controller */ public function fields($id) { - $this->authorize('view', CustomFieldset::class); + $this->authorize('view', CustomField::class); $set = CustomFieldset::findOrFail($id); $fields = $set->fields; @@ -153,7 +153,7 @@ class CustomFieldsetsController extends Controller */ public function fieldsWithDefaultValues($fieldsetId, $modelId) { - $this->authorize('view', CustomFieldset::class); + $this->authorize('view', CustomField::class); $set = CustomFieldset::findOrFail($fieldsetId); diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index 36ba063674..c1f59d69b5 100644 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -543,9 +543,10 @@ class UsersController extends Controller if (empty($user->email)) { return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.inventorynotification.error'))); } + + $user->notify((new CurrentInventory($user))); - return response()->Helper::formatStandardApiResponse('success', null, trans('admin/users/message.inventorynotification.success')); - + return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/users/message.inventorynotification.success'))); } /** diff --git a/app/Http/Controllers/CustomFieldsetsController.php b/app/Http/Controllers/CustomFieldsetsController.php index c7c00a7bd2..bee0f38e27 100644 --- a/app/Http/Controllers/CustomFieldsetsController.php +++ b/app/Http/Controllers/CustomFieldsetsController.php @@ -75,9 +75,9 @@ class CustomFieldsetsController extends Controller */ public function create() { - $this->authorize('create', CustomFieldset::class); + $this->authorize('create', CustomField::class); - return view('custom_fields.fieldsets.edit'); + return view('custom_fields.fieldsets.edit')->with('item', new CustomFieldset()); } /** @@ -91,7 +91,7 @@ class CustomFieldsetsController extends Controller */ public function store(Request $request) { - $this->authorize('create', CustomFieldset::class); + $this->authorize('create', CustomField::class); $cfset = new CustomFieldset([ 'name' => e($request->get('name')), @@ -110,31 +110,52 @@ class CustomFieldsetsController extends Controller } /** - * What the actual fuck, Brady? + * Presents edit form for fieldset * - * @todo Uhh, build this? - * @author [Brady Wetherington] [] + * @author [A. Gianotto] [] * @param int $id - * @since [v1.8] - * @return Fuckall + * @since [v6.0.14] + * @return Redirect + * @throws \Illuminate\Auth\Access\AuthorizationException */ public function edit($id) { - // + $this->authorize('create', CustomField::class); + + if ($fieldset = CustomFieldset::find($id)) { + return view('custom_fields.fieldsets.edit')->with('item', $fieldset); + } + + return redirect()->route('fields.index')->with('error', trans('admin/custom_fields/general.fieldset_does_not_exist', ['id' => $id])); + } /** - * GET IN THE SEA BRADY. + * Saves updated fieldset data * - * @todo Uhh, build this too? - * @author [Brady Wetherington] [] + * @author [A. Gianotto] [] * @param int $id - * @since [v1.8] - * @return Fuckall + * @since [v6.0.14] + * @return Redirect + * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function update($id) + public function update(Request $request, $id) { - // + $this->authorize('create', CustomField::class); + + if ($fieldset = CustomFieldset::find($id)) { + + $fieldset->name = $request->input('name'); + + if ($fieldset->save()) { + return redirect()->route('fields.index')->with('success', trans('admin/custom_fields/general.fieldset_updated')); + } + + return redirect()->back()->withInput()->withErrors($fieldset->getErrors()); + + } + + return redirect()->route('fields.index')->with('error', trans('admin/custom_fields/general.fieldset_does_not_exist', ['id' => $id])); } /** @@ -148,7 +169,7 @@ class CustomFieldsetsController extends Controller */ public function destroy($id) { - $fieldset = CustomFieldset::find($id); + $fieldset = CustomField::find($id); $this->authorize('delete', $fieldset); @@ -175,7 +196,7 @@ class CustomFieldsetsController extends Controller */ public function associate(Request $request, $id) { - $set = CustomFieldset::find($id); + $set = CustomField::find($id); $this->authorize('update', $set); @@ -202,7 +223,7 @@ class CustomFieldsetsController extends Controller */ public function makeFieldRequired($fieldset_id, $field_id) { - $this->authorize('update', CustomFieldset::class); + $this->authorize('update', CustomField::class); $field = CustomField::findOrFail($field_id); $fieldset = CustomFieldset::findOrFail($fieldset_id); $fields[$field->id] = ['required' => 1]; @@ -220,7 +241,7 @@ class CustomFieldsetsController extends Controller */ public function makeFieldOptional($fieldset_id, $field_id) { - $this->authorize('update', CustomFieldset::class); + $this->authorize('update', CustomField::class); $field = CustomField::findOrFail($field_id); $fieldset = CustomFieldset::findOrFail($fieldset_id); $fields[$field->id] = ['required' => 0]; diff --git a/app/Http/Transformers/AccessoriesTransformer.php b/app/Http/Transformers/AccessoriesTransformer.php index 6f254b3b87..00c30f9ea2 100644 --- a/app/Http/Transformers/AccessoriesTransformer.php +++ b/app/Http/Transformers/AccessoriesTransformer.php @@ -38,7 +38,8 @@ class AccessoriesTransformer 'purchase_cost' => Helper::formatCurrencyOutput($accessory->purchase_cost), 'order_number' => ($accessory->order_number) ? e($accessory->order_number) : null, 'min_qty' => ($accessory->min_amt) ? (int) $accessory->min_amt : null, - 'remaining_qty' => $accessory->numRemaining(), + 'remaining_qty' => (int) $accessory->numRemaining(), + 'users_count' => $accessory->users_count, 'created_at' => Helper::getFormattedDateObject($accessory->created_at, 'datetime'), 'updated_at' => Helper::getFormattedDateObject($accessory->updated_at, 'datetime'), diff --git a/app/Models/Accessory.php b/app/Models/Accessory.php index 3f2004b047..d39d653382 100755 --- a/app/Models/Accessory.php +++ b/app/Models/Accessory.php @@ -327,20 +327,6 @@ class Accessory extends SnipeModel return null; } - /** - * Check how many items within an accessory are checked out - * - * @author [A. Gianotto] [] - * @since [v5.0] - * @return int - */ - public function numCheckedOut() - { - $checkedout = 0; - $checkedout = $this->users->count(); - - return $checkedout; - } /** * Check how many items of an accessory remain @@ -351,11 +337,11 @@ class Accessory extends SnipeModel */ public function numRemaining() { - $checkedout = $this->users->count(); + $checkedout = $this->users_count; $total = $this->qty; $remaining = $total - $checkedout; - return $remaining; + return (int) $remaining; } /** diff --git a/app/Models/SnipeSCIMConfig.php b/app/Models/SnipeSCIMConfig.php index 36a9ac855c..77cbf01c1a 100644 --- a/app/Models/SnipeSCIMConfig.php +++ b/app/Models/SnipeSCIMConfig.php @@ -12,94 +12,9 @@ class SnipeSCIMConfig extends \ArieTimmerman\Laravel\SCIMServer\SCIMConfig { public function getUserConfig() { - $config = parent::getUserConfig(); - // Much of this is copied verbatim from the library, then adjusted for our needs - $config['class'] = SCIMUser::class; - unset($config['mapping']['example:name:space']); - - $config['map_unmapped'] = false; // anything we don't explicitly map will _not_ show up. - - $core_namespace = 'urn:ietf:params:scim:schemas:core:2.0:User'; - $core = $core_namespace.':'; - $mappings =& $config['mapping'][$core_namespace]; //grab this entire key, we don't want to be repeating ourselves - - //username - *REQUIRED* - $config['validations'][$core.'userName'] = 'required'; - $mappings['userName'] = AttributeMapping::eloquent('username'); - - //human name - *FIRST NAME REQUIRED* - $config['validations'][$core.'name.givenName'] = 'required'; - $config['validations'][$core.'name.familyName'] = 'string'; //not required - - $mappings['name']['familyName'] = AttributeMapping::eloquent("last_name"); - $mappings['name']['givenName'] = AttributeMapping::eloquent("first_name"); - $mappings['name']['formatted'] = (new AttributeMapping())->ignoreWrite()->setRead( - function (&$object) { - return $object->getFullNameAttribute(); - } - ); - - // externalId support - $config['validations'][$core.'externalId'] = 'string|nullable'; // not required, but supported mostly just for Okta - // note that the mapping is *not* namespaced like the other $mappings - $config['mapping']['externalId'] = AttributeMapping::eloquent('scim_externalid'); - - $config['validations'][$core.'emails'] = 'nullable|array'; // emails are not required in Snipe-IT... - $config['validations'][$core.'emails.*.value'] = 'email'; // ...(had to remove the recommended 'required' here) - - $mappings['emails'] = [[ - "value" => AttributeMapping::eloquent("email"), - "display" => null, - "type" => AttributeMapping::constant("work")->ignoreWrite(), - "primary" => AttributeMapping::constant(true)->ignoreWrite() - ]]; - - //active - $config['validations'][$core.'active'] = 'boolean'; - - $mappings['active'] = AttributeMapping::eloquent('activated'); - - //phone - $config['validations'][$core.'phoneNumbers'] = 'nullable|array'; - $config['validations'][$core.'phoneNumbers.*.value'] = 'string'; // another one where want to say 'we don't _need_ a phone number, but if you have one it better have a value. - - $mappings['phoneNumbers'] = [[ - "value" => AttributeMapping::eloquent("phone"), - "display" => null, - "type" => AttributeMapping::constant("work")->ignoreWrite(), - "primary" => AttributeMapping::constant(true)->ignoreWrite() - ]]; - - //address - $config['validations'][$core.'addresses'] = 'nullable|array'; - $config['validations'][$core.'addresses.*.streetAddress'] = 'string'; - $config['validations'][$core.'addresses.*.locality'] = 'string'; - $config['validations'][$core.'addresses.*.region'] = 'nullable|string'; - $config['validations'][$core.'addresses.*.postalCode'] = 'nullable|string'; - $config['validations'][$core.'addresses.*.country'] = 'string'; - - $mappings['addresses'] = [[ - 'type' => AttributeMapping::constant("work")->ignoreWrite(), - 'formatted' => AttributeMapping::constant("n/a")->ignoreWrite(), // TODO - is this right? This doesn't look right. - 'streetAddress' => AttributeMapping::eloquent("address"), - 'locality' => AttributeMapping::eloquent("city"), - 'region' => AttributeMapping::eloquent("state"), - 'postalCode' => AttributeMapping::eloquent("zip"), - 'country' => AttributeMapping::eloquent("country"), - 'primary' => AttributeMapping::constant(true)->ignoreWrite() //this isn't in the example? - ]]; - - //title - $config['validations'][$core.'title'] = 'string'; - $mappings['title'] = AttributeMapping::eloquent('jobtitle'); - - //Preferred Language - $config['validations'][$core.'preferredLanguage'] = 'string'; - $mappings['preferredLanguage'] = AttributeMapping::eloquent('locale'); - - /* + /* more snipe-it attributes I'd like to check out (to map to 'enterprise' maybe?): - website - notes? @@ -108,66 +23,213 @@ class SnipeSCIMConfig extends \ArieTimmerman\Laravel\SCIMServer\SCIMConfig - company_id to "organization?" */ - $enterprise_namespace = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'; - $ent = $enterprise_namespace.':'; - // we remove the 'example' namespace and add the Enterprise one - $config['mapping']['schemas'] = AttributeMapping::constant( [$core_namespace, $enterprise_namespace] )->ignoreWrite(); + $user_prefix = 'urn:ietf:params:scim:schemas:core:2.0:User:'; + $enterprise_prefix = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:'; - $config['validations'][$ent.'employeeNumber'] = 'string'; - $config['validations'][$ent.'department'] = 'string'; - $config['validations'][$ent.'manager'] = 'nullable'; - $config['validations'][$ent.'manager.value'] = 'string'; + return [ - $config['mapping'][$enterprise_namespace] = [ - 'employeeNumber' => AttributeMapping::eloquent('employee_num'), - 'department' =>(new AttributeMapping())->setAdd( // FIXME parent? - function ($value, &$object) { - $department = Department::where("name", $value)->first(); - if ($department) { - $object->department_id = $department->id; - } - } - )->setReplace( - function ($value, &$object) { - $department = Department::where("name", $value)->first(); - if ($department) { - $object->department_id = $department->id; + // Set to 'null' to make use of auth.providers.users.model (App\User::class) + 'class' => SCIMUser::class, + + 'validations' => [ + $user_prefix . 'userName' => 'required', + $user_prefix . 'name.givenName' => 'required', + $user_prefix . 'name.familyName' => 'nullable|string', + $user_prefix . 'externalId' => 'nullable|string', + $user_prefix . 'emails' => 'nullable|array', + $user_prefix . 'emails.*.value' => 'nullable|email', + $user_prefix . 'active' => 'boolean', + $user_prefix . 'phoneNumbers' => 'nullable|array', + $user_prefix . 'phoneNumbers.*.value' => 'nullable|string', + $user_prefix . 'addresses' => 'nullable|array', + $user_prefix . 'addresses.*.streetAddress' => 'nullable|string', + $user_prefix . 'addresses.*.locality' => 'nullable|string', + $user_prefix . 'addresses.*.region' => 'nullable|string', + $user_prefix . 'addresses.*.postalCode' => 'nullable|string', + $user_prefix . 'addresses.*.country' => 'nullable|string', + $user_prefix . 'title' => 'nullable|string', + $user_prefix . 'preferredLanguage' => 'nullable|string', + + // Enterprise validations: + $enterprise_prefix . 'employeeNumber' => 'nullable|string', + $enterprise_prefix . 'department' => 'nullable|string', + $enterprise_prefix . 'manager' => 'nullable', + $enterprise_prefix . 'manager.value' => 'nullable|string' + ], + + 'singular' => 'User', + 'schema' => [Schema::SCHEMA_USER], + + //eager loading + 'withRelations' => [], + 'map_unmapped' => false, +// 'unmapped_namespace' => 'urn:ietf:params:scim:schemas:laravel:unmapped', + 'description' => 'User Account', + + // Map a SCIM attribute to an attribute of the object. + 'mapping' => [ + + 'id' => AttributeMapping::eloquent("id")->disableWrite(), + + 'externalId' => AttributeMapping::eloquent('scim_externalid'), // FIXME - I have a PR that changes a lot of this. + + 'meta' => [ + 'created' => AttributeMapping::eloquent("created_at")->disableWrite(), + 'lastModified' => AttributeMapping::eloquent("updated_at")->disableWrite(), + + 'location' => (new AttributeMapping())->setRead( + function ($object) { + return route( + 'scim.resource', + [ + 'resourceType' => 'Users', + 'resourceObject' => $object->id + ] + ); } - } - )->setRead( - function (&$object) { - return $object->department ? $object->department->name : null; - } - ), - 'manager' => [ - // FIXME - manager writes are disabled. This kinda works but it leaks errors all over the place. Not cool. - // '$ref' => (new AttributeMapping())->ignoreWrite()->ignoreRead(), - // 'displayName' => (new AttributeMapping())->ignoreWrite()->ignoreRead(), - // NOTE: you could probably do a 'plain' Eloquent mapping here, but we don't for future-proofing - 'value' => (new AttributeMapping())->setAdd( - function ($value, &$object) { - $manager = User::find($value); - if ($manager) { - $object->manager_id = $manager->id; + )->disableWrite(), + + 'resourceType' => AttributeMapping::constant("User") + ], + + 'schemas' => AttributeMapping::constant( + [ + 'urn:ietf:params:scim:schemas:core:2.0:User', + 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' + ] + )->ignoreWrite(), + + 'urn:ietf:params:scim:schemas:core:2.0:User' => [ + + 'userName' => AttributeMapping::eloquent("username"), + + 'name' => [ + 'formatted' => (new AttributeMapping())->ignoreWrite()->setRead( + function (&$object) { + return $object->getFullNameAttribute(); + } + ), + 'familyName' => AttributeMapping::eloquent("last_name"), + 'givenName' => AttributeMapping::eloquent("first_name"), + 'middleName' => null, + 'honorificPrefix' => null, + 'honorificSuffix' => null + ], + + 'displayName' => null, + 'nickName' => null, + 'profileUrl' => null, + 'title' => AttributeMapping::eloquent('jobtitle'), + 'userType' => null, + 'preferredLanguage' => AttributeMapping::eloquent('locale'), // Section 5.3.5 of [RFC7231] + 'locale' => null, // see RFC5646 + 'timezone' => null, // see RFC6557 + 'active' => AttributeMapping::eloquent('activated'), + + 'password' => AttributeMapping::eloquent('password')->disableRead(), + + // Multi-Valued Attributes + 'emails' => [[ + "value" => AttributeMapping::eloquent("email"), + "display" => null, + "type" => AttributeMapping::constant("work")->ignoreWrite(), + "primary" => AttributeMapping::constant(true)->ignoreWrite() + ]], + + 'phoneNumbers' => [[ + "value" => AttributeMapping::eloquent("phone"), + "display" => null, + "type" => AttributeMapping::constant("work")->ignoreWrite(), + "primary" => AttributeMapping::constant(true)->ignoreWrite() + ]], + + 'ims' => [[ + "value" => null, + "display" => null, + "type" => null, + "primary" => null + ]], // Instant messaging addresses for the User + + 'photos' => [[ + "value" => null, + "display" => null, + "type" => null, + "primary" => null + ]], + + 'addresses' => [[ + 'type' => AttributeMapping::constant("work")->ignoreWrite(), + 'formatted' => AttributeMapping::constant("n/a")->ignoreWrite(), // TODO - is this right? This doesn't look right. + 'streetAddress' => AttributeMapping::eloquent("address"), + 'locality' => AttributeMapping::eloquent("city"), + 'region' => AttributeMapping::eloquent("state"), + 'postalCode' => AttributeMapping::eloquent("zip"), + 'country' => AttributeMapping::eloquent("country"), + 'primary' => AttributeMapping::constant(true)->ignoreWrite() //this isn't in the example? + ]], + + 'groups' => [[ + 'value' => null, + '$ref' => null, + 'display' => null, + 'type' => null, + 'type' => null + ]], + + 'entitlements' => null, + 'roles' => null, + 'x509Certificates' => null + ], + + 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => [ + 'employeeNumber' => AttributeMapping::eloquent('employee_num'), + 'department' => (new AttributeMapping())->setAdd( // FIXME parent? + function ($value, &$object) { + $department = Department::where("name", $value)->first(); + if ($department) { + $object->department_id = $department->id; + } } - } )->setReplace( function ($value, &$object) { - $manager = User::find($value); - if ($manager) { - $object->manager_id = $manager->id; + $department = Department::where("name", $value)->first(); + if ($department) { + $object->department_id = $department->id; } } )->setRead( function (&$object) { - return $object->manager_id; - } + return $object->department ? $object->department->name : null; + } ), + 'manager' => [ + // FIXME - manager writes are disabled. This kinda works but it leaks errors all over the place. Not cool. + // '$ref' => (new AttributeMapping())->ignoreWrite()->ignoreRead(), + // 'displayName' => (new AttributeMapping())->ignoreWrite()->ignoreRead(), + // NOTE: you could probably do a 'plain' Eloquent mapping here, but we don't for future-proofing + 'value' => (new AttributeMapping())->setAdd( + function ($value, &$object) { + $manager = User::find($value); + if ($manager) { + $object->manager_id = $manager->id; + } + } + )->setReplace( + function ($value, &$object) { + $manager = User::find($value); + if ($manager) { + $object->manager_id = $manager->id; + } + } + )->setRead( + function (&$object) { + return $object->manager_id; + } + ), + ] + ] ] ]; - - return $config; } - } diff --git a/app/Presenters/AccessoryPresenter.php b/app/Presenters/AccessoryPresenter.php index 7d77acc8d7..cc4f9badfc 100644 --- a/app/Presenters/AccessoryPresenter.php +++ b/app/Presenters/AccessoryPresenter.php @@ -80,19 +80,25 @@ class AccessoryPresenter extends Presenter ], [ 'field' => 'qty', 'searchable' => false, - 'sortable' => false, - 'title' => trans('admin/accessories/general.total'), - ], [ - 'field' => 'min_qty', - 'searchable' => false, 'sortable' => true, - 'title' => trans('general.min_amt'), + 'title' => trans('admin/accessories/general.total'), ], [ 'field' => 'remaining_qty', 'searchable' => false, 'sortable' => false, 'visible' => false, 'title' => trans('admin/accessories/general.remaining'), + ],[ + 'field' => 'users_count', + 'searchable' => false, + 'sortable' => true, + 'visible' => true, + 'title' => trans('general.checked_out'), + ], [ + 'field' => 'min_qty', + 'searchable' => false, + 'sortable' => true, + 'title' => trans('general.min_amt'), ], [ 'field' => 'purchase_date', 'searchable' => true, diff --git a/composer.lock b/composer.lock index bf8cb1f758..d6067c5e27 100644 --- a/composer.lock +++ b/composer.lock @@ -78,12 +78,12 @@ "source": { "type": "git", "url": "https://github.com/grokability/laravel-scim-server.git", - "reference": "2c7ecc450eee59234e059ec2e7724b2d8f3a8369" + "reference": "9e8dd2d3958d3c3c05d0a99fe6475361ad9e9419" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/grokability/laravel-scim-server/zipball/2c7ecc450eee59234e059ec2e7724b2d8f3a8369", - "reference": "2c7ecc450eee59234e059ec2e7724b2d8f3a8369", + "url": "https://api.github.com/repos/grokability/laravel-scim-server/zipball/9e8dd2d3958d3c3c05d0a99fe6475361ad9e9419", + "reference": "9e8dd2d3958d3c3c05d0a99fe6475361ad9e9419", "shasum": "" }, "require": { @@ -133,7 +133,7 @@ "support": { "source": "https://github.com/grokability/laravel-scim-server/tree/master" }, - "time": "2022-11-22T20:26:54+00:00" + "time": "2023-01-12T00:32:07+00:00" }, { "name": "asm89/stack-cors", diff --git a/config/database.php b/config/database.php index 9d94454db2..36440b2127 100755 --- a/config/database.php +++ b/config/database.php @@ -102,6 +102,7 @@ return [ 'pgsql' => [ 'driver' => 'pgsql', 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT', '5432'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), diff --git a/config/scim.php b/config/scim.php index d7d578efc2..4e343cd3a6 100644 --- a/config/scim.php +++ b/config/scim.php @@ -3,6 +3,6 @@ return [ "trace" => env("SCIM_TRACE",false), // below, if we ever get 'sure' that we can change this default to 'true' we should - "omit_main_schema_in_return" => env('SCIM_STANDARDS_COMPLIANCE', false), + "omit_main_schema_in_return" => env('SCIM_STANDARDS_COMPLIANCE', true), "publish_routes" => false, ]; diff --git a/public/img/snipe-logo-bug.png b/public/img/snipe-logo-bug.png new file mode 100644 index 0000000000..423a5b7490 Binary files /dev/null and b/public/img/snipe-logo-bug.png differ diff --git a/resources/lang/en/admin/components/general.php b/resources/lang/en/admin/components/general.php index f7689a7ad1..5b788a51ec 100644 --- a/resources/lang/en/admin/components/general.php +++ b/resources/lang/en/admin/components/general.php @@ -12,4 +12,5 @@ return array( 'remaining' => 'Remaining', 'total' => 'Total', 'update' => 'Update Component', + 'checkin_limit' => 'Amount checked in must be equal to or less than :assigned_qty' ); diff --git a/resources/lang/en/admin/custom_fields/general.php b/resources/lang/en/admin/custom_fields/general.php index 92bf240a76..9dae380aa5 100644 --- a/resources/lang/en/admin/custom_fields/general.php +++ b/resources/lang/en/admin/custom_fields/general.php @@ -27,6 +27,9 @@ return [ 'used_by_models' => 'Used By Models', 'order' => 'Order', 'create_fieldset' => 'New Fieldset', + 'update_fieldset' => 'Update Fieldset', + 'fieldset_does_not_exist' => 'Fieldset :id does not exist', + 'fieldset_updated' => 'Fieldset updated', 'create_fieldset_title' => 'Create a new fieldset', 'create_field' => 'New Custom Field', 'create_field_title' => 'Create a new custom field', diff --git a/resources/views/accessories/edit.blade.php b/resources/views/accessories/edit.blade.php old mode 100755 new mode 100644 diff --git a/resources/views/accessories/view.blade.php b/resources/views/accessories/view.blade.php index 82d7c8201a..fd31722204 100644 --- a/resources/views/accessories/view.blade.php +++ b/resources/views/accessories/view.blade.php @@ -290,7 +290,7 @@ @if ($accessory->company)
- {{ trans('general.company')}} + {{ trans('general.company')}}
{{ $accessory->company->name }} @@ -302,7 +302,7 @@ @if ($accessory->category)
- {{ trans('general.category')}} + {{ trans('general.category')}}
{{ $accessory->category->name }} @@ -327,13 +327,22 @@
- Number remaining + {{ trans('admin/accessories/general.remaining') }}
{{ $accessory->numRemaining() }}
+
+
+ {{ trans('general.checked_out') }} +
+
+ {{ $accessory->users_count }} +
+
+ @can('checkout', \App\Models\Accessory::class) diff --git a/resources/views/components/checkin.blade.php b/resources/views/components/checkin.blade.php index 8dca260443..6a7055f727 100644 --- a/resources/views/components/checkin.blade.php +++ b/resources/views/components/checkin.blade.php @@ -42,7 +42,7 @@
-

Must be {{ $component_assets->assigned_qty }} or less.

+

{{ trans('admin/components/general.checkin_limit', ['assigned_qty' => $component_assets->assigned_qty]) }}

{!! $errors->first('checkin_qty', '') !!}
diff --git a/resources/views/consumables/checkout.blade.php b/resources/views/consumables/checkout.blade.php index f768f78484..85cec320e2 100644 --- a/resources/views/consumables/checkout.blade.php +++ b/resources/views/consumables/checkout.blade.php @@ -60,7 +60,7 @@ @if ($snipeSettings->slack_endpoint!='') - A slack message will be sent + {{ trans('general.slack_msg_note') }} @endif
diff --git a/resources/views/custom_fields/fieldsets/edit.blade.php b/resources/views/custom_fields/fieldsets/edit.blade.php index 04885891ec..7a35ca146f 100644 --- a/resources/views/custom_fields/fieldsets/edit.blade.php +++ b/resources/views/custom_fields/fieldsets/edit.blade.php @@ -1,49 +1,17 @@ -@extends('layouts.default') +@extends('layouts/edit-form', [ + 'createText' => trans('admin/custom_fields/general.create_fieldset') , + 'updateText' => trans('admin/custom_fields/general.update_fieldset'), + 'helpText' => trans('admin/custom_fields/general.about_fieldsets_text'), + 'helpPosition' => 'right', + 'formAction' => (isset($item->id)) ? route('fieldsets.update', ['fieldset' => $item->id]) : route('fieldsets.store'), +]) -{{-- Page title --}} -@section('title') - {{ trans('admin/custom_fields/general.create_fieldset') }} -@parent -@stop - -@section('header_right') - - {{ trans('general.back') }} -@stop - - -{{-- Page content --}} @section('content') -
-
- - {{ Form::open(['route' => 'fieldsets.store', 'class'=>'form-horizontal']) }} - -
-
- - -
- -
- - {!! $errors->first('name', '') !!} -
-
- -
- - -
- {{ Form::close() }} -
-
-

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

-

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

-
-
+ @parent @stop + +@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 08c43cdd7b..d2435d7db5 100644 --- a/resources/views/custom_fields/index.blade.php +++ b/resources/views/custom_fields/index.blade.php @@ -69,8 +69,18 @@ @endforeach + + + + @can('update', $fieldset) + + + {{ trans('button.edit') }} + + @endcan + @can('delete', $fieldset) - {{ Form::open(['route' => array('fieldsets.destroy', $fieldset->id), 'method' => 'delete']) }} + {{ Form::open(['route' => array('fieldsets.destroy', $fieldset->id), 'method' => 'delete','style' => 'display:inline-block']) }} @if($fieldset->models->count() > 0) @else @@ -78,6 +88,7 @@ @endif {{ Form::close() }} @endcan + @endforeach diff --git a/resources/views/layouts/default.blade.php b/resources/views/layouts/default.blade.php index daf4275481..ceabe13553 100644 --- a/resources/views/layouts/default.blade.php +++ b/resources/views/layouts/default.blade.php @@ -15,8 +15,8 @@ - - + + diff --git a/resources/views/partials/bootstrap-table.blade.php b/resources/views/partials/bootstrap-table.blade.php index 5a6e9dd464..d3a0fddc14 100644 --- a/resources/views/partials/bootstrap-table.blade.php +++ b/resources/views/partials/bootstrap-table.blade.php @@ -327,8 +327,8 @@ item_icon = ''; } - // display the username if it's checked out to a user - if (value.username) { + // display the username if it's checked out to a user, but don't do it if the username's there already + if (value.username && !value.name.match('\\(') && !value.name.match('\\)')) { value.name = value.name + ' (' + value.username + ')'; }