Managing user roles and permissions is a core part of any HR management system. Whether it’s for administrators, support staff, or other user roles, defining who has access to what functionalities is crucial for security and organisation. In this blog post, we’ll explore how to create and manage roles with specific privileges for users, update roles, display them in an organised table, and protect pages based on user permissions.
1. Creating and Inserting a New Role
The first step is to allow administrators to create new roles for the HR system. When a new role is created, specific privileges (access to different pages) are assigned, and the role details are stored in the database.
Role Creation Form:
We start by creating a form where the administrator can specify the role, the salary associated with that role, and the specific URLs the role has access to. Each URL will have a checkbox indicating whether the role should have access to that page, along with a visibility checkbox for whether the page should appear in the sidebar.
Form Example:
<form id="roleForm">
<!-- Form fields to add role name, salary, and access URLs -->
<div class="form-floating mb-3">
<input type="text" id="role" name="role" class="form-control" required>
<label for="role">Role:</label>
</div>
<div class="form-floating mb-3">
<input type="number" id="salary" name="salary" class="form-control" required>
<label for="salary">Salary:</label>
</div>
<p>URL Accessibility (Select the URLs the role can access):</p>
<!-- URL checkboxes with Show in Sidebar option -->
<div>
<input type="checkbox" id="home" name="urls[]" value="index.php" class="form-check-input">
<label for="home" class="form-check-label">Home</label>
<input type="checkbox" id="home_visibility" name="home_visibility" class="form-check-input">
<label for="home_visibility" class="form-check-label">Show in Sidebar</label>
</div>
<div>
<input type="checkbox" id="addadmin" name="urls[]" value="addadmin.php" class="form-check-input">
<label for="addadmin" class="form-check-label">Add Admin</label>
<input type="checkbox" id="addadmin_visibility" name="addadmin_visibility" class="form-check-input">
<label for="addadmin_visibility" class="form-check-label">Show in Sidebar</label>
</div>
<div>
<input type="checkbox" id="userslist" name="urls[]" value="users.php" class="form-check-input">
<label for="userslist" class="form-check-label">Users List</label>
<input type="checkbox" id="userslist_visibility" name="userslist_visibility" class="form-check-input">
<label for="userslist_visibility" class="form-check-label">Show in Sidebar</label>
</div>
<!-- Additional checkboxes for other pages like Password Management, Users List, etc. -->
<button type="submit" class="btn btn-lg btn-primary">Save Role</button>
</form>
<script>
document.getElementById("roleForm").addEventListener("submit", function(event) {
event.preventDefault();
const role = document.getElementById("role").value;
const salary = document.getElementById("salary").value;
// Mapping between URL values and checkbox IDs
const urlToIdMapping = {
"index.php": "home",
"passreset.php": "passreset",
"addadmin.php": "addadmin",
"users.php": "userslist",
"staff.php": "stafflist"
};
// Get selected URLs and visibility
const urls = Array.from(document.querySelectorAll('input[name="urls[]"]:checked')).map(input => input.value);
const visibility = {
"index.php": document.getElementById("home_visibility").checked,
"passreset.php": document.getElementById("passreset_visibility").checked,
"addadmin.php": document.getElementById("addadmin_visibility").checked,
"users.php": document.getElementById("userslist_visibility").checked,
"staff.php": document.getElementById("stafflist_visibility").checked
};
// Create privileges object
const privileges = {
urls: urls.map(url => {
const id = urlToIdMapping[url]; // Get the corresponding id from the mapping
const label = document.querySelector(`label[for=${id}]`); // Use the ID as the selector
return {
name: label ? label.textContent.trim() : url,
url: url,
visible: visibility[url] || false // If visibility is checked, set to true, otherwise false
};
})
};
// Log the collected data to the console
console.log("Role:", role);
console.log("Salary:", salary);
console.log("Privileges:", privileges);
const data = {
role: role,
salary: salary,
privileges: JSON.stringify(privileges)
};
fetch('save_role.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(result => {
console.log("Result from server:", result); // Log the server's response
if (result.success) {
alert('Role saved successfully!');
} else {
alert('Error saving role: ' + result.message);
}
})
.catch(error => {
console.error('Error:', error);
});
});
</script>
The form captures the role’s name, salary, and URLs (with visibility options). When the form is submitted, the data is sent via JavaScript to the server for processing.
2. Saving the Role to the Database
When the form is submitted, the data is captured and sent to a PHP script (save_role.php
) that will insert the new role into the database.
Save Role Script:
The save_role.php
script checks if the role already exists and if not, inserts the new role into the roles
table, along with its privileges (in JSON format).
$data = json_decode(file_get_contents('php://input'), true);
if (isset($data['role']) && isset($data['salary']) && isset($data['privileges'])) {
// Insert role into the database
$query = "INSERT INTO roles (role, salary, privileges) VALUES (?, ?, ?)";
if ($stmt = mysqli_prepare($link, $query)) {
mysqli_stmt_bind_param($stmt, "sss", $role, $salary, $privileges);
if (mysqli_stmt_execute($stmt)) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'message' => 'Database error']);
}
mysqli_stmt_close($stmt);
}
} else {
echo json_encode(['success' => false, 'message' => 'Invalid data']);
}
This PHP script saves the role, salary, and privileges as a JSON string in the database, which allows us to manage complex privileges for each role.
3. Displaying Roles in a Table
After the roles are created, it's important to display them in an easy-to-read table for administrators. This table shows each role’s details, including the associated privileges and the ability to update the role if needed.
Role List Table:
<div class="table-responsive">
<?php
$sql = "SELECT * FROM roles";
$result = mysqli_query($link, $sql);
if ($result->num_rows > 0) {
echo '<table id="datatable-basic" class="table table-bordered text-nowrap w-100">
<thead>
<tr>
<th>ID</th>
<th>Role</th>
<th>Privileges</th>
<th>Action</th>
</tr>
</thead>
<tbody>';
while ($row = $result->fetch_assoc()) {
$id = $row["id"];
$role = $row["role"];
$privileges = json_decode($row["privileges"], true);
$privileges_list = array_map(function($privilege) {
return $privilege['name'];
}, $privileges['urls']);
$privileges_str = implode(', ', $privileges_list);
echo "<tr>
<td>{$id}</td>
<td>{$role}</td>
<td>{$privileges_str}</td>
<td><a href='./roleupdate.php?ref={$id}' class='btn btn-primary'>Update Role</a></td>
</tr>";
}
echo '</tbody></table>';
}
?>
</div>
The table displays the roles and their associated privileges. Clicking the "Update Role" button leads to the role update page where you can modify the role's details and privileges.
4. Updating the Role
To update a role, administrators can modify the role name, salary, and the URLs the role has access to. The form used for role creation is reused here, with pre-filled values for the selected role.
Update Role Form:
<?php
$id = $_GET['ref']; // Get the role ID from the URL
$sql = "SELECT * FROM roles WHERE id = ?";
$stmt = mysqli_prepare($link, $sql);
mysqli_stmt_bind_param($stmt, "i", $id);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$roleData = mysqli_fetch_assoc($result);
mysqli_stmt_close($stmt);
if (!$roleData) {
echo "Role not found.";
exit;
}
?>
<form id="roleForm">
<div class="form-floating mb-3">
<input type="text" id="role" name="role" class="form-control" value="<?= htmlspecialchars($roleData['role']) ?>" readonly>
<label for="role">Role:</label>
</div>
<div class="form-floating mb-3">
<input type="number" id="salary" name="salary" class="form-control" value="<?= htmlspecialchars($roleData['salary']) ?>" required>
<label for="salary">Salary:</label>
</div>
<!-- Checkbox for URLs and Visibility -->
<?php
$privileges = json_decode($roleData['privileges'], true);
$urls = $privileges['urls'] ?? [];
foreach ($urls as $urlData) {
$isChecked = in_array($urlData['url'], array_column($urls, 'url'));
echo "
<div>
<input type='checkbox' id='{$urlData['url']}' name='urls[]' value='{$urlData['url']}' class='form-check-input' " . ($isChecked ? 'checked' : '') . ">
<label for='{$urlData['url']}' class='form-check-label'>{$urlData['name']}</label>
<input type='checkbox' id='{$urlData['url']}_visibility' name='{$urlData['url']}_visibility' class='form-check-input' " . ($urlData['visible'] ? 'checked' : '') . ">
<label for='{$urlData['url']}_visibility' class='form-check-label'>Show in Sidebar</label>
</div>";
}
?>
<button type="submit" class="btn btn-lg btn-primary">Update Role</button>
</form>
<script>
document.getElementById("roleForm").addEventListener("submit", function(event) {
event.preventDefault();
const role = document.getElementById("role").value;
const salary = document.getElementById("salary").value;
// Mapping between URL values and checkbox IDs
const urlToIdMapping = {
"index.php": "home",
"passreset.php": "passreset",
"addadmin.php": "addadmin",
"users.php": "userslist",
"staff.php": "stafflist"
};
// Available URLs with human-readable names
const availableUrls = {
"index.php": "Home",
"passreset.php": "Password Management",
"addadmin.php": "Add Admin",
"users.php": "Users List",
"staff.php": "Staff List"
};
// Get selected URLs and visibility
const urls = Array.from(document.querySelectorAll('input[name="urls[]"]:checked')).map(input => input.value);
const visibility = {};
document.querySelectorAll('input[name$="_visibility"]').forEach(input => {
const url = input.id.replace('_visibility', '');
visibility[url] = input.checked;
});
// Create privileges object
const privileges = {
urls: urls.map(url => {
const label = availableUrls[url] || url; // Get the human-readable name from availableUrls
return {
name: label, // Use the label name (e.g., 'Home' instead of 'index.php')
url: url,
visible: visibility[url] || false
};
})
};
// Log the collected data to the console
// Get the 'ref' parameter from the URL
const urlParams = new URLSearchParams(window.location.search);
const ref = urlParams.get('ref'); // This will extract the 'ref' value
console.log("Role:", role);
console.log("Salary:", salary);
console.log("Privileges:", privileges);
console.log("Role ID (ref):", ref); // Log the ref value
const data = {
role: role,
salary: salary,
privileges: JSON.stringify(privileges),
id: ref
};
fetch('update_role.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(result => {
console.log("Result from server:", result); // Log the server's response
if (result.success) {
alert('Role updated successfully!');
} else {
alert('Error updating role: ' + result.message);
}
})
.catch(error => {
console.error('Error:', error);
});
});
</script>
The script pre-populates the form fields with the current role's data and allows administrators to update the role’s details. After the form is submitted, the updated data is sent to the server.
5. Protecting Pages Based on Role
To ensure that users can only access pages they have permissions for, we need to check the user's role and privileges on every page. We do this by fetching the role's privileges and comparing them with the current page's URL.
Protecting Pages Example:
// Fetch the role's privileges from the database
$rolePagesQuery = "SELECT privileges FROM roles WHERE role = ?";
$stmt = mysqli_prepare($link, $rolePagesQuery);
mysqli_stmt_bind_param($stmt, "s", $role);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$roleData = mysqli_fetch_assoc($result);
mysqli_stmt_close($stmt);
// Ensure role data is found
if (!$roleData) {
echo "Role not found.";
exit;
}
// Decode the privileges JSON data
$privileges = json_decode($roleData['privileges'], true);
$allowedPages = $privileges['urls'] ?? []; // This will be the array of URLs the user has access to
// Get the current page name (for example, from the URL)
$currentPage = basename($_SERVER['PHP_SELF']); // This will give you the current page name with extension (e.g., "addadmin.php")
// Check if the current page URL is allowed for the user's role
$pageAccessGranted = false;
foreach ($allowedPages as $page) {
if ($page['url'] === $currentPage && $page['visible'] === true) {
$pageAccessGranted = true;
break;
}
}
if (!$pageAccessGranted) {
// If the current page is not in the allowed list for the user's role, redirect them to the home page
header("location: index.php");
exit;
}
This code checks if the current user has the necessary permissions to view the page and redirects them if they do not.
4. Understanding the Sidebar Structure
Once the sidebar is rendered based on privileges, it's important to protect the pages. We need to ensure that users can only access pages they have privileges for.
<?php
$userRole = $role; // Assuming the role is saved in session or comes from a database
// Fetch the privileges based on the role
$query = "SELECT privileges FROM roles WHERE role = ?";
$stmt = mysqli_prepare($link, $query);
mysqli_stmt_bind_param($stmt, "s", $userRole);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$roleData = mysqli_fetch_assoc($result);
mysqli_stmt_close($stmt);
// Ensure role data is found
if (!$roleData) {
echo "Role not found.";
exit;
}
// Decode the privileges JSON data
$privileges = json_decode($roleData['privileges'], true);
$urls = $privileges['urls'] ?? [];
?>
<aside>
<div class="main-sidebar" id="sidebar-scroll">
<nav class="main-menu-container nav nav-pills flex-column sub-open">
<div class="slide-left" id="slide-left">
<svg xmlns="http://www.w3.org/2000/svg" fill="#7b8191" width="24" height="24" viewBox="0 0 24 24">
<path d="M13.293 6.293 7.586 12l5.707 5.707 1.414-1.414L10.414 12l4.293-4.293z"></path>
</svg>
</div>
<ul class="main-menu">
<!-- Render Menu Based on Role -->
<?php
// Icon mapping for menu items
$iconMapping = [
'Home' => 'fe-home',
'Password Management' => 'fe-file-text',
'Add Admin' => 'fe-package',
'Users List' => 'fe-zap',
'Roles' => 'fe-bar-chart-2',
'Roles List' => 'fe-bar-chart-2',
'Staff List' => 'fe-slack',
'Payment List' => 'fe-bar-chart-2',
'Profit List' => 'fe-slack',
'Add Wallet' => 'fe-slack',
'Admin Wallet List' => 'fe-file-text',
'Set Fees' => 'fe-cpu',
'Transaction' => 'fe-file-text',
'Expendition' => 'fe-file-text',
'Expendition List' => 'fe-bar-chart-2',
'Revenue' => 'fe-cpu',
'Revenue List' => 'fe-map-pin',
'Notification' => 'fe-grid',
'Notification List' => 'fe-file-text',
'Log out' => 'fe-power',
];
// Loop through the URL data and render the sidebar items
foreach ($urls as $urlData) :
if ($urlData['visible']) :
$iconClass = $iconMapping[$urlData['name']] ?? 'fe-file'; // Default to 'fe-file' if no specific icon exists
?>
<li class="slide">
<a href="<?php echo htmlspecialchars($urlData['url']); ?>" class="side-menu__item">
<i class="fe <?php echo $iconClass; ?> side-menu__icon"></i>
<span class="side-menu__label"><?php echo htmlspecialchars($urlData['name']); ?></span>
</a>
</li>
<?php
endif;
endforeach;
?>
</ul>
<div class="slide-right" id="slide-right">
<svg xmlns="http://www.w3.org/2000/svg" fill="#7b8191" width="24" height="24" viewBox="0 0 24 24">
<path d="M10.707 17.707 16.414 12l-5.707-5.707-1.414 1.414L13.586 12l-4.293 4.293z"></path>
</svg>
</div>
</nav>
</div>
<!-- End::main-sidebar -->
</aside>
Conclusion
Managing roles and privileges is a critical aspect of any HR management system. By defining roles, assigning appropriate privileges, and dynamically managing access to different pages, you can ensure that your system is both secure and user-friendly. This system allows administrators to create, update, and display roles, while protecting pages based on user access rights, ensuring a seamless HR management experience.