Merge pull request #18170 from grokability/fix-for-groups

Fixed #18157 - only partial information stored on group save if lower `max_input_vars` and/or `max_multipart_body_parts`
This commit is contained in:
snipe
2025-11-06 21:09:42 +00:00
committed by GitHub
10 changed files with 254 additions and 63 deletions
@@ -24,7 +24,7 @@ class GroupsController extends Controller
$this->authorize('view', Group::class);
$groups = Group::select('id', 'name', 'permissions', 'notes', 'created_at', 'updated_at', 'created_by')->with('adminuser')->withCount('users as users_count');
$groups = Group::select(['id', 'name', 'permissions', 'notes', 'created_at', 'updated_at', 'created_by'])->with('adminuser')->withCount('users as users_count');
if ($request->filled('search')) {
$groups = $groups->TextSearch($request->input('search'));
@@ -50,6 +50,7 @@ class GroupsController extends Controller
'id',
'name',
'created_at',
'updated_at',
'users_count',
];
+35 -8
View File
@@ -43,9 +43,12 @@ class GroupsController extends Controller
$permissions = config('permissions');
$groupPermissions = Helper::selectedPermissionsArray($permissions, $permissions);
$selectedPermissions = $request->old('permissions', $groupPermissions);
$users = \App\Models\User::orderBy('first_name', 'asc')->orderBy('last_name', 'asc')->get();
// Show the page
return view('groups/edit', compact('permissions', 'selectedPermissions', 'groupPermissions'))->with('group', $group);
return view('groups/edit', compact('permissions', 'selectedPermissions', 'groupPermissions'))
->with('group', $group)
->with('associated_users', [])
->with('unselected_users', $users);
}
/**
@@ -60,12 +63,23 @@ class GroupsController extends Controller
// create a new group instance
$group = new Group();
$group->name = $request->input('name');
if ($request->filled('permission')) {
$group->permissions = json_encode($request->array('permission'));
} else {
$group->permissions = null;
}
$group->permissions = json_encode($request->input('permission'));
$group->created_by = auth()->id();
$group->notes = $request->input('notes');
if ($group->save()) {
$group->users()->sync($request->input('associated_users'));
if ($request->filled('users_to_sync')) {
$associated_users = explode(',',$request->input('users_to_sync'));
$group->users()->sync($associated_users);
}
return redirect()->route('groups.index')->with('success', trans('admin/groups/message.success.create'));
}
@@ -89,10 +103,12 @@ class GroupsController extends Controller
$groupPermissions = [];
}
$selected_array = Helper::selectedPermissionsArray($permissions, $groupPermissions);
$associated_users = $group->users()->get();
$associated_users = $group->users()->orderBy('first_name', 'asc')->orderBy('last_name', 'asc')->get();
//dd($associated_users->toArray());
return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions'))->with('associated_users', $associated_users);
// Get the unselected users
$unselected_users = \App\Models\User::whereNotIn('id', $associated_users->pluck('id')->toArray())->orderBy('first_name', 'asc')->orderBy('last_name', 'asc')->get();
return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions'))->with('associated_users', $associated_users)->with('unselected_users', $unselected_users);
}
/**
@@ -106,13 +122,24 @@ class GroupsController extends Controller
public function update(Request $request, Group $group) : RedirectResponse
{
$group->name = $request->input('name');
$group->permissions = json_encode($request->input('permission'));
if ($request->filled('permission')) {
$group->permissions = json_encode($request->array('permission'));
} else {
$group->permissions = null;
}
$group->notes = $request->input('notes');
if (! config('app.lock_passwords')) {
if ($group->save()) {
$group->users()->sync($request->input('associated_users'));
if ($request->filled('users_to_sync')) {
$associated_users = explode(',',$request->input('users_to_sync'));
$group->users()->sync($associated_users);
}
return redirect()->route('groups.index')->with('success', trans('admin/groups/message.success.update'));
}
+1 -1
View File
@@ -68,7 +68,7 @@ class UserSeeder extends Seeder
]))
->create();
User::factory()->count(50)->viewAssets()
User::factory()->count(2000)->viewAssets()
->state(new Sequence(fn($sequence) => [
'company_id' => $companyIds->random(),
'department_id' => $departmentIds->random(),
+66 -10
View File
@@ -52833,27 +52833,20 @@ $(".remember-toggle").on("click", function () {
var toggle_content_class = 'toggle-content-' + $(this).attr('id');
var toggle_arrow = '#toggle-arrow-' + toggleable_callout_id;
var toggle_cookie_name = 'toggle_state_' + toggleable_callout_id;
console.log('Callout ID: ' + toggleable_callout_id);
console.log('Content ID: ' + toggle_content_class);
console.log('Arrow ID: ' + toggle_arrow);
console.log('Cookie Name: ' + toggle_cookie_name);
$('.' + toggle_content_class).fadeToggle(100);
$(toggle_arrow).toggleClass('fa-caret-right fa-caret-down');
var toggle_open = $(toggle_arrow).hasClass('fa-caret-down');
console.log('Cookie will set open state to: ' + toggle_open);
document.cookie = toggle_cookie_name + "=" + toggle_open + ';path=/';
});
var all_cookies = document.cookie.split(';');
for (var i in all_cookies) {
var trimmed_cookie = all_cookies[i].trim(' ');
elems = all_cookies[i].split('=', 2);
elems = trimmed_cookie.split('=', 2);
// We have to do more here since we don't know the name of the selector
if (trimmed_cookie.startsWith('toggle_state_')) {
console.log(trimmed_cookie + ' matches toggle_state_');
var toggle_selector_name = elems[0].replace(' toggle_state_', '');
if (elems[1] == 'true') {
console.log('Selector name for cookie click trigger: ' + toggle_selector_name);
var toggle_selector_name = elems[0].replace('toggle_state_', '');
if (elems[1] != "true") {
$('#' + toggle_selector_name + '.remember-toggle').trigger('click');
}
}
@@ -52902,6 +52895,69 @@ $(".admin").change(function () {
}
});
// Handle the select/deselect of the select boxes with the button from right to left
$(function () {
function moveItems(origin, dest) {
$(origin).find(':selected').appendTo(dest);
$(dest).attr('selected', true);
$(dest).sort_select_box();
}
function moveAllItems(origin, dest) {
$(origin).children("option:visible").appendTo(dest);
$(dest).attr('selected', true);
$(dest).sort_select_box();
}
$('.left').on('click', function () {
var container = $(this).closest('.addremove-multiselect');
moveItems($(container).find('select.multiselect.selected'), $(container).find('select.multiselect.available'));
});
$('.right').on('click', function () {
var container = $(this).closest('.addremove-multiselect');
moveItems($(container).find('select.multiselect.available'), $(container).find('select.multiselect.selected'));
});
$('.leftall').on('click', function () {
var container = $(this).closest('.addremove-multiselect');
moveAllItems($(container).find('select.multiselect.selected'), $(container).find('select.multiselect.available'));
});
$('.rightall').on('click', function () {
var container = $(this).closest('.addremove-multiselect');
moveAllItems($(container).find('select.multiselect.available'), $(container).find('select.multiselect.selected'));
});
$('select.multiselect.selected').on('dblclick keyup', function (e) {
if (e.which == 13 || e.type == 'dblclick') {
var container = $(this).closest('.addremove-multiselect');
moveItems($(container).find('select.multiselect.selected'), $(container).find('select.multiselect.available'));
}
});
$('select.multiselect.available').on('dblclick keyup', function (e) {
if (e.which == 13 || e.type == 'dblclick') {
var container = $(this).closest('.addremove-multiselect');
moveItems($(container).find('select.multiselect.available'), $(container).find('select.multiselect.selected'));
$('#hidden_ids_box').val($('#selected-select').val());
}
});
});
$.fn.sort_select_box = function () {
// Get options from select box
var selected_options = $(this).children('option');
// sort alphabetically
selected_options.sort(function (a, b) {
if (a.text > b.text) return 1;else if (a.text < b.text) return -1;else return 0;
});
//replace with sorted my_options;
$(this).empty().append(selected_options);
var selected_in_box = $('#selected-select option').toArray().map(function (item) {
return item.value;
}).join();
$('#hidden_ids_box').empty().val(selected_in_box);
$('#count_selected_box').html($('#selected-select option').length);
$('#count_unselected_box').html($('#available-select option').length);
// clearing any selections
$("#" + this.attr('id') + " option").attr('selected', true);
};
/***/ }),
/***/ "./resources/assets/js/snipeit_modals.js":
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,5 +1,5 @@
{
"/js/dist/all.js": "/js/dist/all.js?id=e78128438f0b07a393dc7947ef453b5f",
"/js/dist/all.js": "/js/dist/all.js?id=525664c0ec56444ba1c48c125346918a",
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=42f97cd5b9ee7521b04a448e7fc16ac9",
"/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=3e8b8221c159b829a0edd562eb717563",
"/css/build/overrides.css": "/css/build/overrides.css?id=31f0c9a27245a3b3a37a7d08ba914311",
+83 -12
View File
@@ -625,35 +625,29 @@ $('.header-row input:radio').change(function() {
// Generic toggleable callouts with remember state
$(".remember-toggle").on("click",function(){
var toggleable_callout_id = $(this).attr('id');
var toggle_content_class = 'toggle-content-'+$(this).attr('id');
var toggle_arrow = '#toggle-arrow-' + toggleable_callout_id;
var toggle_cookie_name='toggle_state_'+toggleable_callout_id;
console.log('Callout ID: ' + toggleable_callout_id);
console.log('Content ID: '+toggle_content_class);
console.log('Arrow ID: '+toggle_arrow);
console.log('Cookie Name: '+toggle_cookie_name);
$('.'+toggle_content_class).fadeToggle(100);
$(toggle_arrow).toggleClass('fa-caret-right fa-caret-down');
var toggle_open = $(toggle_arrow).hasClass('fa-caret-down');
console.log('Cookie will set open state to: '+toggle_open);
document.cookie=toggle_cookie_name+"="+toggle_open+';path=/';
});
var all_cookies = document.cookie.split(';')
for (var i in all_cookies) {
var trimmed_cookie = all_cookies[i].trim(' ')
elems = all_cookies[i].split('=', 2);
elems = trimmed_cookie.split('=', 2);
// We have to do more here since we don't know the name of the selector
if (trimmed_cookie.startsWith('toggle_state_')) {
console.log(trimmed_cookie + ' matches toggle_state_');
var toggle_selector_name = elems[0].replace(' toggle_state_','');
if (elems[1] == 'true') {
console.log('Selector name for cookie click trigger: '+toggle_selector_name);
var toggle_selector_name = elems[0].replace('toggle_state_','');
if (elems[1] != "true") {
$('#'+toggle_selector_name+'.remember-toggle').trigger('click')
}
}
@@ -714,3 +708,80 @@ $(".admin").change(function() {
}
});
// Handle the select/deselect of the select boxes with the button from right to left
$(function () {
function moveItems(origin, dest) {
$(origin).find(':selected').appendTo(dest);
$(dest).attr('selected', true);
$(dest).sort_select_box();
}
function moveAllItems(origin, dest) {
$(origin).children("option:visible").appendTo(dest);
$(dest).attr('selected', true);
$(dest).sort_select_box();
}
$('.left').on('click', function () {
var container = $(this).closest('.addremove-multiselect');
moveItems($(container).find('select.multiselect.selected'), $(container).find('select.multiselect.available'));
});
$('.right').on('click', function () {
var container = $(this).closest('.addremove-multiselect');
moveItems($(container).find('select.multiselect.available'), $(container).find('select.multiselect.selected'));
});
$('.leftall').on('click', function () {
var container = $(this).closest('.addremove-multiselect');
moveAllItems($(container).find('select.multiselect.selected'), $(container).find('select.multiselect.available'));
});
$('.rightall').on('click', function () {
var container = $(this).closest('.addremove-multiselect');
moveAllItems($(container).find('select.multiselect.available'), $(container).find('select.multiselect.selected'));
});
$('select.multiselect.selected').on('dblclick keyup',function(e){
if(e.which == 13 || e.type == 'dblclick') {
var container = $(this).closest('.addremove-multiselect');
moveItems($(container).find('select.multiselect.selected'), $(container).find('select.multiselect.available'));
}
});
$('select.multiselect.available').on('dblclick keyup',function(e){
if(e.which == 13 || e.type == 'dblclick') {
var container = $(this).closest('.addremove-multiselect');
moveItems($(container).find('select.multiselect.available'), $(container).find('select.multiselect.selected'));
$('#hidden_ids_box').val($('#selected-select').val());
}
});
});
$.fn.sort_select_box = function(){
// Get options from select box
var selected_options = $(this).children('option');
// sort alphabetically
selected_options.sort(function(a,b) {
if (a.text > b.text) return 1;
else if (a.text < b.text) return -1;
else return 0
})
//replace with sorted my_options;
$(this).empty().append(selected_options);
var selected_in_box = $('#selected-select option').toArray().map(item => item.value).join();
$('#hidden_ids_box').empty().val(selected_in_box);
$('#count_selected_box').html($('#selected-select option').length);
$('#count_unselected_box').html($('#available-select option').length);
// clearing any selections
$("#"+this.attr('id')+" option").attr('selected', true);
}
+8
View File
@@ -384,6 +384,14 @@ return [
'bulk_edit_about_to' => 'You are about to edit the following: ',
'checked_out' => 'Checked Out',
'checked_out_to' => 'Checked out to',
'available_users' => 'Available Users to Add',
'add_users_to_group' => 'Add Users to Group',
'users_to_add_to_group' => 'Users to Add',
'add_all_users_to_group' => 'Add all users to group',
'add_selected_users_to_group' => 'Add selected users to group',
'remove_selected_users_from_group' => 'Remove selected users from group',
'remove_all_users_from_group' => 'Remove all users from group',
'add_users_to_group_help' => 'Use the arrows to add or remove users from this group. You can select multiple users by holding down the Ctrl (Windows) or Command (Mac) key while clicking.',
'fields' => 'Fields',
'last_checkout' => 'Last Checkout',
'due_to_checkin' => 'The following :count items are due to be checked in soon:',
+54 -28
View File
@@ -3,7 +3,6 @@
'updateText' => trans('admin/groups/titles.update'),
'item' => $group,
'formAction' => ($group !== null && $group->id !== null) ? route('groups.update', ['group' => $group->id]) : route('groups.store'),
'container_classes' => 'col-lg-6 col-lg-offset-3 col-md-10 col-md-offset-1 col-sm-12 col-sm-offset-0',
'topSubmit' => 'true',
])
@section('content')
@@ -41,40 +40,67 @@
</div>
<div class="form-group{{ $errors->has('associated_users') ? ' has-error' : '' }}">
<fieldset>
<x-form-legend help_text="{{ trans('general.add_users_to_group_help') }}">
{{ trans('general.add_users_to_group') }}
</x-form-legend>
<label for="associated_users[]" class="col-md-3 control-label">
{{ trans('general.users') }}
</label>
<!-- this is a temp fix for the select2 not working inside modals -->
<div class="col-md-7">
<select class="js-data-ajax"
data-endpoint="users"
data-placeholder="{{ trans('general.select_user') }}"
name="associated_users[]"
style="width: 100%"
id="associated_users[]"
aria-label="associated_users[]" multiple>
<option value="" role="option">{{ trans('general.select_user') }}</option>
@if(isset($associated_users))
@foreach($associated_users as $associated_user)
<option value="{{ $associated_user->id }}" selected="selected" role="option" aria-selected="true"
role="option">
{{ $associated_user->present()->fullName }} ({{ $associated_user->username }})
</option>
@endforeach
@endif
</select>
<div class="form-group">
<div class="col-md-12">
<!-- This hidden input will store the selected user IDs as a comma-separated string to avoid max_input_vars and max_multipart_body_parts php.ini issues -->
<input type="hidden" name="users_to_sync" id="hidden_ids_box" class="form-control" value="{{ ($associated_users && is_array($associated_users)) ? implode(",", $associated_users->pluck('id')->toArray()) : '' }}"/>
<div class="addremove-multiselect">
<div class="col-lg-5 col-sm-5 col-xs-12">
<h4>{{ trans('general.available_users') }}</h4>
<select id="available-select" class="multiselect available form-control" size="8" multiple="multiple">
@foreach($unselected_users as $unselected_user)
<option value="{{ $unselected_user->id }}" role="option">
{{ $unselected_user->present()->fullName }} ({{ $unselected_user->username }})
</option>
@endforeach
</select>
<p class="help-block text-right">
<x-icon type="users" />
<span id="count_unselected_box">{{ count($unselected_users) }}</span>
</p>
</div>
<div class="multiselect-controls col-lg-2 col-sm-2 col-xs-12">
<br><br>
<button type="button" id="rightall" class="rightall btn btn-sm btn-block btn-default" data-tooltip="true" title="{{ trans('general.add_all_users_to_group') }}"><i class="fa-solid fa-angles-right"></i></button>
<button type="button" id="right" class="right btn btn-sm btn-block btn-default" data-tooltip="true" title="{{ trans('general.add_selected_users_to_group') }}"><i class="fa-solid fa-angle-right"></i></button>
<button type="button" id="left" class="left btn btn-block btn-sm btn-default" data-tooltip="true" title="{{ trans('general.remove_selected_users_from_group') }}"><i class="fa-solid fa-angle-left"></i></button>
<button type="button" id="leftall" class="leftall btn btn-sm btn-block btn-default" data-tooltip="true" title="{{ trans('general.remove_all_users_from_group') }}"><i class="fa-solid fa-angles-left"></i></button>
</div>
<div class="col-lg-5 col-sm-5 col-xs-12">
<h4>{{ trans('general.users_to_add_to_group') }}</h4>
<select id="selected-select" class="multiselect selected form-control" size="8" multiple="multiple">
@foreach($associated_users as $associated_user)
<option value="{{ $associated_user->id }}" aria-selected="true" selected="selected" role="option">
{{ $associated_user->present()->fullName }} ({{ $associated_user->username }})
</option>
@endforeach
</select>
<p class="help-block text-right">
<x-icon type="users" />
<span id="count_selected_box"> {{ count($associated_users) }}</span>
</p>
</div>
</div>
</div>
{!! $errors->first('associated_users', '<div class="col-md-8 col-md-offset-3"><span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span></div>') !!}
</div>
</fieldset>
<div class="col-md-12">
@include ('partials.forms.edit.permissions-base', ['use_inherit' => false])
</div>
@stop
+3 -1
View File
@@ -42,7 +42,9 @@
<th data-switchable="true" data-sortable="true" data-field="notes" data-visible="true">{{ trans('general.notes') }}</th>
<th data-switchable="true" data-sortable="true" data-field="created_at" data-visible="true" data-formatter="dateDisplayFormatter">{{ trans('general.created_at') }}</th>
<th data-switchable="false" data-searchable="false" data-sortable="true" data-field="created_by" data-formatter="usersLinkObjFormatter">{{ trans('general.created_by') }}</th>
<th data-switchable="false" data-searchable="false" data-sortable="false" data-field="actions" data-formatter="groupsActionsFormatter">{{ trans('table.actions') }}</th>
<th data-switchable="false" data-searchable="false" data-sortable="true" data-field="created_at" data-formatter="dateDisplayFormatter">{{ trans('general.created_at') }}</th>
<th data-switchable="false" data-searchable="false" data-sortable="true" data-field="updated_at" data-formatter="dateDisplayFormatter">{{ trans('general.updated_at') }}</th>
<th data-switchable="false" data-searchable="false" data-sortable="false" data-field="actions" data-formatter="groupsActionsFormatter">{{ trans('table.actions') }}</th>
</tr>
</thead>