diff --git a/cp/app/Controllers/UsersController.php b/cp/app/Controllers/UsersController.php index 4046c49..f54da7a 100644 --- a/cp/app/Controllers/UsersController.php +++ b/cp/app/Controllers/UsersController.php @@ -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)) { diff --git a/cp/resources/views/admin/users/createUser.twig b/cp/resources/views/admin/users/createUser.twig index 339913a..530519a 100644 --- a/cp/resources/views/admin/users/createUser.twig +++ b/cp/resources/views/admin/users/createUser.twig @@ -93,7 +93,7 @@
- +
diff --git a/cp/resources/views/admin/users/updateUser.twig b/cp/resources/views/admin/users/updateUser.twig index 59354de..a44daea 100644 --- a/cp/resources/views/admin/users/updateUser.twig +++ b/cp/resources/views/admin/users/updateUser.twig @@ -94,24 +94,66 @@
-
+
- +
{% 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 %}
+ + {% if user.roles_mask != 0 %} + {% if user.roles_mask == 4 %} +
+
+ + {{ __('Registrar Administrator') }} +
+
+ {% else %} +
+
+ + +
+
+ {% endif %} + {% endif %}
+
{% include 'partials/footer.twig' %}
+ + {% endblock %} \ No newline at end of file diff --git a/cp/resources/views/partials/js-users.twig b/cp/resources/views/partials/js-users.twig index ca5b236..d1413d1 100644 --- a/cp/resources/views/partials/js-users.twig +++ b/cp/resources/views/partials/js-users.twig @@ -7,7 +7,7 @@ var table; document.addEventListener("DOMContentLoaded", function(){ - function userLinkFormatter(cell){ + function emailLinkFormatter(cell){ var value = cell.getValue(); return `${value}`; } @@ -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 'Administrator'; - } else if (value === 4) { - return 'Registrar'; - } else if (value === 6) { - return 'Registrar Assistant'; } - 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 '' + role.label + ''; + }); + + // Join assigned roles with separators + return assignedRoles.length > 0 ? assignedRoles.join(', ') : 'Unknown Role'; } 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(); }}, ]