diff --git a/add-asset.php b/add-asset.php index 293df0d..7f0e926 100644 --- a/add-asset.php +++ b/add-asset.php @@ -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') { + @@ -122,10 +145,43 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { - +
- - + + +
+ + +
+ + +
+ + +
+ +
@@ -159,6 +215,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { + + diff --git a/add-category.php b/add-category.php new file mode 100644 index 0000000..0871391 --- /dev/null +++ b/add-category.php @@ -0,0 +1,94 @@ +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(); + } + } +} +?> + + + + + + Add New Category - IC-Inventory + + + + + + + + + + +
+ + +
+
+

Add New Category

+
+ +
+
+ +
+ +
+ + +
+
+
+ + +
+
+ + + Cancel +
+
+
+
+ + + + + + diff --git a/add-location.php b/add-location.php new file mode 100644 index 0000000..5bc34bf --- /dev/null +++ b/add-location.php @@ -0,0 +1,94 @@ +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(); + } + } +} +?> + + + + + + Add New Location - IC-Inventory + + + + + + + + + + +
+ + +
+
+

Add New Location

+
+ +
+
+ +
+ +
+ + +
+
+
+ + +
+
+ + + Cancel +
+
+
+
+ + + + + + diff --git a/add-role.php b/add-role.php new file mode 100644 index 0000000..9a20e9e --- /dev/null +++ b/add-role.php @@ -0,0 +1,94 @@ +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(); + } + } +} +?> + + + + + + Add New Role - IC-Inventory + + + + + + + + + + +
+ + +
+
+

Add New Role

+
+ +
+
+ +
+ +
+ + +
+
+
+ + +
+
+ + + Cancel +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/add-user.php b/add-user.php index 34ee2c6..12d7f04 100644 --- a/add-user.php +++ b/add-user.php @@ -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; } diff --git a/assets/js/choices.js b/assets/js/choices.js index a60b61c..8ea0f6b 100644 --- a/assets/js/choices.js +++ b/assets/js/choices.js @@ -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, }); } diff --git a/auth-helpers.php b/auth-helpers.php index 45e2bd4..570aad0 100644 --- a/auth-helpers.php +++ b/auth-helpers.php @@ -1,7 +1,7 @@ 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; } diff --git a/categories.php b/categories.php new file mode 100644 index 0000000..1786f65 --- /dev/null +++ b/categories.php @@ -0,0 +1,118 @@ +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(); + +?> + + + + + + Category Management - IC-Inventory + + + + + + + + + + +
+ + +
+
+

Category Management

+
+ + Add New Category + +
+ +
+
+
+ + +
+ +
+ + +
+ +
+ +
+ +
+

No categories found.

+ +

Get started by adding your first category.

+ Add Category + +
+ +
+ + + + + + + + + + + + + + + +
NameActions
+ + Edit + + + Delete + +
+
+ +
+
+
+ + + + + + diff --git a/db/migrations/006_create_locations_table.sql b/db/migrations/006_create_locations_table.sql new file mode 100644 index 0000000..348a519 --- /dev/null +++ b/db/migrations/006_create_locations_table.sql @@ -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 +); diff --git a/db/migrations/007_add_location_id_to_assets.sql b/db/migrations/007_add_location_id_to_assets.sql new file mode 100644 index 0000000..925c693 --- /dev/null +++ b/db/migrations/007_add_location_id_to_assets.sql @@ -0,0 +1 @@ +ALTER TABLE `assets` ADD COLUMN `location_id` INT NULL, ADD FOREIGN KEY (`location_id`) REFERENCES `locations`(`id`) ON DELETE SET NULL; diff --git a/db/migrations/008_create_roles_table.sql b/db/migrations/008_create_roles_table.sql new file mode 100644 index 0000000..fb6fe0b --- /dev/null +++ b/db/migrations/008_create_roles_table.sql @@ -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'); diff --git a/db/migrations/009_add_role_id_to_users.sql b/db/migrations/009_add_role_id_to_users.sql new file mode 100644 index 0000000..39b2b4d --- /dev/null +++ b/db/migrations/009_add_role_id_to_users.sql @@ -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`; diff --git a/db/migrations/010_update_role_permissions.sql b/db/migrations/010_update_role_permissions.sql new file mode 100644 index 0000000..301c107 --- /dev/null +++ b/db/migrations/010_update_role_permissions.sql @@ -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; diff --git a/db/migrations/011_add_permission_resource.sql b/db/migrations/011_add_permission_resource.sql new file mode 100644 index 0000000..2cc8c2b --- /dev/null +++ b/db/migrations/011_add_permission_resource.sql @@ -0,0 +1 @@ +INSERT INTO `role_permissions` (`role_id`, `resource`, `action`) VALUES (1, 'permission', 'update'); diff --git a/db/migrations/012_cleanup_permissions_table.sql b/db/migrations/012_cleanup_permissions_table.sql new file mode 100644 index 0000000..4852fa9 --- /dev/null +++ b/db/migrations/012_cleanup_permissions_table.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS `permissions`; diff --git a/db/migrations/013_grant_full_permissions_to_super_admin.sql b/db/migrations/013_grant_full_permissions_to_super_admin.sql new file mode 100644 index 0000000..85163a0 --- /dev/null +++ b/db/migrations/013_grant_full_permissions_to_super_admin.sql @@ -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 \ No newline at end of file diff --git a/debug_tables.php b/debug_tables.php new file mode 100644 index 0000000..033e12b --- /dev/null +++ b/debug_tables.php @@ -0,0 +1,8 @@ +query('SHOW TABLES'); +$tables = $stmt->fetchAll(PDO::FETCH_COLUMN); +echo "
";
+print_r($tables);
+echo "
"; diff --git a/delete-category.php b/delete-category.php new file mode 100644 index 0000000..5fd10c8 --- /dev/null +++ b/delete-category.php @@ -0,0 +1,43 @@ +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; +} diff --git a/delete-location.php b/delete-location.php new file mode 100644 index 0000000..1163353 --- /dev/null +++ b/delete-location.php @@ -0,0 +1,43 @@ +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; +} diff --git a/delete-role.php b/delete-role.php new file mode 100644 index 0000000..621c572 --- /dev/null +++ b/delete-role.php @@ -0,0 +1,41 @@ +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; +} diff --git a/edit-asset.php b/edit-asset.php index 1dfb261..faa18e3 100644 --- a/edit-asset.php +++ b/edit-asset.php @@ -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') { - +
- - + + +
+ + +
+ +
diff --git a/edit-category.php b/edit-category.php new file mode 100644 index 0000000..0a54e26 --- /dev/null +++ b/edit-category.php @@ -0,0 +1,116 @@ +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(); + } + } +} +?> + + + + + + Edit Category - IC-Inventory + + + + + + + + + + +
+ + +
+
+

Edit Category

+
+ +
+
+ +
+ +
+ + + +
+
+
+ + +
+
+ + + Cancel +
+ +
+
+
+ + + + + + diff --git a/edit-location.php b/edit-location.php new file mode 100644 index 0000000..0ff373e --- /dev/null +++ b/edit-location.php @@ -0,0 +1,116 @@ +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(); + } + } +} +?> + + + + + + Edit Location - IC-Inventory + + + + + + + + + + +
+ + +
+
+

Edit Location

+
+ +
+
+ +
+ +
+ + + +
+
+
+ + +
+
+ + + Cancel +
+ +
+
+
+ + + + + + diff --git a/edit-role.php b/edit-role.php new file mode 100644 index 0000000..6d28b6e --- /dev/null +++ b/edit-role.php @@ -0,0 +1,116 @@ +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(); + } + } +} +?> + + + + + + Edit Role - IC-Inventory + + + + + + + + + + +
+ + +
+
+

Edit Role

+
+ +
+
+ +
+ +
+ + + +
+
+
+ + +
+
+ + + Cancel +
+ +
+
+
+ + + + + + \ No newline at end of file diff --git a/edit-user.php b/edit-user.php index c0169d7..a2e81b3 100644 --- a/edit-user.php +++ b/edit-user.php @@ -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 = ''; diff --git a/index.php b/index.php index 60b823c..38409aa 100644 --- a/index.php +++ b/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) {

Asset Dashboard

- + Add New Asset
@@ -233,7 +233,7 @@ function getStatusClass($status) {

No assets found.

- +

Get started by adding your first company asset.

Add Asset @@ -274,10 +274,10 @@ function getStatusClass($status) { - + Edit - + Delete diff --git a/locations.php b/locations.php new file mode 100644 index 0000000..43ebe66 --- /dev/null +++ b/locations.php @@ -0,0 +1,118 @@ +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(); + +?> + + + + + + Location Management - IC-Inventory + + + + + + + + + + +
+ + +
+
+

Location Management

+
+ + Add New Location + +
+ +
+
+
+ + +
+ +
+ + +
+ +
+ +
+ +
+

No locations found.

+ +

Get started by adding your first location.

+ Add Location + +
+ +
+ + + + + + + + + + + + + + + +
NameActions
+ + Edit + + + Delete + +
+
+ +
+
+
+ + + + + + diff --git a/login.php b/login.php index cd5d2a0..b1557f8 100644 --- a/login.php +++ b/login.php @@ -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 { diff --git a/roles.php b/roles.php new file mode 100644 index 0000000..2094772 --- /dev/null +++ b/roles.php @@ -0,0 +1,126 @@ +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(); + +?> + + + + + + Role Management - IC-Inventory + + + + + + + + + + +
+ + +
+
+

Role Management

+
+ + Add New Role + +
+ +
+
+
+ + +
+ +
+ + +
+ +
+ +
+ +
+

No roles found.

+ +

Get started by adding your first role.

+ Add Role + +
+ +
+ + + + + + + + + + + + + + + +
NameActions
+ + Edit + + + Delete + +
+
+ +
+
+
+ + + + + + \ No newline at end of file diff --git a/run-migrations.php b/run-migrations.php index 559bc8c..82337b7 100644 --- a/run-migrations.php +++ b/run-migrations.php @@ -3,14 +3,76 @@ 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()); } -?> \ No newline at end of file +?> diff --git a/settings.php b/settings.php index 1f9fc92..a7f3bfa 100644 --- a/settings.php +++ b/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) {

Settings - Role Permissions

-
- -
@@ -126,17 +135,25 @@ foreach ($permissions_from_db as $p) { $resource): ?> - + + + + Super Admin + + - +
- > + >
- - + + > @@ -158,4 +175,4 @@ foreach ($permissions_from_db as $p) { feather.replace(); - \ No newline at end of file + diff --git a/templates/sidebar.php b/templates/sidebar.php index acc665e..b2e8f62 100644 --- a/templates/sidebar.php +++ b/templates/sidebar.php @@ -19,13 +19,39 @@ $current_page = basename($_SERVER['PHP_SELF']); Assets - +
  • Users
  • + + +
  • + + + Categories + +
  • + + +
  • + + + Locations + +
  • + + +
  • + + + Roles + +
  • + +
  • diff --git a/users.php b/users.php index 72bdb94..caceb8b 100644 --- a/users.php +++ b/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);

    User Management

    - + Add New User
    @@ -102,7 +102,7 @@ $users = get_users($allowed_fields);

    No users found.

    - +

    Get started by adding your first user.

    Add User @@ -125,10 +125,10 @@ $users = get_users($allowed_fields); - + Edit - + Delete