mirror of
https://github.com/getnamingo/registry.git
synced 2025-05-29 17:00:06 +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,
|
'password' => $password_hashed,
|
||||||
'username' => $username,
|
'username' => $username,
|
||||||
'verified' => $verified,
|
'verified' => $verified,
|
||||||
'roles_mask' => 6,
|
'roles_mask' => 4,
|
||||||
'status' => $status,
|
'status' => $status,
|
||||||
'registered' => \time()
|
'registered' => \time()
|
||||||
]
|
]
|
||||||
|
@ -205,12 +205,21 @@ class UsersController extends Controller
|
||||||
|
|
||||||
$_SESSION['user_to_update'] = [$args];
|
$_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', [
|
return view($response,'admin/users/updateUser.twig', [
|
||||||
'user' => $user,
|
'user' => $user,
|
||||||
'currentUri' => $uri,
|
'currentUri' => $uri,
|
||||||
'registrars' => $registrars,
|
'registrars' => $registrars,
|
||||||
'user_asso' => $user_asso,
|
'user_asso' => $user_asso,
|
||||||
'registrar_name' => $registrar_name
|
'registrar_name' => $registrar_name,
|
||||||
|
'roles_new' => $roles_new
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
// User does not exist, redirect to the users view
|
// User does not exist, redirect to the users view
|
||||||
|
@ -240,6 +249,10 @@ class UsersController extends Controller
|
||||||
$password_confirmation = $data['password_confirmation'] ?? null;
|
$password_confirmation = $data['password_confirmation'] ?? null;
|
||||||
$status = $data['status'] ?? null;
|
$status = $data['status'] ?? null;
|
||||||
$verified = $data['verified'] ?? 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
|
// Define validation rules
|
||||||
$validators = [
|
$validators = [
|
||||||
|
@ -249,6 +262,14 @@ class UsersController extends Controller
|
||||||
'verified' => v::in(['0', '1'])->setName('Verified'), // Ensure verified is checked as 0 or 1
|
'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
|
// Add password validation only if provided
|
||||||
if (!empty($password)) {
|
if (!empty($password)) {
|
||||||
$validators['password'] = v::stringType()->notEmpty()->length(6, 255)->setName('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);
|
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();
|
$db->beginTransaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -301,6 +327,7 @@ class UsersController extends Controller
|
||||||
'username' => $username,
|
'username' => $username,
|
||||||
'verified' => $verified,
|
'verified' => $verified,
|
||||||
'status' => $status,
|
'status' => $status,
|
||||||
|
'roles_mask' => $roles_mask,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!empty($password)) {
|
if (!empty($password)) {
|
||||||
|
|
|
@ -93,7 +93,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Role</label>
|
<label class="form-label">Type</label>
|
||||||
<div class="form-check form-switch">
|
<div class="form-check form-switch">
|
||||||
<input class="form-check-input" type="checkbox" id="role-toggle" name="role" value="admin">
|
<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>
|
<label class="form-check-label" for="role-toggle" id="role-label">Registrar</label>
|
||||||
|
|
|
@ -94,24 +94,66 @@
|
||||||
|
|
||||||
<!-- Role -->
|
<!-- Role -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-6">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">{{ __('Role') }}</label>
|
<label class="form-label">{{ __('Type') }}</label>
|
||||||
<div class="form-control-plaintext">
|
<div class="form-control-plaintext">
|
||||||
{% if user.roles_mask == 0 %}
|
{% if user.roles_mask == 0 %}
|
||||||
{{ __('Admin') }}
|
{{ __('Admin') }}
|
||||||
{% elseif user.roles_mask == 4 %}
|
|
||||||
{{ __('Registrar') }}
|
|
||||||
{% if user_asso is defined and user_asso is not null %}
|
|
||||||
- {{ __('Associated with Registrar') }} {{ registrar_name }}
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
{% 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 %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
<div class="card-footer text-end">
|
<div class="card-footer text-end">
|
||||||
|
@ -126,4 +168,51 @@
|
||||||
</div>
|
</div>
|
||||||
{% include 'partials/footer.twig' %}
|
{% include 'partials/footer.twig' %}
|
||||||
</div>
|
</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 %}
|
{% endblock %}
|
|
@ -7,7 +7,7 @@
|
||||||
var table;
|
var table;
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function(){
|
document.addEventListener("DOMContentLoaded", function(){
|
||||||
function userLinkFormatter(cell){
|
function emailLinkFormatter(cell){
|
||||||
var value = cell.getValue();
|
var value = cell.getValue();
|
||||||
return `<span style="font-weight:bold;">${value}</a>`;
|
return `<span style="font-weight:bold;">${value}</a>`;
|
||||||
}
|
}
|
||||||
|
@ -28,14 +28,33 @@
|
||||||
|
|
||||||
function roleLabelFormatter(cell) {
|
function roleLabelFormatter(cell) {
|
||||||
var value = cell.getValue();
|
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) {
|
if (value === 0) {
|
||||||
return '<span class="status status-purple">Administrator</span>';
|
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) {
|
function verifiedFormatter(cell) {
|
||||||
|
@ -144,10 +163,10 @@
|
||||||
placeholder: "{{ __('No Data') }}",
|
placeholder: "{{ __('No Data') }}",
|
||||||
columns:[
|
columns:[
|
||||||
{formatter:"responsiveCollapse", width:30, minWidth:30, hozAlign:"center", resizable:false, headerSort:false, responsive:0},
|
{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, formatter: emailLinkFormatter, responsive:0},
|
||||||
{title:"{{ __('Email') }}", field:"email", width:300, resizable:false, headerSort:true, responsive:2},
|
{title:"{{ __('User Name') }}", field:"username", width:200, resizable:false, headerSort:true, responsive:2},
|
||||||
{title:"{{ __('Roles') }}", field:"roles_mask", width:200, resizable:false, headerSort:true, formatter: roleLabelFormatter, responsive:2},
|
{title:"{{ __('Roles') }}", field:"roles_mask", width:300, resizable:false, headerSort:true, formatter: roleLabelFormatter, responsive:2},
|
||||||
{title:"{{ __('Verified') }}", field:"verified", width:200, resizable:false, headerSort:true, formatter: verifiedFormatter, 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:"{{ __('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(); }},
|
{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