mirror of
https://github.com/getnamingo/registry.git
synced 2025-05-28 13:50:27 +02:00
Added ability to have different user roles
This commit is contained in:
parent
9504733dbf
commit
d07fa5481c
4 changed files with 157 additions and 22 deletions
|
@ -104,7 +104,7 @@ class UsersController extends Controller
|
|||
'password' => $password_hashed,
|
||||
'username' => $username,
|
||||
'verified' => $verified,
|
||||
'roles_mask' => 6,
|
||||
'roles_mask' => 4,
|
||||
'status' => $status,
|
||||
'registered' => \time()
|
||||
]
|
||||
|
@ -205,12 +205,21 @@ class UsersController extends Controller
|
|||
|
||||
$_SESSION['user_to_update'] = [$args];
|
||||
|
||||
$roles_new = [
|
||||
'4' => ($user['roles_mask'] & 4) ? true : false, // Registrar
|
||||
'8' => ($user['roles_mask'] & 8) ? true : false, // Accountant
|
||||
'16' => ($user['roles_mask'] & 16) ? true : false, // Support
|
||||
'32' => ($user['roles_mask'] & 32) ? true : false, // Auditor
|
||||
'64' => ($user['roles_mask'] & 64) ? true : false, // Sales
|
||||
];
|
||||
|
||||
return view($response,'admin/users/updateUser.twig', [
|
||||
'user' => $user,
|
||||
'currentUri' => $uri,
|
||||
'registrars' => $registrars,
|
||||
'user_asso' => $user_asso,
|
||||
'registrar_name' => $registrar_name
|
||||
'registrar_name' => $registrar_name,
|
||||
'roles_new' => $roles_new
|
||||
]);
|
||||
} else {
|
||||
// User does not exist, redirect to the users view
|
||||
|
@ -240,6 +249,10 @@ class UsersController extends Controller
|
|||
$password_confirmation = $data['password_confirmation'] ?? null;
|
||||
$status = $data['status'] ?? null;
|
||||
$verified = $data['verified'] ?? null;
|
||||
$roles_mask = $data['roles_mask'] ?? 0;
|
||||
|
||||
$allowedRoles = [0, 2, 4, 8, 16, 32, 64];
|
||||
$allowedRolesMask = array_sum($allowedRoles); // 124 (sum of allowed roles)
|
||||
|
||||
// Define validation rules
|
||||
$validators = [
|
||||
|
@ -249,6 +262,14 @@ class UsersController extends Controller
|
|||
'verified' => v::in(['0', '1'])->setName('Verified'), // Ensure verified is checked as 0 or 1
|
||||
];
|
||||
|
||||
// Add custom validation for roles_mask
|
||||
$validators['roles_mask'] = v::oneOf(
|
||||
v::intVal()->callback(function ($value) use ($allowedRolesMask) {
|
||||
return ($value & ~$allowedRolesMask) === 0; // Ensure only allowed roles are included
|
||||
}),
|
||||
v::nullType() // Allow null as a valid value
|
||||
)->setName('Roles Mask');
|
||||
|
||||
// Add password validation only if provided
|
||||
if (!empty($password)) {
|
||||
$validators['password'] = v::stringType()->notEmpty()->length(6, 255)->setName('Password');
|
||||
|
@ -289,6 +310,11 @@ class UsersController extends Controller
|
|||
return $response->withHeader('Location', '/user/update/'.$old_username)->withStatus(302);
|
||||
}
|
||||
|
||||
if (in_array($roles_mask, [0, '0'], true)) {
|
||||
$this->container->get('flash')->addMessage('error', 'No roles assigned. Please assign at least one role');
|
||||
return $response->withHeader('Location', '/user/update/' . $old_username)->withStatus(302);
|
||||
}
|
||||
|
||||
$db->beginTransaction();
|
||||
|
||||
try {
|
||||
|
@ -301,6 +327,7 @@ class UsersController extends Controller
|
|||
'username' => $username,
|
||||
'verified' => $verified,
|
||||
'status' => $status,
|
||||
'roles_mask' => $roles_mask,
|
||||
];
|
||||
|
||||
if (!empty($password)) {
|
||||
|
|
|
@ -93,7 +93,7 @@
|
|||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Role</label>
|
||||
<label class="form-label">Type</label>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="role-toggle" name="role" value="admin">
|
||||
<label class="form-check-label" for="role-toggle" id="role-label">Registrar</label>
|
||||
|
|
|
@ -94,24 +94,66 @@
|
|||
|
||||
<!-- Role -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ __('Role') }}</label>
|
||||
<label class="form-label">{{ __('Type') }}</label>
|
||||
<div class="form-control-plaintext">
|
||||
{% if user.roles_mask == 0 %}
|
||||
{{ __('Admin') }}
|
||||
{% elseif user.roles_mask == 4 %}
|
||||
{{ __('Registrar') }}
|
||||
{% if user_asso is defined and user_asso is not null %}
|
||||
- {{ __('Associated with Registrar') }} {{ registrar_name }}
|
||||
{% endif %}
|
||||
{{ __('Admin') }}
|
||||
{% else %}
|
||||
{{ __('Unknown Role') }}
|
||||
{# Display Assigned Roles #}
|
||||
{% set assigned_roles = [] %}
|
||||
{% if roles_new['4'] %}
|
||||
{% set assigned_roles = assigned_roles | merge([__('Registrar')]) %}
|
||||
{% endif %}
|
||||
{% if roles_new['8'] %}
|
||||
{% set assigned_roles = assigned_roles | merge([__('Accountant')]) %}
|
||||
{% endif %}
|
||||
{% if roles_new['16'] %}
|
||||
{% set assigned_roles = assigned_roles | merge([__('Support')]) %}
|
||||
{% endif %}
|
||||
{% if roles_new['32'] %}
|
||||
{% set assigned_roles = assigned_roles | merge([__('Auditor')]) %}
|
||||
{% endif %}
|
||||
{% if roles_new['64'] %}
|
||||
{% set assigned_roles = assigned_roles | merge([__('Sales')]) %}
|
||||
{% endif %}
|
||||
|
||||
{{ assigned_roles | join(', ') }}
|
||||
|
||||
{# Show association if the user is a Registrar and associated with another registrar #}
|
||||
{% if roles_new['4'] and user_asso is defined and user_asso is not null %}
|
||||
- {{ __('Associated with Registrar') }} {{ registrar_name }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if user.roles_mask != 0 %}
|
||||
{% if user.roles_mask == 4 %}
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ __('Roles') }}</label>
|
||||
<span class="badge bg-blue-lt">{{ __('Registrar Administrator') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ __('Manage Roles') }}</label>
|
||||
<select class="form-select" id="select-roles" name="roles_mask[]" multiple>
|
||||
<option value="8" {{ roles_new['8'] ? 'selected' : '' }}>{{ __('Accountant') }}</option>
|
||||
<option value="16" {{ roles_new['16'] ? 'selected' : '' }}>{{ __('Support') }}</option>
|
||||
<option value="32" {{ roles_new['32'] ? 'selected' : '' }}>{{ __('Auditor') }}</option>
|
||||
<option value="64" {{ roles_new['64'] ? 'selected' : '' }}>{{ __('Sales') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-footer text-end">
|
||||
|
@ -126,4 +168,51 @@
|
|||
</div>
|
||||
{% include 'partials/footer.twig' %}
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
var el;
|
||||
window.TomSelect && (new TomSelect(el = document.getElementById('select-roles'), {
|
||||
copyClassesToDropdown: false,
|
||||
dropdownParent: 'body',
|
||||
controlInput: '<input>',
|
||||
render:{
|
||||
item: function(data,escape) {
|
||||
if( data.customProperties ){
|
||||
return '<div><span class="dropdown-item-indicator">' + data.customProperties + '</span>' + escape(data.text) + '</div>';
|
||||
}
|
||||
return '<div>' + escape(data.text) + '</div>';
|
||||
},
|
||||
option: function(data,escape){
|
||||
if( data.customProperties ){
|
||||
return '<div><span class="dropdown-item-indicator">' + data.customProperties + '</span>' + escape(data.text) + '</div>';
|
||||
}
|
||||
return '<div>' + escape(data.text) + '</div>';
|
||||
},
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
document.querySelector('form').addEventListener('submit', function (e) {
|
||||
const select = document.getElementById('select-roles');
|
||||
let rolesMask = 0;
|
||||
|
||||
// Loop through selected options and combine their values using bitwise OR
|
||||
for (const option of select.selectedOptions) {
|
||||
rolesMask |= parseInt(option.value);
|
||||
}
|
||||
|
||||
// Create a hidden input to send the combined roles_mask to the server
|
||||
const hiddenInput = document.createElement('input');
|
||||
hiddenInput.type = 'hidden';
|
||||
hiddenInput.name = 'roles_mask';
|
||||
hiddenInput.value = rolesMask;
|
||||
|
||||
// Append the hidden input to the form
|
||||
this.appendChild(hiddenInput);
|
||||
|
||||
// Optional: Disable the select to avoid sending duplicate data
|
||||
select.disabled = true;
|
||||
});
|
||||
</script>
|
||||
<script src="/assets/libs/tom-select/dist/js/tom-select.base.min.js" defer></script>
|
||||
{% endblock %}
|
|
@ -7,7 +7,7 @@
|
|||
var table;
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function(){
|
||||
function userLinkFormatter(cell){
|
||||
function emailLinkFormatter(cell){
|
||||
var value = cell.getValue();
|
||||
return `<span style="font-weight:bold;">${value}</a>`;
|
||||
}
|
||||
|
@ -28,14 +28,33 @@
|
|||
|
||||
function roleLabelFormatter(cell) {
|
||||
var value = cell.getValue();
|
||||
|
||||
// Define role mappings with labels and styles
|
||||
var roles = [
|
||||
{ bit: 0, label: 'Administrator', class: 'status status-purple' },
|
||||
{ bit: 4, label: 'Registrar', class: 'status status-indigo' },
|
||||
{ bit: 8, label: 'Accountant', class: 'status status-green' },
|
||||
{ bit: 16, label: 'Support', class: 'status status-azure' },
|
||||
{ bit: 32, label: 'Auditor', class: 'status status-orange' },
|
||||
{ bit: 64, label: 'Sales', class: 'status status-teal' }
|
||||
];
|
||||
|
||||
// Special case for Administrator (no roles assigned)
|
||||
if (value === 0) {
|
||||
return '<span class="status status-purple">Administrator</span>';
|
||||
} else if (value === 4) {
|
||||
return '<span class="status status-indigo">Registrar</span>';
|
||||
} else if (value === 6) {
|
||||
return '<span class="status status-azure">Registrar Assistant</span>';
|
||||
}
|
||||
return value; // If the value is neither 0 nor 4, return it as is
|
||||
|
||||
// Check assigned roles using bitmask
|
||||
var assignedRoles = roles
|
||||
.filter(function (role) {
|
||||
return role.bit !== 0 && (value & role.bit);
|
||||
})
|
||||
.map(function (role) {
|
||||
return '<span class="' + role.class + '">' + role.label + '</span>';
|
||||
});
|
||||
|
||||
// Join assigned roles with separators
|
||||
return assignedRoles.length > 0 ? assignedRoles.join(', ') : '<span class="status status-secondary">Unknown Role</span>';
|
||||
}
|
||||
|
||||
function verifiedFormatter(cell) {
|
||||
|
@ -144,10 +163,10 @@
|
|||
placeholder: "{{ __('No Data') }}",
|
||||
columns:[
|
||||
{formatter:"responsiveCollapse", width:30, minWidth:30, hozAlign:"center", resizable:false, headerSort:false, responsive:0},
|
||||
{title:"{{ __('Name') }}", field:"username", width:200, resizable:false, headerSort:true, formatter: userLinkFormatter, responsive:0},
|
||||
{title:"{{ __('Email') }}", field:"email", width:300, resizable:false, headerSort:true, responsive:2},
|
||||
{title:"{{ __('Roles') }}", field:"roles_mask", width:200, resizable:false, headerSort:true, formatter: roleLabelFormatter, responsive:2},
|
||||
{title:"{{ __('Verified') }}", field:"verified", width:200, resizable:false, headerSort:true, formatter: verifiedFormatter, responsive:2},
|
||||
{title:"{{ __('Email') }}", field:"email", width:300, resizable:false, headerSort:true, formatter: emailLinkFormatter, responsive:0},
|
||||
{title:"{{ __('User Name') }}", field:"username", width:200, resizable:false, headerSort:true, responsive:2},
|
||||
{title:"{{ __('Roles') }}", field:"roles_mask", width:300, resizable:false, headerSort:true, formatter: roleLabelFormatter, responsive:2},
|
||||
{title:"{{ __('Verified') }}", field:"verified", width:150, resizable:false, headerSort:true, formatter: verifiedFormatter, responsive:2},
|
||||
{title:"{{ __('Status') }}", field:"status", width:200, resizable:false, headerSort:true, formatter: statusBadgeFormatter, responsive:2},
|
||||
{title: "{{ __('Actions') }}", formatter: actionsFormatter, responsive:0, headerSort: false, download:false, hozAlign: "center", cellClick:function(e, cell){ e.stopPropagation(); }},
|
||||
]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue