Compare commits

...

33 Commits

Author SHA1 Message Date
snipe e774f9e42e More sigh 2026-03-13 19:33:58 +00:00
snipe 283ea00b3b Sigh 2026-03-13 19:33:58 +00:00
snipe 062233329d Added migration 2026-03-13 19:33:58 +00:00
snipe bc61c8712c Normalize breadcrumb text 2026-03-13 19:33:58 +00:00
Marcus Moore d9f5185dd2 Remove barcode_types macro 2026-03-13 19:33:58 +00:00
snipe dd0e31778d Hide email as field in selectlist search unless authorized 2026-03-13 19:33:57 +00:00
snipe e835e13de8 Moved sensitive fields 2026-03-13 19:33:49 +00:00
snipe 1105ff43e1 Added fields to export 2026-03-13 19:33:32 +00:00
snipe bcf69d53ae Added getters 2026-03-13 19:33:06 +00:00
snipe f1b66ef96f Added permissions 2026-03-13 19:32:51 +00:00
snipe 5097dc8ee4 Normalized breadcrumbs 2026-03-13 19:32:05 +00:00
snipe 581d90b0a7 Merge branch 'develop' into view-user-address-permission 2026-03-13 19:18:57 +00:00
snipe 65ee12a052 Merge branch 'develop' into view-user-address-permission 2026-03-13 11:28:06 +00:00
snipe 6bd5bafece Rewmoved files permission (for now) 2026-03-12 09:22:24 +00:00
snipe 47d25da371 Use upated $request->request stuff 2026-03-11 19:00:05 +00:00
snipe 2cc6109959 Fixed create 2026-03-11 18:56:07 +00:00
snipe a186f54478 Added more checks 2026-03-11 18:55:58 +00:00
snipe 17bbe7736a Added negative tests 2026-03-11 18:50:41 +00:00
snipe 7beabb3a9c Work on tests 2026-03-11 17:36:21 +00:00
snipe aac3b7b372 Updated string so the Oxford comma sticklers don’t kill me 2026-03-11 15:54:21 +00:00
snipe fea4a3d53e Use assets_count 2026-03-11 15:54:04 +00:00
snipe 0c3f551dde Clarify the senstitive fields 2026-03-11 15:51:32 +00:00
snipe 64780e338b Disallow updating address info in controller 2026-03-11 15:06:20 +00:00
snipe 2dc2e6328f Removed $user from gate check 2026-03-11 15:05:39 +00:00
snipe 5851e2cd68 Removed $user from gate check 2026-03-11 15:05:26 +00:00
snipe b90f2d719c Added fields gto SimpleNameSearch scope 2026-03-11 14:59:13 +00:00
snipe d7fdb71554 Hide email as field in selectlist search unless authorized 2026-03-11 14:55:06 +00:00
snipe 646de9a074 Moved sensitive fields 2026-03-10 15:14:30 +00:00
snipe 34acd827be Added fields to export 2026-03-10 15:11:54 +00:00
snipe fbce94f591 Set policy 2026-03-10 15:11:45 +00:00
snipe 0b30f9eae6 Added getters 2026-03-10 15:11:21 +00:00
snipe 8f6782bdfa Added permissions 2026-03-10 15:08:40 +00:00
snipe bbe7393a61 Normalized breadcrumbs 2026-03-10 15:06:47 +00:00
20 changed files with 1004 additions and 459 deletions
+79 -52
View File
@@ -106,7 +106,6 @@ class UsersController extends Controller
'last_name',
'first_name',
'display_name',
'email',
'jobtitle',
'username',
'employee_num',
@@ -123,13 +122,6 @@ class UsersController extends Controller
'accessories_count',
'manages_users_count',
'manages_locations_count',
'phone',
'mobile',
'address',
'city',
'state',
'country',
'zip',
'id',
'ldap_import',
'two_factor_optin',
@@ -139,7 +131,6 @@ class UsersController extends Controller
'start_date',
'end_date',
'autoassign_licenses',
'website',
'locale',
'notes',
'employee_num',
@@ -156,6 +147,21 @@ class UsersController extends Controller
];
// Do not even request these fields if the requesting user cannot manage user contact info
if (auth()->user()->can('manageContactInfo')) {
array_push($allowed_columns,
'address',
'city',
'country',
'email',
'mobile',
'phone',
'state',
'website',
'zip',
);
}
$filter = [];
if ($request->filled('filter')) {
@@ -193,12 +199,36 @@ class UsersController extends Controller
$users = $users->where('users.company_id', '=', $request->input('company_id'));
}
if ($request->filled('phone')) {
$users = $users->where('users.phone', '=', $request->input('phone'));
}
// Check that the user can view contact info
if (auth()->user()->can('manageContactInfo')) {
if ($request->filled('mobile')) {
$users = $users->where('users.mobile', '=', $request->input('mobile'));
if ($request->filled('phone')) {
$users = $users->where('users.phone', '=', $request->input('phone'));
}
if ($request->filled('mobile')) {
$users = $users->where('users.mobile', '=', $request->input('mobile'));
}
if ($request->filled('email')) {
$users = $users->where('users.email', '=', $request->input('email'));
}
if ($request->filled('state')) {
$users = $users->where('users.state', '=', $request->input('state'));
}
if ($request->filled('country')) {
$users = $users->where('users.country', '=', $request->input('country'));
}
if ($request->filled('website')) {
$users = $users->where('users.website', '=', $request->input('website'));
}
if ($request->filled('zip')) {
$users = $users->where('users.zip', '=', $request->input('zip'));
}
}
if ($request->filled('location_id')) {
@@ -209,10 +239,6 @@ class UsersController extends Controller
$users = $users->where('users.created_by', '=', $request->input('created_by'));
}
if ($request->filled('email')) {
$users = $users->where('users.email', '=', $request->input('email'));
}
if ($request->filled('username')) {
$users = $users->where('users.username', '=', $request->input('username'));
}
@@ -233,22 +259,6 @@ class UsersController extends Controller
$users = $users->where('users.employee_num', '=', $request->input('employee_num'));
}
if ($request->filled('state')) {
$users = $users->where('users.state', '=', $request->input('state'));
}
if ($request->filled('country')) {
$users = $users->where('users.country', '=', $request->input('country'));
}
if ($request->filled('website')) {
$users = $users->where('users.website', '=', $request->input('website'));
}
if ($request->filled('zip')) {
$users = $users->where('users.zip', '=', $request->input('zip'));
}
if ($request->filled('group_id')) {
$users = $users->ByGroup($request->input('group_id'));
}
@@ -379,27 +389,33 @@ class UsersController extends Controller
*/
public function selectlist(Request $request): array
{
$users = User::select(
[
'users.id',
'users.username',
'users.employee_num',
'users.first_name',
'users.last_name',
'users.display_name',
'users.gravatar',
'users.avatar',
'users.email',
]
)->where('show_in_list', '=', '1');
$select_array = [
'users.id',
'users.username',
'users.employee_num',
'users.first_name',
'users.last_name',
'users.display_name',
'users.gravatar',
'users.avatar',
];
if (auth()->user()->can('manageContactInfo')) {
array_push($select_array, 'users.email');
}
$users = User::select($select_array)->where('show_in_list', '=', '1');
if ($request->filled('search')) {
$users = $users->where(function ($query) use ($request) {
$query->SimpleNameSearch($request->input('search'))
->orWhere('username', 'LIKE', '%'.$request->input('search').'%')
->orWhere('display_name', 'LIKE', '%'.$request->input('search').'%')
->orWhere('email', 'LIKE', '%'.$request->input('search').'%')
->orWhere('employee_num', 'LIKE', '%'.$request->input('search').'%');
$query->SimpleNameSearch($request->input('search'));
// Check that the requesting user can search against the email field
if (auth()->user()->can('manageContactInfo')) {
$query->orWhere('users.email', 'LIKE', '%'.$request->input('search').'%');
}
});
}
@@ -602,6 +618,17 @@ class UsersController extends Controller
}
if (auth()->user()->cannot('manageContactInfo')) {
$request->request->remove('phone');
$request->request->remove('mobile');
$request->request->remove('address');
$request->request->remove('city');
$request->request->remove('state');
$request->request->remove('country');
$request->request->remove('zip');
$request->request->remove('website');
}
if ($request->filled('display_name')) {
$user->display_name = $request->input('display_name');
}
@@ -176,9 +176,16 @@ class BulkUsersController extends Controller
->conditionallyAddItem('display_name')
->conditionallyAddItem('start_date')
->conditionallyAddItem('end_date')
->conditionallyAddItem('city')
->conditionallyAddItem('autoassign_licenses');
// Check that the user can manage contact info for users
if (auth()->user()->can('manageContactInfo')) {
$this->conditionallyAddItem('city')
->conditionallyAddItem('state')
->conditionallyAddItem('country')
->conditionallyAddItem('zip');
}
// If the manager_id is one of the users being updated, generate a warning.
if (array_search($request->input('manager_id'), $user_raw_array)) {
$manager_conflict = true;
+141 -50
View File
@@ -17,6 +17,7 @@ use App\Notifications\CurrentInventory;
use App\Notifications\WelcomeNotification;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
@@ -99,7 +100,19 @@ class UsersController extends Controller
$this->authorize('create', User::class);
$user = new User;
// Username, email, and password need to be handled specially because the need to respect config values on an edit.
$user->email = trim($request->input('email'));
if (auth()->user()->can('manageContactInfo')) {
$user->email = trim($request->input('email'));
$user->phone = $request->input('phone');
$user->mobile = $request->input('mobile');
$user->address = $request->input('address', null);
$user->city = $request->input('city', null);
$user->state = $request->input('state', null);
$user->country = $request->input('country', null);
$user->zip = $request->input('zip', null);
$user->website = $request->input('website', null);
}
$user->username = trim($request->input('username'));
$user->display_name = $request->input('display_name');
if ($request->filled('password')) {
@@ -111,20 +124,15 @@ class UsersController extends Controller
$user->employee_num = $request->input('employee_num');
$user->activated = $request->input('activated', 0);
$user->jobtitle = $request->input('jobtitle');
$user->phone = $request->input('phone');
$user->mobile = $request->input('mobile');
$user->location_id = $request->input('location_id', null);
$user->department_id = $request->input('department_id', null);
$user->company_id = Company::getIdForUser($request->input('company_id', null));
$user->manager_id = $request->input('manager_id', null);
$user->notes = $request->input('notes');
$user->address = $request->input('address', null);
$user->city = $request->input('city', null);
$user->state = $request->input('state', null);
$user->country = $request->input('country', null);
$user->zip = $request->input('zip', null);
$user->remote = $request->input('remote', 0);
$user->website = $request->input('website', null);
$user->created_by = auth()->id();
$user->start_date = $request->input('start_date', null);
$user->end_date = $request->input('end_date', null);
@@ -289,6 +297,18 @@ class UsersController extends Controller
// Update the user fields
if (auth()->user()->can('manageContactInfo')) {
$user->email = trim($request->input('email'));
$user->phone = $request->input('phone');
$user->mobile = $request->input('mobile');
$user->address = $request->input('address', null);
$user->city = $request->input('city', null);
$user->state = $request->input('state', null);
$user->country = $request->input('country', null);
$user->zip = $request->input('zip', null);
$user->website = $request->input('website', null);
}
$user->first_name = $request->input('first_name');
$user->last_name = $request->input('last_name');
$user->display_name = $request->input('display_name');
@@ -296,21 +316,13 @@ class UsersController extends Controller
$user->locale = $request->input('locale');
$user->employee_num = $request->input('employee_num');
$user->jobtitle = $request->input('jobtitle', null);
$user->phone = $request->input('phone');
$user->mobile = $request->input('mobile');
$user->location_id = $request->input('location_id', null);
$user->company_id = Company::getIdForUser($request->input('company_id', null));
$user->manager_id = $request->input('manager_id', null);
$user->notes = $request->input('notes');
$user->department_id = $request->input('department_id', null);
$user->address = $request->input('address', null);
$user->city = $request->input('city', null);
$user->state = $request->input('state', null);
$user->country = $request->input('country', null);
$user->zip = $request->input('zip', null);
$user->remote = $request->input('remote', 0);
$user->vip = $request->input('vip', 0);
$user->website = $request->input('website', null);
$user->start_date = $request->input('start_date', null);
$user->end_date = $request->input('end_date', null);
$user->autoassign_licenses = $request->input('autoassign_licenses', 0);
@@ -519,11 +531,15 @@ class UsersController extends Controller
// Blank out some fields
$user->first_name = '';
$user->last_name = '';
$user->email = substr($user->email, ($pos = strpos($user->email, '@')) !== false ? $pos : 0);
$user->id = null;
$user->username = null;
$user->avatar = null;
if (auth()->user()->can('manageContactInfo')) {
$user->email = substr($user->email, ($pos = strpos($user->email, '@')) !== false ? $pos : 0);
}
// Get this user's groups
$userGroups = $user_to_clone->groups()->pluck('name', 'id');
@@ -565,6 +581,63 @@ class UsersController extends Controller
// Open output stream
$handle = fopen('php://output', 'w');
$headers = [
// strtolower to prevent Excel from trying to open it as a SYLK file
strtolower(trans('general.id')),
trans('admin/companies/table.title'),
trans('admin/users/table.title'),
trans('general.employee_number'),
trans('admin/users/table.first_name'),
trans('admin/users/table.last_name'),
trans('admin/users/table.name'),
trans('admin/users/table.username'),
trans('admin/users/table.display_name'),
];
if (auth()->user()->can('manageContactInfo')) {
array_push($headers,
trans('admin/users/table.email'),
trans('admin/users/table.phone'),
trans('admin/users/table.mobile'),
trans('general.address'),
trans('general.city'),
trans('general.state'),
trans('general.country'),
trans('general.zip'),
trans('general.website'));
}
array_push($headers,
trans('admin/users/table.manager'),
trans('admin/users/table.location'),
trans('general.department'),
trans('admin/users/general.department_manager'),
trans('general.assets'),
trans('general.licenses'),
trans('general.accessories'),
trans('general.consumables'),
trans('admin/users/table.managed_users'),
trans('admin/users/table.managed_locations'),
trans('general.groups'),
trans('general.permissions'),
trans('general.notes'),
trans('admin/users/table.activated'),
trans('admin/settings/general.ldap_enabled'),
trans('admin/users/general.two_factor_enrolled'),
trans('admin/users/general.two_factor_active'),
trans('general.autoassign_licenses'),
trans('admin/users/general.remote'),
trans('admin/users/general.vip_label'),
trans('general.language'),
trans('general.start_date'),
trans('general.end_date'),
trans('general.last_login'),
trans('general.updated_at'),
trans('general.created_at'),
trans('general.created_by'),
);
$users = User::with(
'assets',
'accessories',
@@ -575,32 +648,17 @@ class UsersController extends Controller
'groups',
'userloc',
'company'
)->orderBy('created_at', 'DESC')
->chunk(500, function ($users) use ($handle) {
$headers = [
// strtolower to prevent Excel from trying to open it as a SYLK file
strtolower(trans('general.id')),
trans('admin/companies/table.title'),
trans('admin/users/table.title'),
trans('general.employee_number'),
trans('admin/users/table.first_name'),
trans('admin/users/table.last_name'),
trans('admin/users/table.name'),
trans('admin/users/table.username'),
trans('admin/users/table.email'),
trans('admin/users/table.manager'),
trans('admin/users/table.location'),
trans('general.department'),
trans('general.assets'),
trans('general.licenses'),
trans('general.accessories'),
trans('general.consumables'),
trans('general.groups'),
trans('general.permissions'),
trans('general.notes'),
trans('admin/users/table.activated'),
trans('general.created_at'),
];
)->withCount([
'assets as assets_count' => function (Builder $query) {
$query->withoutTrashed();
},
'licenses as licenses_count',
'accessories as accessories_count',
'consumables as consumables_count',
'managesUsers as manages_users_count',
'managedLocations as manages_locations_count',
])->orderBy('created_at', 'DESC')
->chunk(500, function ($users) use ($handle, $headers) {
fputcsv($handle, $headers);
@@ -631,20 +689,53 @@ class UsersController extends Controller
$user->last_name,
$user->display_name,
$user->username,
$user->email,
$user->getRawOriginal('display_name'),
];
if (auth()->user()->can('manageContactInfo')) {
array_push($values,
$user->email,
$user->phone,
$user->mobile,
$user->address,
$user->city,
$user->state,
$user->country,
$user->zip,
$user->website,
);
}
array_push($values,
($user->manager) ? $user->manager->display_name : '',
($user->userloc) ? $user->userloc->name : '',
($user->department) ? $user->department->name : '',
$user->assets->count(),
$user->licenses->count(),
$user->accessories->count(),
$user->consumables->count(),
(($user->department) && ($user->department->manager)) ? $user->department->manager->display_name : '',
$user->assets_count,
$user->licenses_count,
$user->accessories_count,
$user->consumables_count,
$user->manages_users_count,
$user->manages_locations_count,
$user_groups,
$permissionstring,
$user->notes,
($user->activated == '1') ? trans('general.yes') : trans('general.no'),
($user->ldap_import == '1') ? trans('general.yes') : trans('general.no'),
($user->two_factor_active_and_enrolled()) ? trans('general.yes') : trans('general.no'),
($user->two_factor_active()) ? trans('general.yes') : trans('general.no'),
($user->autoassign_licenses == '1') ? trans('general.yes') : trans('general.no'),
($user->remote == '1') ? trans('general.yes') : trans('general.no'),
($user->vip == '1') ? trans('general.yes') : trans('general.no'),
$user->locale,
$user->start_date,
$user->end_date,
$user->last_login,
$user->updated_at,
$user->created_at,
];
$user->createdBy?->display_name,
);
fputcsv($handle, $values);
}
+87 -72
View File
@@ -28,77 +28,90 @@ class UsersTransformer
} elseif ($user->isAdmin()) {
$role = 'admin';
}
$array = [
'id' => (int) $user->id,
'avatar' => e($user->present()->gravatar) ?? null,
'name' => e($user->getFullNameAttribute()) ?? null,
'first_name' => e($user->first_name) ?? null,
'last_name' => e($user->last_name) ?? null,
'display_name' => ($user->getRawOriginal('display_name')) ? e($user->getRawOriginal('display_name')) : null,
'username' => e($user->username) ?? null,
'remote' => ($user->remote == '1') ? true : false,
'locale' => ($user->locale) ? e($user->locale) : null,
'employee_num' => ($user->employee_num) ? e($user->employee_num) : null,
'manager' => ($user->manager) ? [
'id' => (int) $user->manager->id,
'name' => e($user->manager->display_name),
] : null,
'jobtitle' => ($user->jobtitle) ? e($user->jobtitle) : null,
'vip' => ($user->vip == '1') ? true : false,
'phone' => ($user->phone) ? e($user->phone) : null,
'mobile' => ($user->mobile) ? e($user->mobile) : null,
'website' => ($user->website) ? e($user->website) : null,
'address' => ($user->address) ? e($user->address) : null,
'city' => ($user->city) ? e($user->city) : null,
'state' => ($user->state) ? e($user->state) : null,
'country' => ($user->country) ? e($user->country) : null,
'zip' => ($user->zip) ? e($user->zip) : null,
'email' => ($user->email) ? e($user->email) : null,
'department' => ($user->department) ? [
'id' => (int) $user->department->id,
'name' => e($user->department->name),
'tag_color' => ($user->department->tag_color) ? e($user->department->tag_color) : null,
] : null,
'department_manager' => ($user->department?->manager) ? [
'id' => (int) $user->department->manager->id,
'name' => e($user->department->manager->display_name),
] : null,
'location' => ($user->userloc) ? [
'id' => (int) $user->userloc->id,
'name' => e($user->userloc->name),
'tag_color' => ($user->userloc->tag_color) ? e($user->userloc->tag_color) : null,
] : null,
'notes' => Helper::parseEscapedMarkedownInline($user->notes),
'role' => $role,
'permissions' => $user->decodePermissions(),
'activated' => ($user->activated == '1') ? true : false,
'autoassign_licenses' => ($user->autoassign_licenses == '1') ? true : false,
'ldap_import' => ($user->ldap_import == '1') ? true : false,
'two_factor_enrolled' => ($user->two_factor_active_and_enrolled()) ? true : false,
'two_factor_optin' => ($user->two_factor_active()) ? true : false,
'assets_count' => (int) $user->assets_count,
'licenses_count' => (int) $user->licenses_count,
'accessories_count' => (int) $user->accessories_count,
'consumables_count' => (int) $user->consumables_count,
'manages_users_count' => (int) $user->manages_users_count,
'manages_locations_count' => (int) $user->manages_locations_count,
'company' => ($user->company) ? [
'id' => (int) $user->company->id,
'name' => e($user->company->name),
'tag_color' => ($user->company->tag_color) ? e($user->company->tag_color) : null,
] : null,
'created_by' => ($user->createdBy) ? [
'id' => (int) $user->createdBy->id,
'name' => e($user->createdBy->display_name),
] : null,
'created_at' => Helper::getFormattedDateObject($user->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($user->updated_at, 'datetime'),
'start_date' => Helper::getFormattedDateObject($user->start_date, 'date'),
'end_date' => Helper::getFormattedDateObject($user->end_date, 'date'),
'last_login' => Helper::getFormattedDateObject($user->last_login, 'datetime'),
'deleted_at' => ($user->deleted_at) ? Helper::getFormattedDateObject($user->deleted_at, 'datetime') : null,
$sensitive_fields = [
'email' => ($user->email) ? e($user->email) : null,
'phone' => ($user->phone) ? e($user->phone) : null,
'mobile' => ($user->mobile) ? e($user->mobile) : null,
'website' => ($user->website) ? e($user->website) : null,
'address' => ($user->address) ? e($user->address) : null,
'city' => ($user->city) ? e($user->city) : null,
'state' => ($user->state) ? e($user->state) : null,
'country' => ($user->country) ? e($user->country) : null,
'zip' => ($user->zip) ? e($user->zip) : null,
];
$array = [
'id' => (int) $user->id,
'avatar' => e($user->present()->gravatar) ?? null,
'name' => e($user->getFullNameAttribute()) ?? null,
'first_name' => e($user->first_name) ?? null,
'last_name' => e($user->last_name) ?? null,
'display_name' => ($user->getRawOriginal('display_name')) ? e($user->getRawOriginal('display_name')) : null,
'username' => e($user->username) ?? null,
'remote' => ($user->remote == '1') ? true : false,
'locale' => ($user->locale) ? e($user->locale) : null,
'employee_num' => ($user->employee_num) ? e($user->employee_num) : null,
'manager' => ($user->manager) ? [
'id' => (int) $user->manager->id,
'name'=> e($user->manager->display_name),
] : null,
'jobtitle' => ($user->jobtitle) ? e($user->jobtitle) : null,
'vip' => ($user->vip == '1') ? true : false,
];
if (auth()->user()->can('manageContactInfo')) {
$array += $sensitive_fields;
}
$array += [
'department' => ($user->department) ? [
'id' => (int) $user->department->id,
'name'=> e($user->department->name),
'tag_color' => ($user->department->tag_color) ? e($user->department->tag_color) : null,
] : null,
'department_manager' => ($user->department?->manager) ? [
'id' => (int) $user->department->manager->id,
'name'=> e($user->department->manager->display_name),
] : null,
'location' => ($user->userloc) ? [
'id' => (int) $user->userloc->id,
'name'=> e($user->userloc->name),
'tag_color'=> ($user->userloc->tag_color) ? e($user->userloc->tag_color) : null,
] : null,
'notes'=> Helper::parseEscapedMarkedownInline($user->notes),
'role' => $role,
'permissions' => $user->decodePermissions(),
'activated' => ($user->activated == '1') ? true : false,
'autoassign_licenses' => ($user->autoassign_licenses == '1') ? true : false,
'ldap_import' => ($user->ldap_import == '1') ? true : false,
'two_factor_enrolled' => ($user->two_factor_active_and_enrolled()) ? true : false,
'two_factor_optin' => ($user->two_factor_active()) ? true : false,
'assets_count' => (int) $user->assets_count,
'licenses_count' => (int) $user->licenses_count,
'accessories_count' => (int) $user->accessories_count,
'consumables_count' => (int) $user->consumables_count,
'manages_users_count' => (int) $user->manages_users_count,
'manages_locations_count' => (int) $user->manages_locations_count,
'company' => ($user->company) ? [
'id' => (int) $user->company->id,
'name'=> e($user->company->name),
'tag_color'=> ($user->company->tag_color) ? e($user->company->tag_color) : null,
] : null,
'created_by' => ($user->createdBy) ? [
'id' => (int) $user->createdBy->id,
'name'=> e($user->createdBy->display_name),
] : null,
'created_at' => Helper::getFormattedDateObject($user->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($user->updated_at, 'datetime'),
'start_date' => Helper::getFormattedDateObject($user->start_date, 'date'),
'end_date' => Helper::getFormattedDateObject($user->end_date, 'date'),
'last_login' => Helper::getFormattedDateObject($user->last_login, 'datetime'),
'deleted_at' => ($user->deleted_at) ? Helper::getFormattedDateObject($user->deleted_at, 'datetime') : null,
];
$permissions_array['available_actions'] = [
'update' => (Gate::allows('update', User::class) && ($user->deleted_at == '')),
'delete' => ($user->isDeletable() && (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo'))),
@@ -130,9 +143,11 @@ class UsersTransformer
* allowing us to 1) deliver a smaller payload and 2) avoid additional queries on relations that
* have not been easy/lazy loaded already
*
* @param User $user
* @return array
* @throws \Exception
*/
public function transformUserCompact(User $user): array
public function transformUserCompact(User $user) : array
{
$array = [
@@ -146,8 +161,8 @@ class UsersTransformer
'display_name' => e($user->display_name),
'created_by' => $user->adminuser ? [
'id' => (int) $user->adminuser->id,
'name' => e($user->adminuser->present()->fullName),
] : null,
'name'=> e($user->adminuser->present()->fullName),
]: null,
'created_at' => Helper::getFormattedDateObject($user->created_at, 'datetime'),
'deleted_at' => ($user->deleted_at) ? Helper::getFormattedDateObject($user->deleted_at, 'datetime') : null,
];
+65 -3
View File
@@ -208,6 +208,66 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
*
* @see SnipeModel::displayName()
*/
/**
* These fields should be hidden if the requesting user cannot view contact info
*/
protected function address(): Attribute
{
return Attribute::make(
get: fn (mixed $value) => (auth()->user() && auth()->user()->can('manageContactInfo')) ? $value : null,
);
}
protected function city(): Attribute
{
return Attribute::make(
get: fn (mixed $value) => (auth()->user() && auth()->user()->can('manageContactInfo')) ? $value : null,
);
}
protected function state(): Attribute
{
return Attribute::make(
get: fn (mixed $value) => (auth()->user() && auth()->user()->can('manageContactInfo')) ? $value : null,
);
}
protected function country(): Attribute
{
return Attribute::make(
get: fn (mixed $value) => (auth()->user() && auth()->user()->can('manageContactInfo')) ? $value : null,
);
}
protected function zip(): Attribute
{
return Attribute::make(
get: fn (mixed $value) => (auth()->user() && auth()->user()->can('manageContactInfo')) ? $value : null,
);
}
protected function phone(): Attribute
{
return Attribute::make(
get: fn (mixed $value) => (auth()->user() && auth()->user()->can('manageContactInfo')) ? $value : null,
);
}
protected function mobile(): Attribute
{
return Attribute::make(
get: fn (mixed $value) => (auth()->user() && auth()->user()->can('manageContactInfo')) ? $value : null,
);
}
protected function website(): Attribute
{
return Attribute::make(
get: fn (mixed $value) => (auth()->user() && auth()->user()->can('manageContactInfo')) ? $value : null,
);
}
protected function displayName(): Attribute
{
return Attribute::make(
@@ -1084,9 +1144,11 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
*/
public function scopeSimpleNameSearch($query, $search)
{
return $query->where('first_name', 'LIKE', '%'.$search.'%')
->orWhere('last_name', 'LIKE', '%'.$search.'%')
->orWhere('display_name', 'LIKE', '%'.$search.'%')
return $query->where('users.first_name', 'LIKE', '%'.$search.'%')
->orWhere('users.last_name', 'LIKE', '%'.$search.'%')
->orWhere('users.username', 'LIKE', '%'.$search.'%')
->orWhere('users.display_name', 'LIKE', '%'.$search.'%')
->orWhere('users.employee_num', 'LIKE', '%'.$search.'%')
->orWhereMultipleColumns(
[
'users.first_name',
+7
View File
@@ -2,10 +2,17 @@
namespace App\Policies;
use App\Models\User;
class UserPolicy extends SnipePermissionsPolicy
{
protected function columnName()
{
return 'users';
}
public function manageContactInfo(User $user)
{
return $user->hasAccess('users.contact');
}
}
+19 -27
View File
@@ -125,7 +125,11 @@ class UserPresenter extends Presenter
'title' => trans('admin/users/general.remote'),
'visible' => false,
'formatter' => 'trueFalseFormatter',
],
]
];
$sensitive_fields = [
[
'field' => 'email',
'searchable' => true,
@@ -201,8 +205,17 @@ class UserPresenter extends Presenter
'switchable' => true,
'title' => trans('general.zip'),
'visible' => false,
],
]
];
// Add the sensitive fields in if the user can see them
if (auth()->user()->can('manageContactInfo')) {
foreach ($sensitive_fields as $sensitive_field) {
array_push($layout, $sensitive_field);
}
}
array_push($layout,
[
'field' => 'locale',
'searchable' => true,
@@ -431,8 +444,8 @@ class UserPresenter extends Presenter
'formatter' => 'usersActionsFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
];
]
);
return json_encode($layout);
}
@@ -446,29 +459,6 @@ class UserPresenter extends Presenter
return '';
}
/**
* Returns the user full name, it simply concatenates
* the user first and last name.
*
* @return string
*/
// public function fullName()
// {
// if ($this->display_name) {
// return 'kjdfh'.html_entity_decode($this->display_name, ENT_QUOTES | ENT_XML1, 'UTF-8');
// }
// return 'roieuoe'.html_entity_decode($this->first_name.' '.$this->last_name, ENT_QUOTES | ENT_XML1, 'UTF-8');
// }
// /**
// * Standard accessor.
// * @TODO Remove presenter::fullName() entirely?
// * @return string
// */
// public function name()
// {
// return $this->fullName();
// }
/**
* Returns the user Gravatar image url.
@@ -557,4 +547,6 @@ class UserPresenter extends Presenter
return '<span class="'.(($this->deleted_at != '') ? 'deleted' : '').'">'.e($this->display_name).'</span>';
}
}
+6
View File
@@ -160,6 +160,12 @@ class AuthServiceProvider extends ServiceProvider
return true;
});
Gate::define('manageContactInfo', function ($user) {
if ($user->hasAccess('users.contact')) {
return true;
}
});
Gate::define('admin', function ($user) {
if ($user->hasAccess('admin')) {
return true;
+238 -119
View File
@@ -1,9 +1,8 @@
<?php
namespace App\Providers;
<?php namespace App\Providers;
use App\Models\Accessory;
use App\Models\Asset;
use App\Models\Maintenance;
use App\Models\AssetModel;
use App\Models\Category;
use App\Models\Company;
@@ -15,8 +14,8 @@ use App\Models\Department;
use App\Models\Depreciation;
use App\Models\Group;
use App\Models\License;
use App\Models\LicenseSeat;
use App\Models\Location;
use App\Models\Maintenance;
use App\Models\Manufacturer;
use App\Models\PredefinedKit;
use App\Models\Statuslabel;
@@ -28,178 +27,225 @@ use Tabuna\Breadcrumbs\Trail;
class BreadcrumbsServiceProvider extends ServiceProvider
{
/**
* Handles the resource routes for first-class objects
*
* @return void
*/
/**
* Handles the resource routes for first-class objects
*
* @return void
*/
public function boot()
{
// Default home
Breadcrumbs::for('home', fn (Trail $trail) => $trail->push('<x-icon type="home" /><span class="sr-only">'.trans('general.dashboard').'</span>', route('home'))
Breadcrumbs::for('home', fn (Trail $trail) =>
$trail->push('<x-icon type="home" /><span class="sr-only">'.trans('general.dashboard').'</span>', route('home'))
);
/**
* Asset Breadcrumbs
*/
if ((request()->is('hardware*')) && (request()->status != '')) {
Breadcrumbs::for('hardware.index', fn (Trail $trail) => $trail->parent('home', route('home'))
->push(trans('general.assets'), route('hardware.index'))
->push(trans('general.'.strtolower(e(request()->status))), route('hardware.index', ['status' => request()->status]))
);
} else {
Breadcrumbs::for('hardware.index', fn (Trail $trail) => $trail->parent('home', route('home'))
->push(trans('general.assets'), route('hardware.index'))
);
}
Breadcrumbs::for('hardware.create', fn (Trail $trail) => $trail->parent('hardware.index', route('hardware.index'))
->push(trans('general.create'), route('hardware.create'))
if ((request()->is('hardware*')) && (request()->status!='')) {
Breadcrumbs::for('hardware.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.assets'), route('hardware.index'))
->push(trans('general.'.strtolower(e(request()->status))), route('hardware.index', ['status' => request()->status]))
);
} else {
Breadcrumbs::for('hardware.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.assets'), route('hardware.index'))
);
}
Breadcrumbs::for('hardware.create', fn (Trail $trail) =>
$trail->parent('hardware.index', route('hardware.index'))
->push(trans('general.create'), route('hardware.create'))
);
Breadcrumbs::for('clone/hardware', fn (Trail $trail) => $trail->parent('hardware.index', route('hardware.index'))
Breadcrumbs::for('clone/hardware', fn (Trail $trail) =>
$trail->parent('hardware.index', route('hardware.index'))
->push(trans('admin/hardware/general.clone'), route('hardware.create'))
);
Breadcrumbs::for('hardware.show', fn (Trail $trail, Asset $asset) => $trail->parent('hardware.index', route('hardware.index'))
Breadcrumbs::for('hardware.show', fn (Trail $trail, Asset $asset) =>
$trail->parent('hardware.index', route('hardware.index'))
->push($asset->display_name, route('hardware.show', $asset))
);
Breadcrumbs::for('hardware.edit', fn (Trail $trail, Asset $asset) => $trail->parent('hardware.index', route('hardware.index'))
Breadcrumbs::for('hardware.edit', fn (Trail $trail, Asset $asset) =>
$trail->parent('hardware.index', route('hardware.index'))
->push($asset->display_name, route('hardware.show', $asset))
->push(trans('general.update'))
);
/**
* Asset Model Breadcrumbs
*/
if ((request()->is('models*')) && (request()->status == 'deleted')) {
Breadcrumbs::for('models.index', fn (Trail $trail) => $trail->parent('hardware.index', route('hardware.index'))
if ((request()->is('models*')) && (request()->status=='deleted')) {
Breadcrumbs::for('models.index', fn (Trail $trail) =>
$trail->parent('hardware.index', route('hardware.index'))
->push(trans('general.asset_models'), route('models.index'))
->push(trans('general.deleted_models'), route('models.index', ['status' => request()->status]))
);
} else {
Breadcrumbs::for('models.index', fn (Trail $trail) => $trail->parent('hardware.index', route('hardware.index'))
Breadcrumbs::for('models.index', fn (Trail $trail) =>
$trail->parent('hardware.index', route('hardware.index'))
->push(trans('general.asset_models'), route('models.index'))
);
}
Breadcrumbs::for('models.create', fn (Trail $trail) => $trail->parent('models.index', route('models.index'))
Breadcrumbs::for('models.create', fn (Trail $trail) =>
$trail->parent('models.index', route('models.index'))
->push(trans('general.create'), route('models.create'))
);
Breadcrumbs::for('models.show', fn (Trail $trail, AssetModel $model) => $trail->parent('models.index', route('models.index'))
Breadcrumbs::for('models.show', fn (Trail $trail, AssetModel $model) =>
$trail->parent('models.index', route('models.index'))
->push($model->name, route('models.show', $model))
);
Breadcrumbs::for('models.edit', fn (Trail $trail, AssetModel $model) => $trail->parent('models.index', route('models.index'))
Breadcrumbs::for('models.edit', fn (Trail $trail, AssetModel $model) =>
$trail->parent('models.index', route('models.index'))
->push($model->display_name, route('models.show', $model))
->push(trans('general.update'))
);
/**
* Accessories Breadcrumbs
*/
Breadcrumbs::for('accessories.index', fn (Trail $trail) => $trail->parent('home', route('home'))
Breadcrumbs::for('accessories.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.accessories'), route('accessories.index'))
);
Breadcrumbs::for('accessories.create', fn (Trail $trail) => $trail->parent('accessories.index', route('accessories.index'))
Breadcrumbs::for('accessories.create', fn (Trail $trail) =>
$trail->parent('accessories.index', route('accessories.index'))
->push(trans('general.create'), route('accessories.create'))
);
Breadcrumbs::for('accessories.show', fn (Trail $trail, Accessory $accessory) => $trail->parent('accessories.index', route('accessories.index'))
Breadcrumbs::for('accessories.show', fn (Trail $trail, Accessory $accessory) =>
$trail->parent('accessories.index', route('accessories.index'))
->push($accessory->name, route('accessories.show', $accessory))
);
Breadcrumbs::for('accessories.edit', fn (Trail $trail, Accessory $accessory) => $trail->parent('accessories.index', route('accessories.index'))
Breadcrumbs::for('accessories.edit', fn (Trail $trail, Accessory $accessory) =>
$trail->parent('accessories.index', route('accessories.index'))
->push($accessory->display_name, route('accessories.show', $accessory))
->push(trans('general.update'))
);
/**
* Categories Breadcrumbs
*/
Breadcrumbs::for('categories.index', fn (Trail $trail) => $trail->parent('home', route('home'))
Breadcrumbs::for('categories.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.categories'), route('categories.index'))
);
Breadcrumbs::for('categories.create', fn (Trail $trail) => $trail->parent('categories.index', route('categories.index'))
Breadcrumbs::for('categories.create', fn (Trail $trail) =>
$trail->parent('categories.index', route('categories.index'))
->push(trans('general.create'), route('categories.create'))
);
Breadcrumbs::for('categories.show', fn (Trail $trail, Category $category) => $trail->parent('categories.index', route('categories.index'))
Breadcrumbs::for('categories.show', fn (Trail $trail, Category $category) =>
$trail->parent('categories.index', route('categories.index'))
->push($category->name, route('categories.show', $category))
);
Breadcrumbs::for('categories.edit', fn (Trail $trail, Category $category) => $trail->parent('categories.index', route('categories.index'))
Breadcrumbs::for('categories.edit', fn (Trail $trail, Category $category) =>
$trail->parent('categories.index', route('categories.index'))
->push($category->display_name, route('categories.show', $category))
->push(trans('general.update'))
);
/**
* Company Breadcrumbs
*/
Breadcrumbs::for('companies.index', fn (Trail $trail) => $trail->parent('home', route('home'))
Breadcrumbs::for('companies.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.companies'), route('companies.index'))
);
Breadcrumbs::for('companies.create', fn (Trail $trail) => $trail->parent('companies.index', route('companies.index'))
Breadcrumbs::for('companies.create', fn (Trail $trail) =>
$trail->parent('companies.index', route('companies.index'))
->push(trans('general.create'), route('companies.create'))
);
Breadcrumbs::for('companies.show', fn (Trail $trail, Company $company) => $trail->parent('companies.index', route('companies.index'))
Breadcrumbs::for('companies.show', fn (Trail $trail, Company $company) =>
$trail->parent('companies.index', route('companies.index'))
->push($company->name, route('companies.show', $company))
);
Breadcrumbs::for('companies.edit', fn (Trail $trail, Company $company) => $trail->parent('companies.index', route('companies.index'))
Breadcrumbs::for('companies.edit', fn (Trail $trail, Company $company) =>
$trail->parent('companies.index', route('companies.index'))
->push($company->display_name, route('companies.show', $company))
->push(trans('general.update'))
);
/**
* Components Breadcrumbs
*/
Breadcrumbs::for('components.index', fn (Trail $trail) => $trail->parent('home', route('home'))
Breadcrumbs::for('components.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.components'), route('components.index'))
);
Breadcrumbs::for('components.create', fn (Trail $trail) => $trail->parent('components.index', route('components.index'))
Breadcrumbs::for('components.create', fn (Trail $trail) =>
$trail->parent('components.index', route('components.index'))
->push(trans('general.create'), route('components.create'))
);
Breadcrumbs::for('components.show', fn (Trail $trail, Component $component) => $trail->parent('components.index', route('components.index'))
Breadcrumbs::for('components.show', fn (Trail $trail, Component $component) =>
$trail->parent('components.index', route('components.index'))
->push($component->name, route('components.show', $component))
);
Breadcrumbs::for('components.edit', fn (Trail $trail, Component $component) => $trail->parent('components.index', route('components.index'))
Breadcrumbs::for('components.edit', fn (Trail $trail, Component $component) =>
$trail->parent('components.index', route('components.index'))
->push($component->display_name, route('components.show', $component))
->push(trans('general.update'))
);
Breadcrumbs::for('components.clone.create', fn (Trail $trail, Component $component) => $trail->parent('components.index', route('components.index'))
Breadcrumbs::for('components.clone.create', fn (Trail $trail, Component $component) =>
$trail->parent('components.index', route('components.index'))
->push($component->display_name, route('components.show', $component))
->push(trans('general.clone'), route('components.create'))
);
/**
* Consumables Breadcrumbs
*/
Breadcrumbs::for('consumables.index', fn (Trail $trail) => $trail->parent('home', route('home'))
Breadcrumbs::for('consumables.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.consumables'), route('consumables.index'))
);
Breadcrumbs::for('consumables.create', fn (Trail $trail) => $trail->parent('consumables.index', route('consumables.index'))
Breadcrumbs::for('consumables.create', fn (Trail $trail) =>
$trail->parent('consumables.index', route('consumables.index'))
->push(trans('general.create'), route('consumables.create'))
);
Breadcrumbs::for('consumables.show', fn (Trail $trail, Consumable $consumable) => $trail->parent('consumables.index', route('consumables.index'))
Breadcrumbs::for('consumables.show', fn (Trail $trail, Consumable $consumable) =>
$trail->parent('consumables.index', route('consumables.index'))
->push($consumable->name, route('consumables.show', $consumable))
);
Breadcrumbs::for('consumables.edit', fn (Trail $trail, Consumable $consumable) => $trail->parent('consumables.index', route('consumables.index'))
Breadcrumbs::for('consumables.edit', fn (Trail $trail, Consumable $consumable) =>
$trail->parent('consumables.index', route('consumables.index'))
->push($consumable->display_name, route('consumables.show', $consumable))
->push(trans('general.update'))
);
@@ -207,30 +253,38 @@ class BreadcrumbsServiceProvider extends ServiceProvider
/**
* Custom fields Breadcrumbs
*/
Breadcrumbs::for('fields.index', fn (Trail $trail) => $trail->parent('models.index', route('models.index'))
Breadcrumbs::for('fields.index', fn (Trail $trail) =>
$trail->parent('models.index', route('models.index'))
->push(trans('admin/custom_fields/general.custom_fields'), route('fields.index'))
);
Breadcrumbs::for('fields.create', fn (Trail $trail) => $trail->parent('fields.index', route('fields.index'))
Breadcrumbs::for('fields.create', fn (Trail $trail) =>
$trail->parent('fields.index', route('fields.index'))
->push(trans('general.create'), route('fields.create'))
);
Breadcrumbs::for('fields.edit', fn (Trail $trail, CustomField $field) => $trail->parent('fields.index', route('fields.index'))
Breadcrumbs::for('fields.edit', fn (Trail $trail, CustomField $field) =>
$trail->parent('fields.index', route('fields.index'))
->push(trans('general.update')) // We skip the show section here since there isn't really a concept of fields.show
);
/**
* Custom fieldsets Breadcrumbs
*/
Breadcrumbs::for('fieldsets.create', fn (Trail $trail) => $trail->parent('fields.index', route('fields.index'))
Breadcrumbs::for('fieldsets.create', fn (Trail $trail) =>
$trail->parent('fields.index', route('fields.index'))
->push(trans('admin/custom_fields/general.create_fieldset'), route('fieldsets.create'))
);
Breadcrumbs::for('fieldsets.show', fn (Trail $trail, CustomFieldset $fieldset) => $trail->parent('fields.index', route('fields.index'))
Breadcrumbs::for('fieldsets.show', fn (Trail $trail, CustomFieldset $fieldset) =>
$trail->parent('fields.index', route('fields.index'))
->push($fieldset->name, route('fields.index'))
);
Breadcrumbs::for('fieldsets.edit', fn (Trail $trail, CustomFieldset $fieldset) => $trail->parent('fields.index', route('fields.index'))
Breadcrumbs::for('fieldsets.edit', fn (Trail $trail, CustomFieldset $fieldset) =>
$trail->parent('fields.index', route('fields.index'))
->push($fieldset->display_name, route('fieldsets.show', $fieldset))
->push(trans('general.update'))
);
@@ -238,39 +292,48 @@ class BreadcrumbsServiceProvider extends ServiceProvider
/**
* Department Breadcrumbs
*/
Breadcrumbs::for('departments.index', fn (Trail $trail) => $trail->parent('home', route('home'))
Breadcrumbs::for('departments.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.departments'), route('departments.index'))
);
Breadcrumbs::for('departments.create', fn (Trail $trail) => $trail->parent('departments.index', route('departments.index'))
Breadcrumbs::for('departments.create', fn (Trail $trail) =>
$trail->parent('departments.index', route('departments.index'))
->push(trans('general.create'), route('departments.create'))
);
Breadcrumbs::for('departments.show', fn (Trail $trail, Department $department) => $trail->parent('departments.index', route('departments.index'))
Breadcrumbs::for('departments.show', fn (Trail $trail, Department $department) =>
$trail->parent('departments.index', route('departments.index'))
->push($department->name, route('home'))
);
Breadcrumbs::for('departments.edit', fn (Trail $trail, Department $department) => $trail->parent('departments.index', route('departments.index'))
Breadcrumbs::for('departments.edit', fn (Trail $trail, Department $department) =>
$trail->parent('departments.index', route('departments.index'))
->push($department->display_name, route('departments.show', $department))
->push(trans('general.update'))
);
/**
* Department Breadcrumbs
*/
Breadcrumbs::for('depreciations.index', fn (Trail $trail) => $trail->parent('home', route('home'))
Breadcrumbs::for('depreciations.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.depreciations'), route('depreciations.index'))
);
Breadcrumbs::for('depreciations.create', fn (Trail $trail) => $trail->parent('depreciations.index', route('depreciations.index'))
Breadcrumbs::for('depreciations.create', fn (Trail $trail) =>
$trail->parent('depreciations.index', route('depreciations.index'))
->push(trans('general.create'), route('depreciations.create'))
);
Breadcrumbs::for('depreciations.show', fn (Trail $trail, Depreciation $depreciation) => $trail->parent('depreciations.index', route('depreciations.index'))
Breadcrumbs::for('depreciations.show', fn (Trail $trail, Depreciation $depreciation) =>
$trail->parent('depreciations.index', route('depreciations.index'))
->push($depreciation->name, route('depreciations.show', $depreciation))
);
Breadcrumbs::for('depreciations.edit', fn (Trail $trail, Depreciation $depreciation) => $trail->parent('depreciations.index', route('depreciations.index'))
Breadcrumbs::for('depreciations.edit', fn (Trail $trail, Depreciation $depreciation) =>
$trail->parent('depreciations.index', route('depreciations.index'))
->push($depreciation->display_name, route('depreciations.show', $depreciation))
->push(trans('general.update'))
);
@@ -278,75 +341,92 @@ class BreadcrumbsServiceProvider extends ServiceProvider
/**
* Groups Breadcrumbs
*/
Breadcrumbs::for('groups.index', fn (Trail $trail) => $trail->parent('settings.index', route('settings.index'))
Breadcrumbs::for('groups.index', fn (Trail $trail) =>
$trail->parent('settings.index', route('settings.index'))
->push(trans('general.groups'), route('groups.index'))
);
Breadcrumbs::for('groups.create', fn (Trail $trail) => $trail->parent('groups.index', route('groups.index'))
Breadcrumbs::for('groups.create', fn (Trail $trail) =>
$trail->parent('groups.index', route('groups.index'))
->push(trans('general.create'), route('groups.create'))
);
Breadcrumbs::for('groups.show', fn (Trail $trail, Group $group) => $trail->parent('groups.index', route('groups.index'))
Breadcrumbs::for('groups.show', fn (Trail $trail, Group $group) =>
$trail->parent('groups.index', route('groups.index'))
->push($group->name, route('groups.show', $group))
);
Breadcrumbs::for('groups.edit', fn (Trail $trail, Group $group) => $trail->parent('groups.index', route('groups.index'))
Breadcrumbs::for('groups.edit', fn (Trail $trail, Group $group) =>
$trail->parent('groups.index', route('groups.index'))
->push($group->display_name, route('groups.show', $group))
->push(trans('general.update'))
);
/**
* Licenses Breadcrumbs
*/
if ((request()->is('licenses*')) && (request()->status == 'inactive')) {
Breadcrumbs::for('licenses.index', fn (Trail $trail) => $trail->parent('home', route('home'))
if ((request()->is('licenses*')) && (request()->status=='inactive')) {
Breadcrumbs::for('licenses.index', fn(Trail $trail) => $trail->parent('home', route('home'))
->push(trans('general.licenses'), route('licenses.index'))
->push(trans('general.show_inactive'), route('licenses.index'))
);
} elseif ((request()->is('licenses*')) && (request()->status == 'expiring')) {
Breadcrumbs::for('licenses.index', fn (Trail $trail) => $trail->parent('home', route('home'))
} elseif ((request()->is('licenses*')) && (request()->status=='expiring')) {
Breadcrumbs::for('licenses.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.licenses'), route('licenses.index'))
->push(trans('general.show_expiring'), route('licenses.index'))
);
} else {
Breadcrumbs::for('licenses.index', fn (Trail $trail) => $trail->parent('home', route('home'))
Breadcrumbs::for('licenses.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.licenses'), route('licenses.index'))
);
}
Breadcrumbs::for('licenses.create', fn (Trail $trail) => $trail->parent('licenses.index', route('licenses.index'))
Breadcrumbs::for('licenses.create', fn (Trail $trail) =>
$trail->parent('licenses.index', route('licenses.index'))
->push(trans('general.create'), route('licenses.create'))
);
Breadcrumbs::for('licenses.show', fn (Trail $trail, License $license) => $trail->parent('licenses.index', route('licenses.index'))
Breadcrumbs::for('licenses.show', fn (Trail $trail, License $license) =>
$trail->parent('licenses.index', route('licenses.index'))
->push($license->name, route('licenses.show', $license))
);
Breadcrumbs::for('licenses.edit', fn (Trail $trail, License $license) => $trail->parent('licenses.index', route('licenses.index'))
Breadcrumbs::for('licenses.edit', fn (Trail $trail, License $license) =>
$trail->parent('licenses.index', route('licenses.index'))
->push($license->display_name, route('licenses.show', $license))
->push(trans('general.update'))
);
/**
* Locations Breadcrumbs
*/
Breadcrumbs::for('locations.index', fn (Trail $trail) => $trail->parent('home', route('home'))
Breadcrumbs::for('locations.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.locations'), route('locations.index'))
);
Breadcrumbs::for('locations.create', fn (Trail $trail) => $trail->parent('locations.index', route('locations.index'))
Breadcrumbs::for('locations.create', fn (Trail $trail) =>
$trail->parent('locations.index', route('locations.index'))
->push(trans('general.create'), route('locations.create'))
);
Breadcrumbs::for('clone/location', fn (Trail $trail) => $trail->parent('locations.index', route('locations.index'))
Breadcrumbs::for('clone/location', fn (Trail $trail) =>
$trail->parent('locations.index', route('locations.index'))
->push(trans('admin/locations/table.clone'), route('locations.create'))
);
Breadcrumbs::for('locations.show', fn (Trail $trail, Location $location) => $trail->parent('locations.index', route('locations.index'))
Breadcrumbs::for('locations.show', fn (Trail $trail, Location $location) =>
$trail->parent('locations.index', route('locations.index'))
->push($location->name, route('locations.show', $location))
);
Breadcrumbs::for('locations.edit', fn (Trail $trail, Location $location) => $trail->parent('locations.index', route('locations.index'))
Breadcrumbs::for('locations.edit', fn (Trail $trail, Location $location) =>
$trail->parent('locations.index', route('locations.index'))
->push($location->display_name, route('locations.show', $location))
->push(trans('general.update'))
);
@@ -354,160 +434,199 @@ class BreadcrumbsServiceProvider extends ServiceProvider
/**
* Maintenances Breadcrumbs
*/
Breadcrumbs::for('maintenances.index', fn (Trail $trail) => $trail->parent('hardware.index', route('hardware.index'))
Breadcrumbs::for('maintenances.index', fn (Trail $trail) =>
$trail->parent('hardware.index', route('hardware.index'))
->push(trans('general.maintenances'), route('maintenances.index'))
);
Breadcrumbs::for('maintenances.create', fn (Trail $trail) => $trail->parent('maintenances.index', route('maintenances.index'))
Breadcrumbs::for('maintenances.create', fn (Trail $trail) =>
$trail->parent('maintenances.index', route('maintenances.index'))
->push(trans('general.create'), route('maintenances.create'))
);
Breadcrumbs::for('maintenances.show', fn (Trail $trail, Maintenance $maintenance) => $trail->parent('maintenances.index', route('maintenances.index'))
Breadcrumbs::for('maintenances.show', fn (Trail $trail, Maintenance $maintenance) =>
$trail->parent('maintenances.index', route('maintenances.index'))
->push($maintenance->name, route('maintenances.show', $maintenance))
);
Breadcrumbs::for('maintenances.edit', fn (Trail $trail, Maintenance $maintenance) => $trail->parent('maintenances.index', route('maintenances.index'))
Breadcrumbs::for('maintenances.edit', fn (Trail $trail, Maintenance $maintenance) =>
$trail->parent('maintenances.index', route('maintenances.index'))
->push($maintenance->display_name, route('maintenances.show', $maintenance))
->push(trans('general.update'))
);
/**
* Manufacturers Breadcrumbs
*/
Breadcrumbs::for('manufacturers.index', fn (Trail $trail) => $trail->parent('home', route('home'))
Breadcrumbs::for('manufacturers.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.manufacturers'), route('manufacturers.index'))
);
Breadcrumbs::for('manufacturers.create', fn (Trail $trail) => $trail->parent('manufacturers.index', route('manufacturers.index'))
Breadcrumbs::for('manufacturers.create', fn (Trail $trail) =>
$trail->parent('manufacturers.index', route('manufacturers.index'))
->push(trans('general.create'), route('manufacturers.create'))
);
Breadcrumbs::for('manufacturers.show', fn (Trail $trail, Manufacturer $manufacturer) => $trail->parent('manufacturers.index', route('manufacturers.index'))
Breadcrumbs::for('manufacturers.show', fn (Trail $trail, Manufacturer $manufacturer) =>
$trail->parent('manufacturers.index', route('manufacturers.index'))
->push($manufacturer->name, route('home'))
);
Breadcrumbs::for('manufacturers.edit', fn (Trail $trail, Manufacturer $manufacturer) => $trail->parent('manufacturers.index', route('manufacturers.index'))
Breadcrumbs::for('manufacturers.edit', fn (Trail $trail, Manufacturer $manufacturer) =>
$trail->parent('manufacturers.index', route('manufacturers.index'))
->push($manufacturer->name, route('manufacturers.show', $manufacturer))
->push(trans('general.update'))
);
/**
* Predefined Kits Breadcrumbs
*/
Breadcrumbs::for('kits.index', fn (Trail $trail) => $trail->parent('home', route('home'))
Breadcrumbs::for('kits.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.kits'), route('kits.index'))
);
Breadcrumbs::for('kits.create', fn (Trail $trail) => $trail->parent('kits.index', route('kits.index'))
Breadcrumbs::for('kits.create', fn (Trail $trail) =>
$trail->parent('kits.index', route('kits.index'))
->push(trans('general.create'), route('kits.create'))
);
Breadcrumbs::for('kits.show', fn (Trail $trail, PredefinedKit $kit) => $trail->parent('kits.index', route('kits.index'))
Breadcrumbs::for('kits.show', fn (Trail $trail, PredefinedKit $kit) =>
$trail->parent('kits.index', route('kits.index'))
->push($kit->name, route('kits.show', $kit))
);
Breadcrumbs::for('kits.edit', fn (Trail $trail, PredefinedKit $kit) => $trail->parent('kits.index', route('kits.index'))
Breadcrumbs::for('kits.edit', fn (Trail $trail, PredefinedKit $kit) =>
$trail->parent('kits.index', route('kits.index'))
->push($kit->display_name, route('kits.show', $kit))
->push(trans('general.update'))
);
/**
* Status Labels Breadcrumbs
*/
Breadcrumbs::for('statuslabels.index', fn (Trail $trail) => $trail->parent('home', route('home'))
Breadcrumbs::for('statuslabels.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.status_labels'), route('statuslabels.index'))
);
Breadcrumbs::for('statuslabels.create', fn (Trail $trail) => $trail->parent('statuslabels.index', route('statuslabels.index'))
Breadcrumbs::for('statuslabels.create', fn (Trail $trail) =>
$trail->parent('statuslabels.index', route('statuslabels.index'))
->push(trans('general.create'), route('statuslabels.create'))
);
Breadcrumbs::for('statuslabels.show', fn (Trail $trail, Statuslabel $statuslabel) => $trail->parent('statuslabels.index', route('statuslabels.index'))
Breadcrumbs::for('statuslabels.show', fn (Trail $trail, Statuslabel $statuslabel) =>
$trail->parent('statuslabels.index', route('statuslabels.index'))
->push($statuslabel->name, route('statuslabels.show', $statuslabel))
);
Breadcrumbs::for('statuslabels.edit', fn (Trail $trail, Statuslabel $statuslabel) => $trail->parent('statuslabels.index', route('statuslabels.index'))
Breadcrumbs::for('statuslabels.edit', fn (Trail $trail, Statuslabel $statuslabel) =>
$trail->parent('statuslabels.index', route('statuslabels.index'))
->push($statuslabel->display_name, route('statuslabels.show', $statuslabel))
->push(trans('general.update'))
);
/**
* Settings Breadcrumbs
*/
Breadcrumbs::for('settings.index', fn (Trail $trail) => $trail->parent('home', route('home'))
Breadcrumbs::for('settings.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.admin'), route('settings.index'))
);
/**
* Suppliers Breadcrumbs
*/
Breadcrumbs::for('suppliers.index', fn (Trail $trail) => $trail->parent('home', route('home'))
Breadcrumbs::for('suppliers.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.suppliers'), route('suppliers.index'))
);
Breadcrumbs::for('suppliers.create', fn (Trail $trail) => $trail->parent('suppliers.index', route('suppliers.index'))
Breadcrumbs::for('suppliers.create', fn (Trail $trail) =>
$trail->parent('suppliers.index', route('suppliers.index'))
->push(trans('general.create'), route('suppliers.create'))
);
Breadcrumbs::for('suppliers.show', fn (Trail $trail, Supplier $supplier) => $trail->parent('suppliers.index', route('suppliers.index'))
Breadcrumbs::for('suppliers.show', fn (Trail $trail, Supplier $supplier) =>
$trail->parent('suppliers.index', route('suppliers.index'))
->push($supplier->name, route('home'))
);
Breadcrumbs::for('suppliers.edit', fn (Trail $trail, Supplier $supplier) => $trail->parent('suppliers.index', route('suppliers.index'))
Breadcrumbs::for('suppliers.edit', fn (Trail $trail, Supplier $supplier) =>
$trail->parent('suppliers.index', route('suppliers.index'))
->push($supplier->display_name, route('suppliers.show', $supplier))
->push(trans('general.update'))
);
/**
* Users Breadcrumbs
*/
if ((request()->is('users*')) && (request()->status == 'deleted')) {
Breadcrumbs::for('users.index', fn (Trail $trail) => $trail->parent('home', route('home'))
if ((request()->is('users*')) && (request()->status=='deleted')) {
Breadcrumbs::for('users.index', fn(Trail $trail) => $trail->parent('home', route('home'))
->push(trans('general.users'), route('users.index'))
->push(trans('general.deleted_users'), route('users.index'))
);
} elseif ((request()->is('users*')) && (request()->admins == 'true')) {
Breadcrumbs::for('users.index', fn (Trail $trail) => $trail->parent('home', route('home'))
} elseif ((request()->is('users*')) && (request()->admins=='true')) {
Breadcrumbs::for('users.index', fn(Trail $trail) => $trail->parent('home', route('home'))
->push(trans('general.users'), route('users.index'))
->push(trans('general.show_admins'), route('users.index'))
);
} elseif ((request()->is('users*')) && (request()->superadmins == 'true')) {
Breadcrumbs::for('users.index', fn (Trail $trail) => $trail->parent('home', route('home'))
} elseif ((request()->is('users*')) && (request()->superadmins=='true')) {
Breadcrumbs::for('users.index', fn(Trail $trail) => $trail->parent('home', route('home'))
->push(trans('general.users'), route('users.index'))
->push(trans('general.show_superadmins'), route('users.index'))
);
} elseif ((request()->is('users*')) && (request()->activated == '0')) {
Breadcrumbs::for('users.index', fn (Trail $trail) => $trail->parent('home', route('home'))
} elseif ((request()->is('users*')) && (request()->activated=='0')) {
Breadcrumbs::for('users.index', fn(Trail $trail) => $trail->parent('home', route('home'))
->push(trans('general.users'), route('users.index'))
->push(trans('general.login_disabled'), route('users.index'))
);
} elseif ((request()->is('users*')) && (request()->activated == '1')) {
Breadcrumbs::for('users.index', fn (Trail $trail) => $trail->parent('home', route('home'))
} elseif ((request()->is('users*')) && (request()->activated=='1')) {
Breadcrumbs::for('users.index', fn(Trail $trail) => $trail->parent('home', route('home'))
->push(trans('general.users'), route('users.index'))
->push(trans('general.login_enabled'), route('users.index'))
);
} else {
Breadcrumbs::for('users.index', fn (Trail $trail) => $trail->parent('home', route('home'))
Breadcrumbs::for('users.index', fn(Trail $trail) => $trail->parent('home', route('home'))
->push(trans('general.users'), route('users.index'))
);
}
Breadcrumbs::for('users.create', fn (Trail $trail) => $trail->parent('users.index', route('users.index'))
Breadcrumbs::for('users.create', fn (Trail $trail) =>
$trail->parent('users.index', route('users.index'))
->push(trans('general.create'), route('users.create'))
);
Breadcrumbs::for('users.clone.show', fn (Trail $trail) => $trail->parent('users.index', route('users.index'))
Breadcrumbs::for('users.clone.show', fn (Trail $trail) =>
$trail->parent('users.index', route('users.index'))
->push(trans('admin/users/general.clone'), route('users.create'))
);
Breadcrumbs::for('users.show', fn (Trail $trail, User $user) => $trail->parent('users.index', route('users.index'))
Breadcrumbs::for('users.show', fn (Trail $trail, User $user) =>
$trail->parent('users.index', route('users.index'))
->push($user->display_name ?? 'Missing Username!', route('users.show', $user))
);
Breadcrumbs::for('users.edit', fn (Trail $trail, User $user) => $trail->parent('users.index', route('users.index'))
Breadcrumbs::for('users.edit', fn (Trail $trail, User $user) =>
$trail->parent('users.index', route('users.index'))
->push($user->display_name, route('users.show', $user))
->push(trans('general.update'))
);
}
}
+4
View File
@@ -247,6 +247,10 @@ return [
'permission' => 'users.delete',
'display' => true,
],
[
'permission' => 'users.contact',
'display' => true,
],
],
+5
View File
@@ -109,6 +109,11 @@ class UserFactory extends Factory
];
});
}
public function manageContactInfo()
{
return $this->appendPermission(['users.contact' => '1']);
}
public function viewAssets()
{
@@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
}
/**
* Reverse the migrations.
*/
public function down(): void
{
}
};
+10
View File
@@ -225,6 +225,16 @@ return [
'usersdelete' => [
'name' => 'Delete Users',
],
'usersfiles' => [
'name' => 'Manage User Files',
'note' => 'Allows the user to view, upload, download, and delete files associated with users.',
],
'userscontact' => [
'name' => 'View/Edit User Contact Info',
'note' => 'Allows the user to view and edit personal contact information about the user. This includes: address, city, state/province, country, postal code, phone number, mobile number, email address, and website.',
],
'models' => [
'name' => 'Models',
'note' => 'Grants access to the Models section of the application.',
+82
View File
@@ -0,0 +1,82 @@
<?php
/**
* Macro helpers
*/
/**
<<<<<<< HEAD
* Barcode macro
* Generates the dropdown menu of available 2D barcodes
*/
Form::macro('barcode_types', function ($name = 'barcode_type', $selected = null, $class = null) {
$barcode_types = [
'QRCODE',
'DATAMATRIX',
];
$select = '<select name="'.$name.'" class="'.$class.'" aria-label="'.$name.'">';
foreach ($barcode_types as $barcode_type) {
$select .= '<option value="'.$barcode_type.'"'.($selected == $barcode_type ? ' selected="selected" role="option" aria-selected="true"' : ' aria-selected="false"').'>'.$barcode_type.'</option> ';
=======
* Country macro
* Generates the dropdown menu of countries for the profile form
*/
Form::macro('countries', function ($name = 'country', $selected = null, $class = null, $id = null) {
$idclause = (!is_null($id)) ? $id : '';
// Pull the autoglossonym array from the localizations translation file
$countries_array = trans('localizations.countries');
$select = '<select name="'.$name.'" class="'.$class.'" style="width:100%" '.$idclause.' aria-label="'.$name.'" data-placeholder="'.trans('localizations.select_country').'" data-allow-clear="true" data-tags="true">';
$select .= '<option value="" role="option">'.trans('localizations.select_country').'</option>';
foreach ($countries_array as $abbr => $country) {
// We have to handle it this way to handle deprecation warnings since you can't strtoupper on null
if ($abbr!='') {
$abbr = strtoupper($abbr);
}
// Loop through the countries configured in the localization file
$select .= '<option value="' . $abbr . '" role="option" ' . (($selected == $abbr) ? ' selected="selected" aria-selected="true"' : ' aria-selected="false"') . '>' . $country . '</option> ';
}
// If the country value doesn't exist in the array, add it as a new option and select it so we don't drop that data
if (!array_key_exists($selected, $countries_array)) {
$select .= '<option value="' . e($selected) . '" selected="selected" role="option" aria-selected="true">' . e($selected) .' *</option> ';
}
$select .= '</select>';
return $select;
});
Form::macro('username_format', function ($name = 'username_format', $selected = null, $class = null) {
$formats = [
'firstname.lastname' => trans('admin/settings/general.username_formats.firstname_lastname_format'),
'firstname' => trans('admin/settings/general.username_formats.first_name_format'),
'lastname' => trans('admin/settings/general.username_formats.last_name_format'),
'filastname' => trans('admin/settings/general.username_formats.filastname_format'),
'lastnamefirstinitial' => trans('admin/settings/general.username_formats.lastnamefirstinitial_format'),
'firstname_lastname' => trans('admin/settings/general.username_formats.firstname_lastname_underscore_format'),
'firstinitial.lastname' => trans('admin/settings/general.username_formats.firstinitial_lastname'),
'lastname_firstinitial' => trans('admin/settings/general.username_formats.lastname_firstinitial'),
'lastname.firstinitial' => trans('admin/settings/general.username_formats.lastname_dot_firstinitial_format'),
'firstnamelastname' => trans('admin/settings/general.username_formats.firstnamelastname'),
'firstnamelastinitial' => trans('admin/settings/general.username_formats.firstnamelastinitial'),
'lastname.firstname' => trans('admin/settings/general.username_formats.lastnamefirstname'),
];
$select = '<select name="'.$name.'" class="'.$class.'" style="width: 100%" aria-label="'.$name.'">';
foreach ($formats as $format => $label) {
$select .= '<option value="'.$format.'"'.($selected == $format ? ' selected="selected" role="option" aria-selected="true"' : ' aria-selected="false"').'>'.$label.'</option> '."\n";
>>>>>>> f3504ce6fc (Remove barcode_types macro)
}
$select .= '</select>';
return $select;
});
+35 -4
View File
@@ -19,8 +19,7 @@
margin-left: -20px;
}
</style>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<x-container class="col-md-6 col-md-offset-3">
<p>{{ trans('admin/users/general.bulk_update_help') }}</p>
@@ -108,6 +107,7 @@
</div>
</div>
@can('manageContactInfo', $user)
<!-- City -->
<div class="form-group{{ $errors->has('city') ? ' has-error' : '' }}">
<label class="col-md-3 control-label" for="city">{{ trans('general.city') }}</label>
@@ -117,6 +117,37 @@
</div>
</div>
<div class="form-group{{ $errors->has('state') ? ' has-error' : '' }}">
<label class="col-md-3 control-label" for="state">{{ trans('general.state') }}</label>
<div class="col-md-4">
<input class="form-control" type="text" name="state" id="state" aria-label="state" />
{!! $errors->first('state', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</div>
</div>
<!-- Country -->
<div class="form-group{{ $errors->has('country') ? ' has-error' : '' }}">
<label class="col-md-3 control-label" for="country">{{ trans('general.country') }}</label>
<div class="col-md-6">
<x-input.country-select
name="country"
class="col-md-12"
/>
<p class="help-block">{{ trans('general.countries_manually_entered_help') }}</p>
{!! $errors->first('country', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</div>
</div>
<div class="form-group{{ $errors->has('zip') ? ' has-error' : '' }}">
<label class="col-md-3 control-label" for="zip">{{ trans('general.zip') }}</label>
<div class="col-md-4">
<input class="form-control" type="text" name="zip" id="zip" aria-label="zip" />
{!! $errors->first('zip', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</div>
</div>
@endcan
<!-- remote -->
<div class="form-group">
<div class="col-sm-3 control-label">
@@ -162,7 +193,7 @@
</div>
</div> <!--/form-group-->
<!-- activated -->
<!-- autoassign -->
<div class="form-group">
<div class="col-sm-3 control-label">
{{ trans('general.autoassign_licenses') }}
@@ -303,5 +334,5 @@
</div> <!--/.box.box-default-->
</form>
</div> <!--/.col-md-8-->
</div>
</x-container>
@stop
+118 -115
View File
@@ -39,8 +39,8 @@
</style>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<x-container class="col-md-6 col-md-offset-3">
<form class="form-horizontal" method="post" autocomplete="off"
action="{{ (isset($user->id)) ? route('users.update', ['user' => $user->id]) : route('users.store') }}"
enctype="multipart/form-data" id="userForm">
@@ -237,57 +237,58 @@
</div>
</div>
<!-- Email -->
<div class="form-group {{ $errors->has('email') ? 'has-error' : '' }}">
<label class="col-md-3 control-label" for="email">{{ trans('admin/users/table.email') }} </label>
<div class="col-md-6">
<input class="form-control" type="email" name="email" id="email" maxlength="191" value="{{ old('email', $user->email) }}" autocomplete="off"
readonly onfocus="this.removeAttribute('readonly');" {{ (Helper::checkIfRequired($user, 'email')) ? ' required' : '' }}{!! (!Gate::allows('canEditAuthFields', $user)) || ((!Gate::allows('editableOnDemo')) && ($user->id)) ? ' style="cursor: not-allowed" disabled ' : '' !!}>
@can('manageContactInfo')
<!-- Email -->
<div class="form-group {{ $errors->has('email') ? 'has-error' : '' }}">
<label class="col-md-3 control-label" for="email">{{ trans('admin/users/table.email') }} </label>
<div class="col-md-6">
<input class="form-control" type="email" name="email" id="email" maxlength="191" value="{{ old('email', $user->email) }}" autocomplete="off"
readonly onfocus="this.removeAttribute('readonly');" {{ (Helper::checkIfRequired($user, 'email')) ? ' required' : '' }}{!! (!Gate::allows('canEditAuthFields', $user)) || ((!Gate::allows('editableOnDemo')) && ($user->id)) ? ' style="cursor: not-allowed" disabled ' : '' !!}>
@cannot('canEditAuthFields', $user)
<!-- authed user is an admin or regular user and is trying to edit someone higher -->
<p class="help-block">
<x-icon type="locked" />
{{ trans('general.action_permission_generic', ['action' => trans('general.edit'), 'item_type' => trans('general.email')]) }}
</p>
@endcannot
@cannot('canEditAuthFields', $user)
<!-- authed user is an admin or regular user and is trying to edit someone higher -->
<p class="help-block">
<x-icon type="locked" />
{{ trans('general.action_permission_generic', ['action' => trans('general.edit'), 'item_type' => trans('general.email')]) }}
</p>
@endcannot
@if (!Gate::allows('editableOnDemo') && ($user->id))
<p class="text-warning">
<x-icon type="locked" />
{{ trans('admin/users/table.lock_passwords') }}
</p>
@endif
@if (!Gate::allows('editableOnDemo') && ($user->id))
<p class="text-warning">
<x-icon type="locked" />
{{ trans('admin/users/table.lock_passwords') }}
</p>
@endif
{!! $errors->first('email', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
{!! $errors->first('email', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</div>
</div>
</div>
</div>
<!-- Send welcome email to user -->
@if (!$user->id)
<div class="form-group" id="email_user_row">
<!-- Send welcome email to user -->
@if (!$user->id)
<div class="form-group" id="email_user_row">
<div class="col-md-8 col-md-offset-3">
<label class="form-control form-control--disabled">
<input
type="checkbox"
name="send_welcome"
id="email_user_checkbox"
value="1"
aria-label="send_welcome"
@checked(old('send_welcome'))
/>
{{ trans('general.send_welcome_email_to_users') }}
</label>
<div class="col-md-8 col-md-offset-3">
<label class="form-control form-control--disabled">
<input
type="checkbox"
name="send_welcome"
id="email_user_checkbox"
value="1"
aria-label="send_welcome"
@checked(old('send_welcome'))
/>
{{ trans('general.send_welcome_email_to_users') }}
</label>
<p class="help-block"> {{ trans('general.send_welcome_email_help') }}</p>
<p class="help-block"> {{ trans('general.send_welcome_email_help') }}</p>
</div>
</div> <!--/form-group-->
@endif
</div>
</div> <!--/form-group-->
@endif
@endcan
@include ('partials.forms.edit.image-upload', ['fieldname' => 'avatar', 'image_path' => app('users_upload_path')])
@@ -444,92 +445,94 @@
<!-- Location -->
@include ('partials.forms.edit.location-select', ['translated_name' => trans('general.location'), 'fieldname' => 'location_id'])
<!-- Phone -->
<div class="form-group {{ $errors->has('phone') ? 'has-error' : '' }}">
<label class="col-md-3 control-label" for="phone">{{ trans('admin/users/table.phone') }}</label>
<div class="col-md-6">
<input class="form-control" type="text" name="phone" id="phone" value="{{ old('phone', $user->phone) }}" maxlength="191" />
{!! $errors->first('phone', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
@can('manageContactInfo')
<!-- Phone -->
<div class="form-group {{ $errors->has('phone') ? 'has-error' : '' }}">
<label class="col-md-3 control-label" for="phone">{{ trans('admin/users/table.phone') }}</label>
<div class="col-md-6">
<input class="form-control" type="text" name="phone" id="phone" value="{{ old('phone', $user->phone) }}" maxlength="191" />
{!! $errors->first('phone', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</div>
</div>
</div>
<!-- Mobile -->
<div class="form-group {{ $errors->has('mobile') ? 'has-error' : '' }}">
<label class="col-md-3 control-label" for="phone">{{ trans('admin/users/table.mobile') }}</label>
<div class="col-md-6">
<input class="form-control" type="text" name="mobile" id="mobile" value="{{ old('mobile', $user->mobile) }}" maxlength="191" />
{!! $errors->first('mobile', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
<!-- Mobile -->
<div class="form-group {{ $errors->has('mobile') ? 'has-error' : '' }}">
<label class="col-md-3 control-label" for="phone">{{ trans('admin/users/table.mobile') }}</label>
<div class="col-md-6">
<input class="form-control" type="text" name="mobile" id="mobile" value="{{ old('mobile', $user->mobile) }}" maxlength="191" />
{!! $errors->first('mobile', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</div>
</div>
</div>
<!-- Website URL -->
<div class="form-group {{ $errors->has('website') ? ' has-error' : '' }}">
<label for="website" class="col-md-3 control-label">{{ trans('general.website') }}</label>
<div class="col-md-6">
<input class="form-control" type="url" name="website" id="website" value="{{ old('website', $user->website) }}" maxlength="191" />
{!! $errors->first('website', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
<!-- Website URL -->
<div class="form-group {{ $errors->has('website') ? ' has-error' : '' }}">
<label for="website" class="col-md-3 control-label">{{ trans('general.website') }}</label>
<div class="col-md-6">
<input class="form-control" type="url" name="website" id="website" value="{{ old('website', $user->website) }}" maxlength="191" />
{!! $errors->first('website', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
</div>
</div>
</div>
<!-- Address -->
<div class="form-group{{ $errors->has('address') ? ' has-error' : '' }}">
<label class="col-md-3 control-label" for="address">{{ trans('general.address') }}</label>
<div class="col-md-6">
<input class="form-control" type="text" name="address" id="address" value="{{ old('address', $user->address) }}" maxlength="191" />
{!! $errors->first('address', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
<!-- Address -->
<div class="form-group{{ $errors->has('address') ? ' has-error' : '' }}">
<label class="col-md-3 control-label" for="address">{{ trans('general.address') }}</label>
<div class="col-md-6">
<input class="form-control" type="text" name="address" id="address" value="{{ old('address', $user->address) }}" maxlength="191" />
{!! $errors->first('address', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</div>
</div>
</div>
<!-- City -->
<div class="form-group{{ $errors->has('city') ? ' has-error' : '' }}">
<label class="col-md-3 control-label" for="city">{{ trans('general.city') }}</label>
<div class="col-md-6">
<input class="form-control" type="text" name="city" id="city" aria-label="city" value="{{ old('city', $user->city) }}" maxlength="191" />
{!! $errors->first('city', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
<!-- City -->
<div class="form-group{{ $errors->has('city') ? ' has-error' : '' }}">
<label class="col-md-3 control-label" for="city">{{ trans('general.city') }}</label>
<div class="col-md-6">
<input class="form-control" type="text" name="city" id="city" aria-label="city" value="{{ old('city', $user->city) }}" maxlength="191" />
{!! $errors->first('city', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</div>
</div>
</div>
<!-- State -->
<div class="form-group{{ $errors->has('state') ? ' has-error' : '' }}">
<label class="col-md-3 control-label" for="state">{{ trans('general.state') }}</label>
<div class="col-md-6">
<input class="form-control" type="text" name="state" id="state" value="{{ old('state', $user->state) }}" maxlength="191" />
{!! $errors->first('state', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
<!-- State -->
<div class="form-group{{ $errors->has('state') ? ' has-error' : '' }}">
<label class="col-md-3 control-label" for="state">{{ trans('general.state') }}</label>
<div class="col-md-6">
<input class="form-control" type="text" name="state" id="state" value="{{ old('state', $user->state) }}" maxlength="191" />
{!! $errors->first('state', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</div>
</div>
</div>
<!-- Country -->
<div class="form-group{{ $errors->has('country') ? ' has-error' : '' }}">
<label class="col-md-3 control-label" for="country">{{ trans('general.country') }}</label>
<div class="col-md-6">
<x-input.country-select
name="country"
:selected="old('country', $user->country)"
class="col-md-12"
/>
<!-- Country -->
<div class="form-group{{ $errors->has('country') ? ' has-error' : '' }}">
<label class="col-md-3 control-label" for="country">{{ trans('general.country') }}</label>
<div class="col-md-6">
<x-input.country-select
name="country"
:selected="old('country', $user->country)"
class="col-md-12"
/>
<p class="help-block">{{ trans('general.countries_manually_entered_help') }}</p>
{!! $errors->first('country', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
<p class="help-block">{{ trans('general.countries_manually_entered_help') }}</p>
{!! $errors->first('country', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</div>
</div>
</div>
<!-- Zip -->
<div class="form-group{{ $errors->has('zip') ? ' has-error' : '' }}">
<label class="col-md-3 control-label" for="zip">{{ trans('general.zip') }}</label>
<div class="col-md-3 text-right">
<input class="form-control" type="text" name="zip" id="zip" value="{{ old('zip', $user->zip) }}" maxlength="10" />
{!! $errors->first('zip', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
<!-- Zip -->
<div class="form-group{{ $errors->has('zip') ? ' has-error' : '' }}">
<label class="col-md-3 control-label" for="zip">{{ trans('general.zip') }}</label>
<div class="col-md-3 text-right">
<input class="form-control" type="text" name="zip" id="zip" value="{{ old('zip', $user->zip) }}" maxlength="10" />
{!! $errors->first('zip', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</div>
</div>
</div>
@endcan
<!-- Notes -->
<div class="form-group{!! $errors->has('notes') ? ' has-error' : '' !!}">
<label for="notes" class="col-md-3 control-label">{{ trans('admin/users/table.notes') }}</label>
<div class="col-md-6">
<textarea class="form-control" rows="5" id="notes" name="notes">{{ old('notes', $user->notes) }}</textarea>
{!! $errors->first('notes', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
</div>
</div>
<!-- Notes -->
<x-form.row
:label="trans('general.notes')"
:item="$user"
name="notes"
type="textarea"
placeholder="{{ trans('general.placeholders.notes') }}"
/>
@if ($snipeSettings->two_factor_enabled!='')
@if ($snipeSettings->two_factor_enabled=='1')
@@ -583,7 +586,7 @@
<label class="col-md-3 control-label" for="groups[]">
{{ trans('general.groups') }}
</label>
<div class="col-md-6">
<div class="col-md-8">
@if ($groups->count())
@if ((!Gate::allows('editableOnDemo') || (!Auth::user()->isSuperUser())))
@@ -676,7 +679,7 @@
</div><!-- nav-tabs-custom -->
</form>
</div> <!--/col-md-8-->
</div><!--/row-->
</x-container>
@stop
@section('moar_scripts')
+22 -13
View File
@@ -83,16 +83,18 @@
</a>
</li>
<li>
<a href="#files" data-toggle="tab">
<span class="hidden-lg hidden-md">
<x-icon type="files" class="fa-2x" />
</span>
<span class="hidden-xs hidden-sm">{{ trans('general.file_uploads') }}
{!! ($user->uploads->count() > 0 ) ? '<span class="badge badge-secondary">'.number_format($user->uploads->count()).'</span>' : '' !!}
</span>
</a>
</li>
@can('update', $user)
<li>
<a href="#files" data-toggle="tab">
<span class="hidden-lg hidden-md">
<x-icon type="files" class="fa-2x" />
</span>
<span class="hidden-xs hidden-sm">{{ trans('general.file_uploads') }}
{!! ($user->uploads->count() > 0 ) ? '<span class="badge badge-secondary">'.number_format($user->uploads->count()).'</span>' : '' !!}
</span>
</a>
</li>
@endcan
<li>
<a href="#eulas" data-toggle="tab">
@@ -160,7 +162,7 @@
</li>
@endcan
@can('update', \App\Models\User::class)
@can('update', $user)
<li class="pull-right">
<a href="#" data-toggle="modal" data-target="#uploadFileModal">
<span class="hidden-xs"><x-icon type="paperclip" /></span>
@@ -375,6 +377,7 @@
</div>
@endif
@can('manageContactInfo')
<!-- address -->
@if (($user->address) || ($user->city) || ($user->state) || ($user->country))
<div class="row">
@@ -402,6 +405,7 @@
</div>
</div>
@endif
@endcan
<!-- company -->
@if (!is_null($user->company))
@@ -512,6 +516,7 @@
@endif
@can('manageContactInfo')
@if ($user->email)
<!-- email -->
<div class="row">
@@ -526,6 +531,7 @@
</div>
@endif
@if ($user->website)
<!-- website -->
<div class="row">
@@ -565,6 +571,7 @@
</div>
</div>
@endif
@endcan
@if ($user->userloc)
<!-- location -->
<div class="row">
@@ -997,6 +1004,7 @@
</table>
</div><!-- /consumables-tab -->
@can('manageFiles', $user)
<div class="tab-pane" id="files">
<div class="row">
@@ -1005,6 +1013,7 @@
</div>
</div> <!--/ROW-->
</div><!--/FILES-->
@endcan
<div class="tab-pane" id="eulas">
<table
@@ -1105,7 +1114,7 @@
</div><!-- nav-tabs-custom -->
</div>
@can('update', \App\Models\User::class)
@can('update', $user)
@include ('modals.upload-file', ['item_type' => 'user', 'item_id' => $user->id])
@endcan
@@ -1114,7 +1123,7 @@
@stop
@section('moar_scripts')
@include ('partials.bootstrap-table', ['simple_view' => true])
@include ('partials.bootstrap-table', ['simple_view' => true])
<script nonce="{{ csrf_token() }}">
$(function () {
@@ -44,7 +44,7 @@ class UsersForSelectListTest extends TestCase
{
User::factory()->create(['first_name' => 'Luke', 'last_name' => 'Skywalker', 'email' => 'luke@jedis.org']);
Passport::actingAs(User::factory()->create());
Passport::actingAs(User::factory()->manageContactInfo()->create());
$response = $this->getJson(route('api.users.selectlist', ['search' => 'luke@jedis']))->assertOk();
$results = collect($response->json('results'));
@@ -53,6 +53,18 @@ class UsersForSelectListTest extends TestCase
$this->assertTrue($results->pluck('text')->contains(fn($text) => str_contains($text, 'Luke')));
}
public function testUsersCannotBeSearchedByEmailWithoutPermission()
{
User::factory()->create(['first_name' => 'Luke', 'last_name' => 'Skywalker', 'email' => 'luke@jedis.org']);
Passport::actingAs(User::factory()->create());
$response = $this->getJson(route('api.users.selectlist', ['search' => 'luke@jedis']))->assertOk();
$results = collect($response->json('results'));
$this->assertEquals(0, $results->count());
}
public function testUsersScopedToCompanyWhenMultipleFullCompanySupportEnabled()
{
$this->settings->enableMultipleFullCompanySupport();
+39 -2
View File
@@ -29,7 +29,7 @@ class CreateUserTest extends TestCase
{
Notification::fake();
$response = $this->actingAs(User::factory()->createUsers()->viewUsers()->create())
$response = $this->actingAs(User::factory()->createUsers()->viewUsers()->manageContactInfo()->create())
->from(route('users.index'))
->post(route('users.store'), [
'first_name' => 'Test First Name',
@@ -59,12 +59,49 @@ class CreateUserTest extends TestCase
}
public function testCannotUpdateContactWithoutPermission()
{
Notification::fake();
$response = $this->actingAs(User::factory()->createUsers()->viewUsers()->create())
->from(route('users.index'))
->post(route('users.store'), [
'first_name' => 'Test First Name',
'last_name' => 'Test Last Name',
'username' => 'testuser',
'password' => 'testpassword1235!!',
'password_confirmation' => 'testpassword1235!!',
'activated' => '1',
'email' => 'foo@example.org',
'notes' => 'Test Note',
])
->assertSessionHasNoErrors()
->assertStatus(302)
->assertRedirect(route('users.index'));
$this->assertDatabaseHas('users', [
'first_name' => 'Test First Name',
'last_name' => 'Test Last Name',
'username' => 'testuser',
'activated' => '1',
'email' => null,
'notes' => 'Test Note',
]);
Notification::assertNothingSent();
$this->followRedirects($response)->assertSee('Success');
}
public function testCanCreateAndNotifyUser()
{
Notification::fake();
$admin = User::factory()->createUsers()->viewUsers()->manageContactInfo()->create();
$response = $this->actingAs(User::factory()->createUsers()->viewUsers()->create())
// dd($admin);
$response = $this->actingAs($admin)
->from(route('users.index'))
->post(route('users.store'), [
'first_name' => 'Test First Name',
+1
View File
@@ -38,4 +38,5 @@ class ViewUserTest extends TestCase
->get(route('users.show', $user))
->assertStatus(302);
}
}