gestione ruoli e permessi
This commit is contained in:
parent
7c2c9d57f8
commit
eca44a4bc7
@ -4,16 +4,35 @@ require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
if (!can($_SESSION['user_role'], 'asset', 'create')) {
|
||||
if (!can($_SESSION['user_role_id'], 'asset', 'create')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
|
||||
$allowed_fields_str = can($_SESSION['user_role'], 'asset', 'create');
|
||||
$allowed_fields = ($allowed_fields_str === '*') ? ['name', 'status', 'location', 'manufacturer', 'model', 'purchase_date'] : explode(',', $allowed_fields_str);
|
||||
$allowed_fields_str = can($_SESSION['user_role_id'], 'asset', 'create');
|
||||
$allowed_fields = ($allowed_fields_str === '*') ? ['name', 'status', 'location_id', 'manufacturer', 'model', 'purchase_date', 'category_id', 'assigned_to'] : explode(',', $allowed_fields_str);
|
||||
|
||||
$success_message = '';
|
||||
$error_message = '';
|
||||
$categories = [];
|
||||
$locations = [];
|
||||
$users = [];
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
// Fetch categories for dropdown
|
||||
$stmt = $pdo->query("SELECT id, name FROM categories ORDER BY name");
|
||||
$categories = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
// Fetch locations for dropdown
|
||||
$stmt = $pdo->query("SELECT id, name FROM locations ORDER BY name");
|
||||
$locations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
// Fetch users for dropdown
|
||||
$stmt = $pdo->query("SELECT id, name FROM users ORDER BY name");
|
||||
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
$error_message = 'Database error: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$data = [];
|
||||
@ -46,9 +65,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (empty($error_message)) {
|
||||
foreach ($allowed_fields as $field) {
|
||||
if (isset($_POST[$field])) {
|
||||
$data[] = $_POST[$field];
|
||||
$value = $_POST[$field];
|
||||
if (($field === 'category_id' || $field === 'location_id' || $field === 'assigned_to') && $value === '') {
|
||||
$value = null;
|
||||
}
|
||||
$data[] = $value;
|
||||
$columns[] = $field;
|
||||
$placeholders[] = '?';
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,6 +101,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<meta name="description" content="Add a new asset to the inventory.">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css"/>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
@ -122,10 +145,43 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
</select>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if (in_array('location', $allowed_fields)): ?>
|
||||
<?php if (in_array('category_id', $allowed_fields)): ?>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="location" class="form-label">Location</label>
|
||||
<input type="text" class="form-control" id="location" name="location">
|
||||
<label for="category_id" class="form-label">Category</label>
|
||||
<select class="form-select" id="category_id" name="category_id">
|
||||
<option value="">No Category</option>
|
||||
<?php foreach ($categories as $category): ?>
|
||||
<option value="<?php echo $category['id']; ?>">
|
||||
<?php echo htmlspecialchars($category['name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if (in_array('location_id', $allowed_fields)): ?>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="location_id" class="form-label">Location</label>
|
||||
<select class="form-select" id="location_id" name="location_id">
|
||||
<option value="">No Location</option>
|
||||
<?php foreach ($locations as $location): ?>
|
||||
<option value="<?php echo $location['id']; ?>">
|
||||
<?php echo htmlspecialchars($location['name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if (in_array('assigned_to', $allowed_fields)): ?>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="assigned_to" class="form-label">Assigned To</label>
|
||||
<select class="form-select" id="assigned_to" name="assigned_to">
|
||||
<option value="">Unassigned</option>
|
||||
<?php foreach ($users as $user): ?>
|
||||
<option value="<?php echo $user['id']; ?>">
|
||||
<?php echo htmlspecialchars($user['name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
@ -159,6 +215,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js"></script>
|
||||
<script src="assets/js/choices.js?v=<?php echo time(); ?>"></script>
|
||||
<script>
|
||||
feather.replace();
|
||||
</script>
|
||||
|
||||
94
add-category.php
Normal file
94
add-category.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
if (!can($_SESSION['user_role_id'], 'category', 'create')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
|
||||
$error_message = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$name = $_POST['name'] ?? '';
|
||||
|
||||
if (empty($name)) {
|
||||
$error_message = 'Please fill in the category name.';
|
||||
} else {
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
$stmt = $pdo->prepare('SELECT id FROM categories WHERE name = ?');
|
||||
$stmt->execute([$name]);
|
||||
if ($stmt->fetch()) {
|
||||
$error_message = 'A category with this name already exists.';
|
||||
} else {
|
||||
$sql = "INSERT INTO categories (name) VALUES (?)";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$name]);
|
||||
|
||||
header("Location: categories.php?success=category_added");
|
||||
exit;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$error_message = 'Database error: ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Add New Category - IC-Inventory</title>
|
||||
<meta name="description" content="Add a new category.">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script src="https://unpkg.com/feather-icons"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
<?php require_once 'templates/sidebar.php'; ?>
|
||||
|
||||
<main id="content">
|
||||
<div class="header">
|
||||
<h1>Add New Category</h1>
|
||||
<div class="theme-switcher" id="theme-switcher">
|
||||
<i data-feather="moon"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="surface p-4">
|
||||
<?php if ($error_message): ?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="add-category.php" method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="name" class="form-label">Category Name*</label>
|
||||
<input type="text" class="form-control" id="name" name="name" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Add Category</button>
|
||||
<a href="categories.php" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
<script>
|
||||
feather.replace();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
94
add-location.php
Normal file
94
add-location.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
if (!can($_SESSION['user_role_id'], 'location', 'create')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
|
||||
$error_message = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$name = $_POST['name'] ?? '';
|
||||
|
||||
if (empty($name)) {
|
||||
$error_message = 'Please fill in the location name.';
|
||||
} else {
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
$stmt = $pdo->prepare('SELECT id FROM locations WHERE name = ?');
|
||||
$stmt->execute([$name]);
|
||||
if ($stmt->fetch()) {
|
||||
$error_message = 'A location with this name already exists.';
|
||||
} else {
|
||||
$sql = "INSERT INTO locations (name) VALUES (?)";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$name]);
|
||||
|
||||
header("Location: locations.php?success=location_added");
|
||||
exit;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$error_message = 'Database error: ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Add New Location - IC-Inventory</title>
|
||||
<meta name="description" content="Add a new location.">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script src="https://unpkg.com/feather-icons"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
<?php require_once 'templates/sidebar.php'; ?>
|
||||
|
||||
<main id="content">
|
||||
<div class="header">
|
||||
<h1>Add New Location</h1>
|
||||
<div class="theme-switcher" id="theme-switcher">
|
||||
<i data-feather="moon"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="surface p-4">
|
||||
<?php if ($error_message): ?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="add-location.php" method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="name" class="form-label">Location Name*</label>
|
||||
<input type="text" class="form-control" id="name" name="name" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Add Location</button>
|
||||
<a href="locations.php" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
<script>
|
||||
feather.replace();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
94
add-role.php
Normal file
94
add-role.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
if (!can($_SESSION['user_role_id'], 'role', 'create')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
|
||||
$error_message = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$name = $_POST['name'] ?? '';
|
||||
|
||||
if (empty($name)) {
|
||||
$error_message = 'Please fill in the role name.';
|
||||
} else {
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
$stmt = $pdo->prepare('SELECT id FROM roles WHERE name = ?');
|
||||
$stmt->execute([$name]);
|
||||
if ($stmt->fetch()) {
|
||||
$error_message = 'A role with this name already exists.';
|
||||
} else {
|
||||
$sql = "INSERT INTO roles (name) VALUES (?)";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$name]);
|
||||
|
||||
header("Location: roles.php?success=role_added");
|
||||
exit;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$error_message = 'Database error: ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Add New Role - IC-Inventory</title>
|
||||
<meta name="description" content="Add a new role.">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script src="https://unpkg.com/feather-icons"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
<?php require_once 'templates/sidebar.php'; ?>
|
||||
|
||||
<main id="content">
|
||||
<div class="header">
|
||||
<h1>Add New Role</h1>
|
||||
<div class="theme-switcher" id="theme-switcher">
|
||||
<i data-feather="moon"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="surface p-4">
|
||||
<?php if ($error_message): ?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="add-role.php" method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="name" class="form-label">Role Name*</label>
|
||||
<input type="text" class="form-control" id="name" name="name" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Add Role</button>
|
||||
<a href="roles.php" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
<script>
|
||||
feather.replace();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -4,7 +4,7 @@ require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
if (!can($_SESSION['user_role'], 'user', 'create')) {
|
||||
if (!can($_SESSION['user_role_id'], 'user', 'create')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -1,7 +1,21 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const assignedTo = document.getElementById('assigned_to');
|
||||
if (assignedTo) {
|
||||
const choices = new Choices(assignedTo, {
|
||||
new Choices(assignedTo, {
|
||||
removeItemButton: true,
|
||||
});
|
||||
}
|
||||
|
||||
const category = document.getElementById('category_id');
|
||||
if (category) {
|
||||
new Choices(category, {
|
||||
removeItemButton: true,
|
||||
});
|
||||
}
|
||||
|
||||
const location = document.getElementById('location_id');
|
||||
if (location) {
|
||||
new Choices(location, {
|
||||
removeItemButton: true,
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
|
||||
function can($role, $resource, $action) {
|
||||
function can($role_id, $resource, $action) {
|
||||
static $permissions = null;
|
||||
|
||||
if ($permissions === null) {
|
||||
@ -11,7 +11,7 @@ function can($role, $resource, $action) {
|
||||
$all_permissions = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
$permissions = [];
|
||||
foreach ($all_permissions as $p) {
|
||||
$permissions[$p['role']][$p['resource']][$p['action']] = $p['fields'] ?? '*';
|
||||
$permissions[$p['role_id']][$p['resource']][$p['action']] = $p['fields'] ?? '*';
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
// Handle database errors, maybe return false or log the error
|
||||
@ -19,9 +19,9 @@ function can($role, $resource, $action) {
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($permissions[$role][$resource][$action])) {
|
||||
if (in_array($action, ['read', 'update'])) {
|
||||
return $permissions[$role][$resource][$action];
|
||||
if (isset($permissions[$role_id][$resource][$action])) {
|
||||
if (in_array($action, ['read', 'update', 'create'])) {
|
||||
return $permissions[$role_id][$resource][$action];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
118
categories.php
Normal file
118
categories.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
// Permissions check
|
||||
if (!can($_SESSION['user_role_id'], 'category', 'read')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
|
||||
function get_categories() {
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query("SELECT * FROM categories ORDER BY name ASC");
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
return ['error' => 'Database error: ' . $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
$categories = get_categories();
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Category Management - IC-Inventory</title>
|
||||
<meta name="description" content="Category management for IC-Inventory.">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script src="https://unpkg.com/feather-icons"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
<?php require_once 'templates/sidebar.php'; ?>
|
||||
|
||||
<main id="content">
|
||||
<div class="header">
|
||||
<h1>Category Management</h1>
|
||||
<div>
|
||||
<?php if (can($_SESSION['user_role_id'], 'category', 'create')): ?>
|
||||
<a href="add-category.php" class="btn btn-primary">Add New Category</a>
|
||||
<?php endif; ?>
|
||||
<div class="theme-switcher" id="theme-switcher">
|
||||
<i data-feather="moon"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (isset($_GET['success'])): ?>
|
||||
<div class="alert alert-success">
|
||||
<?php
|
||||
if ($_GET['success'] === 'category_added') echo 'Category successfully added!';
|
||||
if ($_GET['success'] === 'category_updated') echo 'Category successfully updated!';
|
||||
if ($_GET['success'] === 'category_deleted') echo 'Category successfully deleted!';
|
||||
?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="surface p-4">
|
||||
<?php if (isset($categories['error'])): ?>
|
||||
<div class="alert alert-danger">
|
||||
<?php echo htmlspecialchars($categories['error']); ?>
|
||||
</div>
|
||||
<?php elseif (empty($categories)): ?>
|
||||
<div class="text-center p-5">
|
||||
<h4>No categories found.</h4>
|
||||
<?php if (can($_SESSION['user_role_id'], 'category', 'create')): ?>
|
||||
<p>Get started by adding your first category.</p>
|
||||
<a href="add-category.php" class="btn btn-primary">Add Category</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($categories as $category): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($category['name']); ?></td>
|
||||
<td>
|
||||
<?php if (can($_SESSION['user_role_id'], 'category', 'update')): ?>
|
||||
<a href="edit-category.php?id=<?php echo $category['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
|
||||
<?php endif; ?>
|
||||
<?php if (can($_SESSION['user_role_id'], 'category', 'delete')): ?>
|
||||
<a href="delete-category.php?id=<?php echo $category['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this category?');">Delete</a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
<script>
|
||||
feather.replace();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
5
db/migrations/006_create_locations_table.sql
Normal file
5
db/migrations/006_create_locations_table.sql
Normal file
@ -0,0 +1,5 @@
|
||||
CREATE TABLE IF NOT EXISTS `locations` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
1
db/migrations/007_add_location_id_to_assets.sql
Normal file
1
db/migrations/007_add_location_id_to_assets.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE `assets` ADD COLUMN `location_id` INT NULL, ADD FOREIGN KEY (`location_id`) REFERENCES `locations`(`id`) ON DELETE SET NULL;
|
||||
14
db/migrations/008_create_roles_table.sql
Normal file
14
db/migrations/008_create_roles_table.sql
Normal file
@ -0,0 +1,14 @@
|
||||
CREATE TABLE IF NOT EXISTS `roles` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Seed with existing roles
|
||||
INSERT INTO `roles` (`name`) VALUES
|
||||
('Admin'),
|
||||
('Asset Manager'),
|
||||
('IT Technician'),
|
||||
('Employee');
|
||||
16
db/migrations/009_add_role_id_to_users.sql
Normal file
16
db/migrations/009_add_role_id_to_users.sql
Normal file
@ -0,0 +1,16 @@
|
||||
-- Add role_id column
|
||||
ALTER TABLE `users` ADD COLUMN `role_id` INT(11) NULL AFTER `password`;
|
||||
|
||||
-- Update role_id from existing role name
|
||||
UPDATE `users` u
|
||||
JOIN `roles` r ON u.role = r.name
|
||||
SET u.role_id = r.id;
|
||||
|
||||
-- Make role_id not nullable
|
||||
ALTER TABLE `users` MODIFY `role_id` INT(11) NOT NULL;
|
||||
|
||||
-- Add foreign key constraint
|
||||
ALTER TABLE `users` ADD CONSTRAINT `fk_user_role` FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- Drop the old role column
|
||||
ALTER TABLE `users` DROP COLUMN `role`;
|
||||
22
db/migrations/010_update_role_permissions.sql
Normal file
22
db/migrations/010_update_role_permissions.sql
Normal file
@ -0,0 +1,22 @@
|
||||
-- Add role_id column
|
||||
ALTER TABLE `role_permissions` ADD COLUMN `role_id` INT(11) NULL AFTER `id`;
|
||||
|
||||
-- Update role_id from existing role name
|
||||
UPDATE `role_permissions` rp
|
||||
JOIN `roles` r ON rp.role = r.name
|
||||
SET rp.role_id = r.id;
|
||||
|
||||
-- Make role_id not nullable
|
||||
ALTER TABLE `role_permissions` MODIFY `role_id` INT(11) NOT NULL;
|
||||
|
||||
-- Drop the old unique key
|
||||
ALTER TABLE `role_permissions` DROP INDEX `role_resource_action`;
|
||||
|
||||
-- Drop the old role column
|
||||
ALTER TABLE `role_permissions` DROP COLUMN `role`;
|
||||
|
||||
-- Add new unique key with role_id
|
||||
ALTER TABLE `role_permissions` ADD UNIQUE KEY `role_resource_action` (`role_id`, `resource`, `action`);
|
||||
|
||||
-- Add foreign key to roles table
|
||||
ALTER TABLE `role_permissions` ADD CONSTRAINT `fk_permission_role` FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
1
db/migrations/011_add_permission_resource.sql
Normal file
1
db/migrations/011_add_permission_resource.sql
Normal file
@ -0,0 +1 @@
|
||||
INSERT INTO `role_permissions` (`role_id`, `resource`, `action`) VALUES (1, 'permission', 'update');
|
||||
1
db/migrations/012_cleanup_permissions_table.sql
Normal file
1
db/migrations/012_cleanup_permissions_table.sql
Normal file
@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS `permissions`;
|
||||
12
db/migrations/013_grant_full_permissions_to_super_admin.sql
Normal file
12
db/migrations/013_grant_full_permissions_to_super_admin.sql
Normal file
@ -0,0 +1,12 @@
|
||||
-- Grant all permissions on roles and permissions to Super Admin (role_id = 1)
|
||||
|
||||
INSERT INTO `role_permissions` (`role_id`, `resource`, `action`) VALUES
|
||||
-- Roles
|
||||
(1, 'role', 'create'),
|
||||
(1, 'role', 'read'),
|
||||
(1, 'role', 'update'),
|
||||
(1, 'role', 'delete'),
|
||||
-- Permissions
|
||||
(1, 'permission', 'read'),
|
||||
(1, 'permission', 'update')
|
||||
ON DUPLICATE KEY UPDATE `role_id` = `role_id`; -- Do nothing if the permission already exists
|
||||
8
debug_tables.php
Normal file
8
debug_tables.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query('SHOW TABLES');
|
||||
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
echo "<pre>";
|
||||
print_r($tables);
|
||||
echo "</pre>";
|
||||
43
delete-category.php
Normal file
43
delete-category.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
if (!can($_SESSION['user_role_id'], 'category', 'delete')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
|
||||
$category_id = $_GET['id'] ?? null;
|
||||
|
||||
if (!$category_id) {
|
||||
header('Location: categories.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// Set category_id to NULL for assets associated with this category
|
||||
$stmt = $pdo->prepare('UPDATE assets SET category_id = NULL WHERE category_id = ?');
|
||||
$stmt->execute([$category_id]);
|
||||
|
||||
// Delete the category
|
||||
$stmt = $pdo->prepare('DELETE FROM categories WHERE id = ?');
|
||||
$stmt->execute([$category_id]);
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
header("Location: categories.php?success=category_deleted");
|
||||
exit;
|
||||
|
||||
} catch (PDOException $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
// In a real app, log this error.
|
||||
header("Location: categories.php?error=db_error");
|
||||
exit;
|
||||
}
|
||||
43
delete-location.php
Normal file
43
delete-location.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
if (!can($_SESSION['user_role_id'], 'location', 'delete')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
|
||||
$location_id = $_GET['id'] ?? null;
|
||||
|
||||
if (!$location_id) {
|
||||
header('Location: locations.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// Set location_id to NULL for assets associated with this location
|
||||
$stmt = $pdo->prepare('UPDATE assets SET location_id = NULL WHERE location_id = ?');
|
||||
$stmt->execute([$location_id]);
|
||||
|
||||
// Delete the location
|
||||
$stmt = $pdo->prepare('DELETE FROM locations WHERE id = ?');
|
||||
$stmt->execute([$location_id]);
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
header("Location: locations.php?success=location_deleted");
|
||||
exit;
|
||||
|
||||
} catch (PDOException $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
// In a real app, log this error.
|
||||
header("Location: locations.php?error=db_error");
|
||||
exit;
|
||||
}
|
||||
41
delete-role.php
Normal file
41
delete-role.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
if (!can($_SESSION['user_role_id'], 'role', 'delete')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
|
||||
$role_id = $_GET['id'] ?? null;
|
||||
|
||||
if (!$role_id) {
|
||||
header('Location: roles.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
// Check if any user is assigned to this role
|
||||
$stmt = $pdo->prepare('SELECT id FROM users WHERE role_id = ?');
|
||||
$stmt->execute([$role_id]);
|
||||
if ($stmt->fetch()) {
|
||||
header("Location: roles.php?error=role_in_use");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Delete the role
|
||||
$stmt = $pdo->prepare('DELETE FROM roles WHERE id = ?');
|
||||
$stmt->execute([$role_id]);
|
||||
|
||||
header("Location: roles.php?success=role_deleted");
|
||||
exit;
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// In a real app, log this error.
|
||||
header("Location: roles.php?error=db_error");
|
||||
exit;
|
||||
}
|
||||
@ -4,18 +4,20 @@ require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
if (!can($_SESSION['user_role'], 'asset', 'update')) {
|
||||
if (!can($_SESSION['user_role_id'], 'asset', 'update')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
|
||||
$allowed_fields_str = can($_SESSION['user_role'], 'asset', 'update');
|
||||
$allowed_fields = ($allowed_fields_str === '*') ? ['name', 'asset_tag', 'status', 'location', 'manufacturer', 'model', 'purchase_date', 'assigned_to'] : explode(',', $allowed_fields_str);
|
||||
$allowed_fields_str = can($_SESSION['user_role_id'], 'asset', 'update');
|
||||
$allowed_fields = ($allowed_fields_str === '*') ? ['name', 'asset_tag', 'status', 'location_id', 'manufacturer', 'model', 'purchase_date', 'assigned_to', 'category_id'] : explode(',', $allowed_fields_str);
|
||||
|
||||
$success_message = '';
|
||||
$error_message = '';
|
||||
$asset = null;
|
||||
$users = [];
|
||||
$categories = [];
|
||||
$locations = [];
|
||||
|
||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||
header("Location: index.php");
|
||||
@ -39,6 +41,14 @@ try {
|
||||
$stmt = $pdo->query("SELECT id, name FROM users ORDER BY name");
|
||||
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Fetch categories for dropdown
|
||||
$stmt = $pdo->query("SELECT id, name FROM categories ORDER BY name");
|
||||
$categories = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Fetch locations for dropdown
|
||||
$stmt = $pdo->query("SELECT id, name FROM locations ORDER BY name");
|
||||
$locations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
$error_message = 'Database error: ' . $e->getMessage();
|
||||
}
|
||||
@ -50,7 +60,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
foreach ($allowed_fields as $field) {
|
||||
if (isset($_POST[$field])) {
|
||||
$value = $_POST[$field];
|
||||
if ($field === 'assigned_to' && $value === '') {
|
||||
if (($field === 'assigned_to' || $field === 'category_id' || $field === 'location_id') && $value === '') {
|
||||
$value = null;
|
||||
}
|
||||
$data[] = $value;
|
||||
@ -137,10 +147,30 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
</select>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if (in_array('location', $allowed_fields)): ?>
|
||||
<?php if (in_array('category_id', $allowed_fields)): ?>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="location" class="form-label">Location</label>
|
||||
<input type="text" class="form-control" id="location" name="location" value="<?php echo htmlspecialchars($asset['location']); ?>">
|
||||
<label for="category_id" class="form-label">Category</label>
|
||||
<select class="form-select" id="category_id" name="category_id">
|
||||
<option value="">No Category</option>
|
||||
<?php foreach ($categories as $category): ?>
|
||||
<option value="<?php echo $category['id']; ?>" <?php if ($asset['category_id'] == $category['id']) echo 'selected'; ?>>
|
||||
<?php echo htmlspecialchars($category['name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if (in_array('location_id', $allowed_fields)): ?>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="location_id" class="form-label">Location</label>
|
||||
<select class="form-select" id="location_id" name="location_id">
|
||||
<option value="">No Location</option>
|
||||
<?php foreach ($locations as $location): ?>
|
||||
<option value="<?php echo $location['id']; ?>" <?php if ($asset['location_id'] == $location['id']) echo 'selected'; ?>>
|
||||
<?php echo htmlspecialchars($location['name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
116
edit-category.php
Normal file
116
edit-category.php
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
if (!can($_SESSION['user_role_id'], 'category', 'update')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
|
||||
$error_message = '';
|
||||
$category_id = $_GET['id'] ?? null;
|
||||
|
||||
if (!$category_id) {
|
||||
header('Location: categories.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare('SELECT id, name FROM categories WHERE id = ?');
|
||||
$stmt->execute([$category_id]);
|
||||
$category = $stmt->fetch();
|
||||
|
||||
if (!$category) {
|
||||
header('Location: categories.php');
|
||||
exit;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$error_message = 'Database error: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$name = $_POST['name'] ?? '';
|
||||
|
||||
if (empty($name)) {
|
||||
$error_message = 'Please fill in the category name.';
|
||||
} else {
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
$stmt = $pdo->prepare('SELECT id FROM categories WHERE name = ? AND id != ?');
|
||||
$stmt->execute([$name, $category_id]);
|
||||
if ($stmt->fetch()) {
|
||||
$error_message = 'A category with this name already exists.';
|
||||
} else {
|
||||
$sql = "UPDATE categories SET name = ? WHERE id = ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$name, $category_id]);
|
||||
|
||||
header("Location: categories.php?success=category_updated");
|
||||
exit;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$error_message = 'Database error: ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Edit Category - IC-Inventory</title>
|
||||
<meta name="description" content="Edit an existing category.">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script src="https://unpkg.com/feather-icons"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
<?php require_once 'templates/sidebar.php'; ?>
|
||||
|
||||
<main id="content">
|
||||
<div class="header">
|
||||
<h1>Edit Category</h1>
|
||||
<div class="theme-switcher" id="theme-switcher">
|
||||
<i data-feather="moon"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="surface p-4">
|
||||
<?php if ($error_message): ?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($category): ?>
|
||||
<form action="edit-category.php?id=<?php echo $category['id']; ?>" method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="name" class="form-label">Category Name*</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($category['name']); ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Update Category</button>
|
||||
<a href="categories.php" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
<script>
|
||||
feather.replace();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
116
edit-location.php
Normal file
116
edit-location.php
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
if (!can($_SESSION['user_role_id'], 'location', 'update')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
|
||||
$error_message = '';
|
||||
$location_id = $_GET['id'] ?? null;
|
||||
|
||||
if (!$location_id) {
|
||||
header('Location: locations.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare('SELECT id, name FROM locations WHERE id = ?');
|
||||
$stmt->execute([$location_id]);
|
||||
$location = $stmt->fetch();
|
||||
|
||||
if (!$location) {
|
||||
header('Location: locations.php');
|
||||
exit;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$error_message = 'Database error: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$name = $_POST['name'] ?? '';
|
||||
|
||||
if (empty($name)) {
|
||||
$error_message = 'Please fill in the location name.';
|
||||
} else {
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
$stmt = $pdo->prepare('SELECT id FROM locations WHERE name = ? AND id != ?');
|
||||
$stmt->execute([$name, $location_id]);
|
||||
if ($stmt->fetch()) {
|
||||
$error_message = 'A location with this name already exists.';
|
||||
} else {
|
||||
$sql = "UPDATE locations SET name = ? WHERE id = ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$name, $location_id]);
|
||||
|
||||
header("Location: locations.php?success=location_updated");
|
||||
exit;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$error_message = 'Database error: ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Edit Location - IC-Inventory</title>
|
||||
<meta name="description" content="Edit an existing location.">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script src="https://unpkg.com/feather-icons"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
<?php require_once 'templates/sidebar.php'; ?>
|
||||
|
||||
<main id="content">
|
||||
<div class="header">
|
||||
<h1>Edit Location</h1>
|
||||
<div class="theme-switcher" id="theme-switcher">
|
||||
<i data-feather="moon"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="surface p-4">
|
||||
<?php if ($error_message): ?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($location): ?>
|
||||
<form action="edit-location.php?id=<?php echo $location['id']; ?>" method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="name" class="form-label">Location Name*</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($location['name']); ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Update Location</button>
|
||||
<a href="locations.php" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
<script>
|
||||
feather.replace();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
116
edit-role.php
Normal file
116
edit-role.php
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
if (!can($_SESSION['user_role_id'], 'role', 'update')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
|
||||
$error_message = '';
|
||||
$role_id = $_GET['id'] ?? null;
|
||||
|
||||
if (!$role_id) {
|
||||
header('Location: roles.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare('SELECT id, name FROM roles WHERE id = ?');
|
||||
$stmt->execute([$role_id]);
|
||||
$role = $stmt->fetch();
|
||||
|
||||
if (!$role) {
|
||||
header('Location: roles.php');
|
||||
exit;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$error_message = 'Database error: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$name = $_POST['name'] ?? '';
|
||||
|
||||
if (empty($name)) {
|
||||
$error_message = 'Please fill in the role name.';
|
||||
} else {
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
$stmt = $pdo->prepare('SELECT id FROM roles WHERE name = ? AND id != ?');
|
||||
$stmt->execute([$name, $role_id]);
|
||||
if ($stmt->fetch()) {
|
||||
$error_message = 'A role with this name already exists.';
|
||||
} else {
|
||||
$sql = "UPDATE roles SET name = ? WHERE id = ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$name, $role_id]);
|
||||
|
||||
header("Location: roles.php?success=role_updated");
|
||||
exit;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$error_message = 'Database error: ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Edit Role - IC-Inventory</title>
|
||||
<meta name="description" content="Edit an existing role.">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script src="https://unpkg.com/feather-icons"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
<?php require_once 'templates/sidebar.php'; ?>
|
||||
|
||||
<main id="content">
|
||||
<div class="header">
|
||||
<h1>Edit Role</h1>
|
||||
<div class="theme-switcher" id="theme-switcher">
|
||||
<i data-feather="moon"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="surface p-4">
|
||||
<?php if ($error_message): ?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($role): ?>
|
||||
<form action="edit-role.php?id=<?php echo $role['id']; ?>" method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="name" class="form-label">Role Name*</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($role['name']); ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Update Role</button>
|
||||
<a href="roles.php" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
<script>
|
||||
feather.replace();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -4,12 +4,12 @@ require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
if (!can($_SESSION['user_role'], 'user', 'update')) {
|
||||
if (!can($_SESSION['user_role_id'], 'user', 'update')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
|
||||
$allowed_fields_str = can($_SESSION['user_role'], 'user', 'update');
|
||||
$allowed_fields_str = can($_SESSION['user_role_id'], 'user', 'update');
|
||||
$allowed_fields = ($allowed_fields_str === '*') ? ['name', 'email', 'role'] : explode(',', $allowed_fields_str);
|
||||
|
||||
$error_message = '';
|
||||
|
||||
10
index.php
10
index.php
@ -5,7 +5,7 @@ require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
// Get allowed fields for the current user
|
||||
$allowed_fields_str = can($_SESSION['user_role'], 'asset', 'read');
|
||||
$allowed_fields_str = can($_SESSION['user_role_id'], 'asset', 'read');
|
||||
$allowed_fields = [];
|
||||
if ($allowed_fields_str === '*') {
|
||||
// Wildcard means all fields
|
||||
@ -189,7 +189,7 @@ function getStatusClass($status) {
|
||||
<div class="header">
|
||||
<h1>Asset Dashboard</h1>
|
||||
<div>
|
||||
<?php if (can($_SESSION['user_role'], 'asset', 'create')): ?>
|
||||
<?php if (can($_SESSION['user_role_id'], 'asset', 'create')): ?>
|
||||
<a href="add-asset.php" class="btn btn-primary">Add New Asset</a>
|
||||
<?php endif; ?>
|
||||
<div class="theme-switcher" id="theme-switcher">
|
||||
@ -233,7 +233,7 @@ function getStatusClass($status) {
|
||||
<?php elseif (empty($assets)): ?>
|
||||
<div class="text-center p-5">
|
||||
<h4>No assets found.</h4>
|
||||
<?php if (can($_SESSION['user_role'], 'asset', 'create')): ?>
|
||||
<?php if (can($_SESSION['user_role_id'], 'asset', 'create')): ?>
|
||||
<p>Get started by adding your first company asset.</p>
|
||||
<a href="add-asset.php" class="btn btn-primary">Add Asset</a>
|
||||
<?php endif; ?>
|
||||
@ -274,10 +274,10 @@ function getStatusClass($status) {
|
||||
</td>
|
||||
<?php endforeach; ?>
|
||||
<td>
|
||||
<?php if (can($_SESSION['user_role'], 'asset', 'update')): ?>
|
||||
<?php if (can($_SESSION['user_role_id'], 'asset', 'update')): ?>
|
||||
<a href="edit-asset.php?id=<?php echo $asset['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
|
||||
<?php endif; ?>
|
||||
<?php if (can($_SESSION['user_role'], 'asset', 'delete')): ?>
|
||||
<?php if (can($_SESSION['user_role_id'], 'asset', 'delete')): ?>
|
||||
<a href="delete-asset.php?id=<?php echo $asset['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this asset?');">Delete</a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
||||
118
locations.php
Normal file
118
locations.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
// Permissions check
|
||||
if (!can($_SESSION['user_role_id'], 'location', 'read')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
|
||||
function get_locations() {
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query("SELECT * FROM locations ORDER BY name ASC");
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
return ['error' => 'Database error: ' . $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
$locations = get_locations();
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Location Management - IC-Inventory</title>
|
||||
<meta name="description" content="Location management for IC-Inventory.">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script src="https://unpkg.com/feather-icons"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
<?php require_once 'templates/sidebar.php'; ?>
|
||||
|
||||
<main id="content">
|
||||
<div class="header">
|
||||
<h1>Location Management</h1>
|
||||
<div>
|
||||
<?php if (can($_SESSION['user_role_id'], 'location', 'create')): ?>
|
||||
<a href="add-location.php" class="btn btn-primary">Add New Location</a>
|
||||
<?php endif; ?>
|
||||
<div class="theme-switcher" id="theme-switcher">
|
||||
<i data-feather="moon"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (isset($_GET['success'])): ?>
|
||||
<div class="alert alert-success">
|
||||
<?php
|
||||
if ($_GET['success'] === 'location_added') echo 'Location successfully added!';
|
||||
if ($_GET['success'] === 'location_updated') echo 'Location successfully updated!';
|
||||
if ($_GET['success'] === 'location_deleted') echo 'Location successfully deleted!';
|
||||
?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="surface p-4">
|
||||
<?php if (isset($locations['error'])): ?>
|
||||
<div class="alert alert-danger">
|
||||
<?php echo htmlspecialchars($locations['error']); ?>
|
||||
</div>
|
||||
<?php elseif (empty($locations)): ?>
|
||||
<div class="text-center p-5">
|
||||
<h4>No locations found.</h4>
|
||||
<?php if (can($_SESSION['user_role_id'], 'location', 'create')): ?>
|
||||
<p>Get started by adding your first location.</p>
|
||||
<a href="add-location.php" class="btn btn-primary">Add Location</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($locations as $location): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($location['name']); ?></td>
|
||||
<td>
|
||||
<?php if (can($_SESSION['user_role_id'], 'location', 'update')): ?>
|
||||
<a href="edit-location.php?id=<?php echo $location['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
|
||||
<?php endif; ?>
|
||||
<?php if (can($_SESSION['user_role_id'], 'location', 'delete')): ?>
|
||||
<a href="delete-location.php?id=<?php echo $location['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this location?');">Delete</a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
<script>
|
||||
feather.replace();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -19,14 +19,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
} else {
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
|
||||
$stmt = $pdo->prepare("SELECT u.*, r.name as role_name FROM users u JOIN roles r ON u.role_id = r.id WHERE u.email = ?");
|
||||
$stmt->execute([$email]);
|
||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['user_name'] = $user['name'];
|
||||
$_SESSION['user_role'] = $user['role'];
|
||||
$_SESSION['user_role_id'] = $user['role_id'];
|
||||
$_SESSION['user_role_name'] = $user['role_name'];
|
||||
// For backwards compatibility with old code expecting a role name
|
||||
$_SESSION['user_role'] = $user['role_name'];
|
||||
header("Location: index.php");
|
||||
exit;
|
||||
} else {
|
||||
|
||||
126
roles.php
Normal file
126
roles.php
Normal file
@ -0,0 +1,126 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
// Permissions check
|
||||
if (!can($_SESSION['user_role_id'], 'role', 'read')) {
|
||||
// First, check if the user has permissions for this resource
|
||||
$user_role_id = $_SESSION['user_role'];
|
||||
$sql = "SELECT 1 FROM role_permissions WHERE role_id = :role_id AND resource = 'role' AND action = 'read'";
|
||||
$stmt = db()->prepare($sql);
|
||||
$stmt->execute(['role_id' => $user_role_id]);
|
||||
if ($stmt->rowCount() == 0) {
|
||||
// If no permissions, redirect
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
function get_roles() {
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query("SELECT * FROM roles ORDER BY name ASC");
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
return ['error' => 'Database error: ' . $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
$roles = get_roles();
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Role Management - IC-Inventory</title>
|
||||
<meta name="description" content="Role management for IC-Inventory.">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script src="https://unpkg.com/feather-icons"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
<?php require_once 'templates/sidebar.php'; ?>
|
||||
|
||||
<main id="content">
|
||||
<div class="header">
|
||||
<h1>Role Management</h1>
|
||||
<div>
|
||||
<?php if (can($_SESSION['user_role_id'], 'role', 'create')): ?>
|
||||
<a href="add-role.php" class="btn btn-primary">Add New Role</a>
|
||||
<?php endif; ?>
|
||||
<div class="theme-switcher" id="theme-switcher">
|
||||
<i data-feather="moon"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (isset($_GET['success'])): ?>
|
||||
<div class="alert alert-success">
|
||||
<?php
|
||||
if ($_GET['success'] === 'role_added') echo 'Role successfully added!';
|
||||
if ($_GET['success'] === 'role_updated') echo 'Role successfully updated!';
|
||||
if ($_GET['success'] === 'role_deleted') echo 'Role successfully deleted!';
|
||||
?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="surface p-4">
|
||||
<?php if (isset($roles['error'])): ?>
|
||||
<div class="alert alert-danger">
|
||||
<?php echo htmlspecialchars($roles['error']); ?>
|
||||
</div>
|
||||
<?php elseif (empty($roles)): ?>
|
||||
<div class="text-center p-5">
|
||||
<h4>No roles found.</h4>
|
||||
<?php if (can($_SESSION['user_role_id'], 'role', 'create')): ?>
|
||||
<p>Get started by adding your first role.</p>
|
||||
<a href="add-role.php" class="btn btn-primary">Add Role</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($roles as $role): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($role['name']); ?></td>
|
||||
<td>
|
||||
<?php if (can($_SESSION['user_role_id'], 'role', 'update')): ?>
|
||||
<a href="edit-role.php?id=<?php echo $role['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
|
||||
<?php endif; ?>
|
||||
<?php if (can($_SESSION['user_role_id'], 'role', 'delete')): ?>
|
||||
<a href="delete-role.php?id=<?php echo $role['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this role?');">Delete</a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
<script>
|
||||
feather.replace();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -3,12 +3,74 @@ require_once 'db/config.php';
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$file = 'db/migrations/002_create_users_table.sql';
|
||||
$sql = file_get_contents($file);
|
||||
$pdo->exec($sql);
|
||||
echo "Successfully ran migration: $file\n";
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
echo "All migrations ran successfully.\n";
|
||||
// 1. Create migrations table if it doesn't exist
|
||||
$pdo->exec("CREATE TABLE IF NOT EXISTS migrations (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
migration_file VARCHAR(255) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY (migration_file)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
|
||||
|
||||
// 2. Pre-populate with migrations that are known to have run already
|
||||
$known_migrations = [
|
||||
'001_create_assets_table.sql',
|
||||
'002_create_users_table.sql',
|
||||
'003_create_permissions_table.sql',
|
||||
'004_create_categories_table.sql',
|
||||
'005_add_category_id_to_assets.sql'
|
||||
];
|
||||
|
||||
$stmt = $pdo->prepare("INSERT IGNORE INTO migrations (migration_file) VALUES (?)");
|
||||
foreach ($known_migrations as $migration) {
|
||||
$stmt->execute([$migration]);
|
||||
}
|
||||
|
||||
// 3. Get all migrations that have already been run
|
||||
$ran_migrations_stmt = $pdo->query("SELECT migration_file FROM migrations");
|
||||
$ran_migrations = $ran_migrations_stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
// 4. Get all available migration files
|
||||
$migration_files = glob('db/migrations/*.sql');
|
||||
sort($migration_files);
|
||||
|
||||
// 5. Determine which migrations to run
|
||||
$migrations_to_run = [];
|
||||
foreach ($migration_files as $file) {
|
||||
if (!in_array(basename($file), $ran_migrations)) {
|
||||
$migrations_to_run[] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($migrations_to_run)) {
|
||||
echo "Database is already up to date.\n";
|
||||
} else {
|
||||
// 6. Run the new migrations
|
||||
foreach ($migrations_to_run as $file) {
|
||||
try {
|
||||
$sql = file_get_contents($file);
|
||||
if (empty(trim($sql))) {
|
||||
echo "Skipping empty migration file: $file\n";
|
||||
$stmt = $pdo->prepare("INSERT INTO migrations (migration_file) VALUES (?)");
|
||||
$stmt->execute([basename($file)]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$pdo->exec($sql);
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO migrations (migration_file) VALUES (?)");
|
||||
$stmt->execute([basename($file)]);
|
||||
|
||||
echo "Successfully ran migration: " . basename($file) . "\n";
|
||||
|
||||
} catch (PDOException $e) {
|
||||
echo "Error running migration " . basename($file) . ": " . $e->getMessage() . "\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
echo "All new migrations ran successfully.\n";
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
die("Database error: " . $e->getMessage());
|
||||
|
||||
61
settings.php
61
settings.php
@ -2,9 +2,9 @@
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'auth-check.php';
|
||||
require_once 'auth-helpers.php';
|
||||
|
||||
// Only Admins can access this page
|
||||
if ($_SESSION['user_role'] !== 'Admin') {
|
||||
if (!can($_SESSION['user_role_id'], 'permission', 'update')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
@ -12,8 +12,16 @@ if ($_SESSION['user_role'] !== 'Admin') {
|
||||
$success_message = '';
|
||||
$error_message = '';
|
||||
|
||||
$roles = ['Admin', 'Asset Manager', 'IT Technician', 'Employee'];
|
||||
$resources = ['asset', 'user'];
|
||||
try {
|
||||
$pdo = db();
|
||||
$roles_stmt = $pdo->query('SELECT * FROM roles ORDER BY name');
|
||||
$roles = $roles_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
$error_message = "Database error fetching roles: " . $e->getMessage();
|
||||
$roles = [];
|
||||
}
|
||||
|
||||
$resources = ['asset', 'user', 'category', 'location', 'role', 'permission'];
|
||||
$actions = ['create', 'read', 'update', 'delete'];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
@ -21,22 +29,26 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$pdo = db();
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// Clear existing permissions
|
||||
$pdo->exec('TRUNCATE TABLE role_permissions');
|
||||
// Clear existing permissions, but not for the super admin
|
||||
$pdo->exec('DELETE FROM role_permissions WHERE role_id != 1');
|
||||
|
||||
$stmt = $pdo->prepare('INSERT INTO role_permissions (role, resource, action, fields) VALUES (?, ?, ?, ?)');
|
||||
$stmt = $pdo->prepare('INSERT INTO role_permissions (role_id, resource, action, fields) VALUES (?, ?, ?, ?)');
|
||||
|
||||
$permissions = $_POST['permissions'] ?? [];
|
||||
|
||||
foreach ($roles as $role) {
|
||||
if ($role['id'] == 1) continue; // Skip Admin role, its permissions are immutable
|
||||
foreach ($resources as $resource) {
|
||||
foreach ($actions as $action) {
|
||||
if (!empty($permissions[$role][$resource][$action]['enabled'])) {
|
||||
$fields = $permissions[$role][$resource][$action]['fields'] ?? null;
|
||||
if (in_array($action, ['read', 'update']) && empty($fields)) {
|
||||
$fields = '*'; // Default to all fields if not specified
|
||||
if (isset($permissions[$role['id']][$resource][$action]['enabled']) && $permissions[$role['id']][$resource][$action]['enabled'] == '1') {
|
||||
$fields = null;
|
||||
if (in_array($action, ['read', 'update', 'create'])) {
|
||||
$fields = $permissions[$role['id']][$resource][$action]['fields'] ?? '*';
|
||||
if (empty($fields)) {
|
||||
$fields = '*';
|
||||
}
|
||||
}
|
||||
$stmt->execute([$role, $resource, $action, $fields]);
|
||||
$stmt->execute([$role['id'], $resource, $action, $fields]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -57,7 +69,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
function get_permissions() {
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query('SELECT * FROM role_permissions ORDER BY role, resource, action');
|
||||
$stmt = $pdo->query('SELECT * FROM role_permissions ORDER BY role_id, resource, action');
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
return ['error' => 'Database error: ' . $e->getMessage()];
|
||||
@ -69,7 +81,7 @@ $permissions_from_db = get_permissions();
|
||||
// Group permissions by role and resource for easier display
|
||||
$grouped_permissions = [];
|
||||
foreach ($permissions_from_db as $p) {
|
||||
$grouped_permissions[$p['role']][$p['resource']][$p['action']] = $p['fields'];
|
||||
$grouped_permissions[$p['role_id']][$p['resource']][$p['action']] = $p['fields'];
|
||||
}
|
||||
|
||||
?>
|
||||
@ -95,9 +107,6 @@ foreach ($permissions_from_db as $p) {
|
||||
<main id="content">
|
||||
<div class="header">
|
||||
<h1>Settings - Role Permissions</h1>
|
||||
<div class="theme-switcher" id="theme-switcher">
|
||||
<i data-feather="moon"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="surface p-4">
|
||||
@ -126,17 +135,25 @@ foreach ($permissions_from_db as $p) {
|
||||
<?php foreach ($resources as $resource_idx => $resource): ?>
|
||||
<tr>
|
||||
<?php if ($resource_idx === 0): ?>
|
||||
<td rowspan="<?php echo count($resources); ?>" class="align-middle"><strong><?php echo $role; ?></strong></td>
|
||||
<td rowspan="<?php echo count($resources); ?>" class="align-middle">
|
||||
<strong><?php echo htmlspecialchars($role['name']); ?></strong>
|
||||
<?php if ($role['id'] == 1): ?>
|
||||
<span class="badge bg-primary">Super Admin</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<?php endif; ?>
|
||||
<td class="align-middle"><?php echo ucfirst($resource); ?></td>
|
||||
|
||||
<?php foreach ($actions as $action): ?>
|
||||
<?php foreach ($actions as $action):
|
||||
$is_disabled = ($role['id'] == 1); // Disable all checkboxes for Super Admin
|
||||
$is_checked = isset($grouped_permissions[$role['id']][$resource]) && array_key_exists($action, $grouped_permissions[$role['id']][$resource]);
|
||||
?>
|
||||
<td class="align-middle">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="permissions[<?php echo $role; ?>][<?php echo $resource; ?>][<?php echo $action; ?>][enabled]" value="1" <?php echo isset($grouped_permissions[$role][$resource][$action]) ? 'checked' : ''; ?>>
|
||||
<input class="form-check-input" type="checkbox" name="permissions[<?php echo $role['id']; ?>][<?php echo $resource; ?>][<?php echo $action; ?>][enabled]" value="1" <?php echo $is_checked ? 'checked' : ''; ?> <?php echo $is_disabled ? 'disabled' : ''; ?>>
|
||||
</div>
|
||||
<?php if (in_array($action, ['read', 'update'])): ?>
|
||||
<input type="text" class="form-control form-control-sm mt-1" name="permissions[<?php echo $role; ?>][<?php echo $resource; ?>][<?php echo $action; ?>][fields]" placeholder="* for all" value="<?php echo htmlspecialchars($grouped_permissions[$role][$resource][$action] ?? ''); ?>">
|
||||
<?php if (in_array($action, ['read', 'update', 'create'])): ?>
|
||||
<input type="text" class="form-control form-control-sm mt-1" name="permissions[<?php echo $role['id']; ?>][<?php echo $resource; ?>][<?php echo $action; ?>][fields]" placeholder="* for all" value="<?php echo htmlspecialchars($grouped_permissions[$role['id']][$resource][$action] ?? '*'); ?>" <?php echo $is_disabled ? 'disabled' : ''; ?>>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<?php endforeach; ?>
|
||||
|
||||
@ -19,13 +19,39 @@ $current_page = basename($_SERVER['PHP_SELF']);
|
||||
Assets
|
||||
</a>
|
||||
</li>
|
||||
<?php if ($_SESSION['user_role'] === 'Admin'): ?>
|
||||
<?php if (can($_SESSION['user_role_id'], 'user', 'read')): ?>
|
||||
<li>
|
||||
<a href="users.php" class="nav-link <?php echo ($current_page === 'users.php' || $current_page === 'add-user.php' || $current_page === 'edit-user.php') ? 'active' : ''; ?>">
|
||||
<i data-feather="users" class="me-2"></i>
|
||||
Users
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (can($_SESSION['user_role_id'], 'category', 'read')): ?>
|
||||
<li>
|
||||
<a href="categories.php" class="nav-link <?php echo ($current_page === 'categories.php' || $current_page === 'add-category.php' || $current_page === 'edit-category.php') ? 'active' : ''; ?>">
|
||||
<i data-feather="grid" class="me-2"></i>
|
||||
Categories
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (can($_SESSION['user_role_id'], 'location', 'read')): ?>
|
||||
<li>
|
||||
<a href="locations.php" class="nav-link <?php echo ($current_page === 'locations.php' || $current_page === 'add-location.php' || $current_page === 'edit-location.php') ? 'active' : ''; ?>">
|
||||
<i data-feather="map-pin" class="me-2"></i>
|
||||
Locations
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (can($_SESSION['user_role_id'], 'role', 'read')): ?>
|
||||
<li>
|
||||
<a href="roles.php" class="nav-link <?php echo ($current_page === 'roles.php' || $current_page === 'add-role.php' || $current_page === 'edit-role.php') ? 'active' : ''; ?>">
|
||||
<i data-feather="shield" class="me-2"></i>
|
||||
Roles
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (can($_SESSION['user_role_id'], 'permission', 'update')): ?>
|
||||
<li>
|
||||
<a href="settings.php" class="nav-link <?php echo ($current_page === 'settings.php') ? 'active' : ''; ?>">
|
||||
<i data-feather="settings" class="me-2"></i>
|
||||
|
||||
12
users.php
12
users.php
@ -7,13 +7,13 @@ require_once 'auth-helpers.php';
|
||||
|
||||
|
||||
// Only Admins can access this page
|
||||
if (!can($_SESSION['user_role'], 'user', 'read')) {
|
||||
if (!can($_SESSION['user_role_id'], 'user', 'read')) {
|
||||
header('Location: index.php?error=access_denied');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get allowed fields for the current user
|
||||
$allowed_fields_str = can($_SESSION['user_role'], 'user', 'read');
|
||||
$allowed_fields_str = can($_SESSION['user_role_id'], 'user', 'read');
|
||||
$allowed_fields = ($allowed_fields_str && $allowed_fields_str !== '*') ? explode(',', $allowed_fields_str) : [];
|
||||
|
||||
if ($allowed_fields_str === '*') {
|
||||
@ -75,7 +75,7 @@ $users = get_users($allowed_fields);
|
||||
<div class="header">
|
||||
<h1>User Management</h1>
|
||||
<div>
|
||||
<?php if (can($_SESSION['user_role'], 'user', 'create')): ?>
|
||||
<?php if (can($_SESSION['user_role_id'], 'user', 'create')): ?>
|
||||
<a href="add-user.php" class="btn btn-primary">Add New User</a>
|
||||
<?php endif; ?>
|
||||
<div class="theme-switcher" id="theme-switcher">
|
||||
@ -102,7 +102,7 @@ $users = get_users($allowed_fields);
|
||||
<?php elseif (empty($users)): ?>
|
||||
<div class="text-center p-5">
|
||||
<h4>No users found.</h4>
|
||||
<?php if (can($_SESSION['user_role'], 'user', 'create')): ?>
|
||||
<?php if (can($_SESSION['user_role_id'], 'user', 'create')): ?>
|
||||
<p>Get started by adding your first user.</p>
|
||||
<a href="add-user.php" class="btn btn-primary">Add User</a>
|
||||
<?php endif; ?>
|
||||
@ -125,10 +125,10 @@ $users = get_users($allowed_fields);
|
||||
<td><?php echo htmlspecialchars($user[$field]); ?></td>
|
||||
<?php endforeach; ?>
|
||||
<td>
|
||||
<?php if (can($_SESSION['user_role'], 'user', 'update')): ?>
|
||||
<?php if (can($_SESSION['user_role_id'], 'user', 'update')): ?>
|
||||
<a href="edit-user.php?id=<?php echo $user['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
|
||||
<?php endif; ?>
|
||||
<?php if (can($_SESSION['user_role'], 'user', 'delete')): ?>
|
||||
<?php if (can($_SESSION['user_role_id'], 'user', 'delete')): ?>
|
||||
<a href="delete-user.php?id=<?php echo $user['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this user?');">Delete</a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user