Membangun CMS Sederhana dengan PHP & MySQL - Part 5: Manajemen Pengguna & Sistem Peran (Roles)
Selamat datang di Bagian 5 dari seri tutorial CMS PHP! Sejauh ini, kita telah membangun frontend yang dinamis (Part 1), sistem login aman (Part 2), manajemen artikel (Part 3), dan manajemen kategori (Part 4). Sekarang, kita akan meningkatkan aspek pengelolaan dan keamanan CMS dengan mengimplementasikan Manajemen Pengguna dan Sistem Peran (Roles).
Dengan fitur ini, kita bisa:
- Membuat beberapa akun pengguna untuk mengakses area admin.
- Menetapkan peran yang berbeda untuk setiap pengguna (misalnya,
admin
daneditor
). - Membatasi akses ke fitur-fitur tertentu di area admin berdasarkan peran pengguna.
- Mengimplementasikan operasi CRUD (Create, Read, Update, Delete) untuk pengguna (biasanya hanya bisa dilakukan oleh pengguna dengan peran
admin
).
Fitur ini krusial jika CMS akan digunakan oleh lebih dari satu orang atau jika Anda ingin membatasi kemampuan pengguna tertentu.
Prasyarat
Pastikan Anda telah mengikuti dan memahami bagian-bagian sebelumnya dari seri ini, terutama Part 2 mengenai sistem login dan sesi, karena kita akan banyak bekerja dengan variabel sesi $_SESSION['role']
.
Persiapan: Memperjelas Peran
Kita sudah memiliki kolom role
di tabel users
(dibuat di Part 2) dengan nilai default 'editor'
dan kita membuat user admin awal dengan peran 'admin'
. Mari kita definisikan apa saja yang bisa dilakukan oleh masing-masing peran:
- Admin:
- Akses penuh ke semua fitur CMS.
- Bisa mengelola artikel, kategori, dan pengguna lain.
- Bisa mengubah pengaturan situs (jika ada nanti).
- Editor:
- Bisa mengelola artikel (menambah, mengedit, menghapus artikelnya sendiri atau semua artikel, tergantung kebijakan).
- Bisa mengelola kategori.
- Tidak bisa mengelola pengguna atau pengaturan situs.
Untuk tutorial ini, kita akan buat editor
bisa mengelola semua artikel dan kategori, tetapi tidak bisa mengelola pengguna.
Langkah 1: Membatasi Akses Navigasi Berdasarkan Peran
Kita sudah melakukan ini sedikit di admin/index.php
dan admin/manage_posts.php
(dan halaman manajemen lainnya) untuk link “Kelola Pengguna”. Mari kita pastikan ini konsisten.
Modifikasi Navigasi di File Admin (admin/index.php
, admin/manage_posts.php
, admin/manage_categories.php
, admin/add_post.php
, admin/edit_post.php
, admin/add_category.php
, admin/edit_category.php
):
Cari bagian navigasi di file-file tersebut:
<nav class="admin-nav">
<ul>
<li><a href="index.php" class="<?php echo (basename($_SERVER['PHP_SELF']) == 'index.php') ? 'active' : ''; ?>">Dashboard</a></li>
<li><a href="manage_posts.php" class="<?php echo (basename($_SERVER['PHP_SELF']) == 'manage_posts.php' || basename($_SERVER['PHP_SELF']) == 'add_post.php' || basename($_SERVER['PHP_SELF']) == 'edit_post.php') ? 'active' : ''; ?>">Kelola Artikel</a></li>
<li><a href="manage_categories.php" class="<?php echo (basename($_SERVER['PHP_SELF']) == 'manage_categories.php' || basename($_SERVER['PHP_SELF']) == 'add_category.php' || basename($_SERVER['PHP_SELF']) == 'edit_category.php') ? 'active' : ''; ?>">Kelola Kategori</a></li>
<?php if (isset($current_user_role) && $current_user_role === 'admin'): ?>
<li><a href="manage_users.php" class="<?php echo (basename($_SERVER['PHP_SELF']) == 'manage_users.php' || basename($_SERVER['PHP_SELF']) == 'add_user.php' || basename($_SERVER['PHP_SELF']) == 'edit_user.php') ? 'active' : ''; ?>">Kelola Pengguna</a></li>
<?php endif; ?>
<li><a href="../index.php" target="_blank">Lihat Situs</a></li>
</ul>
</nav>
Perubahan:
- Menambahkan
class="active"
secara dinamis ke link navigasi yang sesuai dengan halaman saat ini. - Memastikan link “Kelola Pengguna” hanya muncul jika
$current_user_role
(dariauth_check.php
) adalah'admin'
. - Variabel
$current_user_role
harus tersedia. Pastikanauth_check.php
di-include dan mendefinisikannya.
Langkah 2: Halaman Manajemen Pengguna (admin/manage_users.php
)
Halaman ini (hanya bisa diakses oleh admin
) akan menampilkan daftar semua pengguna dan menyediakan opsi CRUD.
Buat file admin/manage_users.php
:
<?php
// admin/manage_users.php
require_once 'auth_check.php';
require_once '../includes/db_connect.php';
require_once '../includes/functions.php';
// Hanya admin yang boleh mengakses halaman ini
if ($current_user_role !== 'admin') {
set_flash_message('auth_error', "Anda tidak memiliki izin untuk mengakses halaman ini.", "error");
header("Location: index.php"); // Redirect ke dashboard atau halaman lain
exit;
}
$page_title_admin = "Kelola Pengguna";
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($page_title_admin); ?> - CMS Admin</title>
<link rel="stylesheet" href="../css/admin_style.css">
</head>
<body>
<header class="admin-header">
<h1>Admin Panel</h1>
<div>Selamat datang, <?php echo htmlspecialchars($current_username); ?> | <a href="logout.php">Logout</a></div>
</header>
<div class="admin-container">
<nav class="admin-nav">
<ul>
<li><a href="index.php">Dashboard</a></li>
<li><a href="manage_posts.php">Kelola Artikel</a></li>
<li><a href="manage_categories.php">Kelola Kategori</a></li>
<?php if ($current_user_role === 'admin'): ?>
<li><a href="manage_users.php" class="active">Kelola Pengguna</a></li>
<?php endif; ?>
<li><a href="../index.php" target="_blank">Lihat Situs</a></li>
</ul>
</nav>
<h2><?php echo htmlspecialchars($page_title_admin); ?></h2>
<?php display_flash_message('user_action'); ?>
<?php display_flash_message('auth_error'); // Untuk pesan error dari halaman ini atau auth_check ?>
<a href="add_user.php" class="btn btn-add">Tambah Pengguna Baru</a>
<table class="admin-table">
<thead>
<tr>
<th>ID</th>
<th>Username</th>
<th>Email</th>
<th>Peran</th>
<th>Tanggal Dibuat</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
<?php
$sql = "SELECT id, username, email, role, created_at FROM users ORDER BY created_at DESC";
$result = mysqli_query($conn, $sql);
if (mysqli_num_rows($result) > 0) {
while ($user = mysqli_fetch_assoc($result)) {
echo "<tr>";
echo "<td>" . $user['id'] . "</td>";
echo "<td>" . htmlspecialchars($user['username']) . "</td>";
echo "<td>" . htmlspecialchars($user['email']) . "</td>";
echo "<td>" . ucfirst(htmlspecialchars($user['role'])) . "</td>";
echo "<td>" . date('d M Y, H:i', strtotime($user['created_at'])) . "</td>";
echo "<td>";
echo "<a href='edit_user.php?id=" . $user['id'] . "' class='btn btn-edit'>Edit</a> ";
// Admin tidak boleh menghapus dirinya sendiri
// Dan mungkin ada batasan lain (misal, hanya ada 1 admin, tidak boleh dihapus)
if ($user['id'] != $current_user_id && $user['username'] !== 'admin' /* Tambahan pencegahan super admin */ ) {
echo "<a href='delete_user.php?id=" . $user['id'] . "' class='btn btn-delete' onclick='return confirm(\"Apakah Anda yakin ingin menghapus pengguna ini?\");'>Hapus</a>";
} else {
echo "<span class='btn btn-disabled'>Hapus</span>"; // Tombol disable
}
echo "</td>";
echo "</tr>";
}
} else {
echo "<tr><td colspan='6' style='text-align:center;'>Belum ada pengguna terdaftar.</td></tr>";
}
?>
</tbody>
</table>
</div>
<?php if(isset($conn)) { mysqli_close($conn); } ?>
</body>
</html>
Perubahan Penting:
- Pengecekan Peran di Awal: Jika pengguna bukan
admin
, mereka akan diarahkan. - Tombol Hapus Bersyarat:
- Pengguna tidak bisa menghapus akunnya sendiri (
$user['id'] != $current_user_id
). - Sebagai contoh tambahan, kita mencegah penghapusan user dengan username
admin
(bisa dianggap sebagai super admin). Anda bisa menyesuaikan logika ini.
- Pengguna tidak bisa menghapus akunnya sendiri (
- Menambahkan kelas
btn-disabled
jika tombol hapus tidak aktif (perlu styling di CSS).
CSS untuk Tombol Disabled (css/admin_style.css
):
/* css/admin_style.css (Tambahkan) */
.btn-disabled {
background-color: #adb5bd;
color: #fff;
cursor: not-allowed;
opacity: 0.65;
}
Langkah 3: Halaman Tambah Pengguna (admin/add_user.php
)
Formulir untuk membuat akun pengguna baru (hanya bisa diakses oleh admin
).
Buat file admin/add_user.php
:
<?php
// admin/add_user.php
require_once 'auth_check.php';
require_once '../includes/db_connect.php';
require_once '../includes/functions.php';
if ($current_user_role !== 'admin') {
set_flash_message('auth_error', "Anda tidak memiliki izin untuk mengakses halaman ini.", "error");
header("Location: index.php");
exit;
}
$page_title_admin = "Tambah Pengguna Baru";
$username = ''; $email = ''; $role = 'editor'; // Default role
$errors = [];
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$username = trim($_POST['username']);
$email = trim($_POST['email']);
$password = trim($_POST['password']);
$password_confirm = trim($_POST['password_confirm']);
$role = isset($_POST['role']) ? trim($_POST['role']) : 'editor';
// Validasi
if (empty($username)) $errors[] = "Username tidak boleh kosong.";
if (empty($email)) $errors[] = "Email tidak boleh kosong.";
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) $errors[] = "Format email tidak valid.";
if (empty($password)) $errors[] = "Password tidak boleh kosong.";
if (strlen($password) < 6) $errors[] = "Password minimal 6 karakter."; // Contoh panjang minimal
if ($password !== $password_confirm) $errors[] = "Konfirmasi password tidak cocok.";
if (!in_array($role, ['admin', 'editor'])) $errors[] = "Peran tidak valid.";
// Cek keunikan username
if (empty($errors)) {
$sql_check_user = "SELECT id FROM users WHERE username = ? OR email = ?";
if($stmt_check = mysqli_prepare($conn, $sql_check_user)){
mysqli_stmt_bind_param($stmt_check, "ss", $username, $email);
mysqli_stmt_execute($stmt_check);
mysqli_stmt_store_result($stmt_check);
if(mysqli_stmt_num_rows($stmt_check) > 0){
$errors[] = "Username atau Email sudah terdaftar.";
}
mysqli_stmt_close($stmt_check);
}
}
if (empty($errors)) {
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$sql_insert = "INSERT INTO users (username, email, password, role) VALUES (?, ?, ?, ?)";
if ($stmt = mysqli_prepare($conn, $sql_insert)) {
mysqli_stmt_bind_param($stmt, "ssss", $username, $email, $hashed_password, $role);
if (mysqli_stmt_execute($stmt)) {
set_flash_message('user_action', "Pengguna \"" . htmlspecialchars($username) . "\" berhasil ditambahkan!", "success");
header("Location: manage_users.php");
exit;
} else {
$errors[] = "Gagal menyimpan pengguna: " . mysqli_stmt_error($stmt);
}
mysqli_stmt_close($stmt);
} else {
$errors[] = "Gagal mempersiapkan statement: " . mysqli_error($conn);
}
}
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($page_title_admin); ?> - CMS Admin</title>
<link rel="stylesheet" href="../css/admin_style.css">
</head>
<body>
<header class="admin-header">
<h1>Admin Panel</h1>
<div>Selamat datang, <?php echo htmlspecialchars($current_username); ?> | <a href="logout.php">Logout</a></div>
</header>
<div class="admin-container">
<nav class="admin-nav">
<ul>
<li><a href="index.php">Dashboard</a></li>
<li><a href="manage_posts.php">Kelola Artikel</a></li>
<li><a href="manage_categories.php">Kelola Kategori</a></li>
<?php if ($current_user_role === 'admin'): ?>
<li><a href="manage_users.php">Kelola Pengguna</a></li>
<?php endif; ?>
</ul>
</nav>
<h2><?php echo htmlspecialchars($page_title_admin); ?></h2>
<?php if (!empty($errors)): ?>
<div class="flash-message error">
<strong>Error!</strong>
<ul><?php foreach ($errors as $error) echo "<li>" . htmlspecialchars($error) . "</li>"; ?></ul>
</div>
<?php endif; ?>
<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" method="post" class="admin-form">
<div class="form-group">
<label for="username">Username:</label>
<input type="text" name="username" id="username" value="<?php echo htmlspecialchars($username); ?>" required>
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" name="email" id="email" value="<?php echo htmlspecialchars($email); ?>" required>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" name="password" id="password" required>
<small>Minimal 6 karakter.</small>
</div>
<div class="form-group">
<label for="password_confirm">Konfirmasi Password:</label>
<input type="password" name="password_confirm" id="password_confirm" required>
</div>
<div class="form-group">
<label for="role">Peran:</label>
<select name="role" id="role">
<option value="editor" <?php echo ($role === 'editor') ? 'selected' : ''; ?>>Editor</option>
<option value="admin" <?php echo ($role === 'admin') ? 'selected' : ''; ?>>Admin</option>
</select>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-submit">Simpan Pengguna</button>
<a href="manage_users.php" class="btn btn-cancel">Batal</a>
</div>
</form>
</div>
<?php if(isset($conn)) { mysqli_close($conn); } ?>
</body>
</html>
Perubahan Penting:
- Validasi input lebih banyak (email, konfirmasi password, panjang password).
filter_var($email, FILTER_VALIDATE_EMAIL)
untuk validasi format email.- Pengecekan keunikan username DAN email.
- Password di-hash menggunakan
password_hash()
.
Langkah 4: Halaman Edit Pengguna (admin/edit_user.php
)
Formulir untuk memodifikasi data pengguna yang ada (hanya bisa diakses oleh admin
).
Buat file admin/edit_user.php
:
<?php
// admin/edit_user.php
require_once 'auth_check.php';
require_once '../includes/db_connect.php';
require_once '../includes/functions.php';
if ($current_user_role !== 'admin') {
set_flash_message('auth_error', "Anda tidak memiliki izin untuk mengakses halaman ini.", "error");
header("Location: index.php");
exit;
}
$page_title_admin = "Edit Pengguna";
$user_id_to_edit = isset($_GET['id']) ? (int)$_GET['id'] : 0;
$user_data_original = null;
$username = ''; $email = ''; $role = '';
$errors = [];
if ($user_id_to_edit > 0) {
$sql_select = "SELECT username, email, role FROM users WHERE id = ?";
if ($stmt_select = mysqli_prepare($conn, $sql_select)) {
mysqli_stmt_bind_param($stmt_select, "i", $user_id_to_edit);
mysqli_stmt_execute($stmt_select);
$result_select = mysqli_stmt_get_result($stmt_select);
if ($user = mysqli_fetch_assoc($result_select)) {
$user_data_original = $user;
$username = $user['username'];
$email = $user['email'];
$role = $user['role'];
} else {
set_flash_message('user_action', "Pengguna dengan ID $user_id_to_edit tidak ditemukan.", "error");
header("Location: manage_users.php");
exit;
}
mysqli_stmt_close($stmt_select);
} else { die("Error SELECT: " . mysqli_error($conn)); }
} else {
set_flash_message('user_action', "ID Pengguna tidak valid.", "error");
header("Location: manage_users.php");
exit;
}
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$username_new = trim($_POST['username']);
$email_new = trim($_POST['email']);
$password_new = trim($_POST['password']); // Password baru (opsional)
$password_new_confirm = trim($_POST['password_confirm']); // Konfirmasi password baru
$role_new = isset($_POST['role']) ? trim($_POST['role']) : $role;
if (empty($username_new)) $errors[] = "Username tidak boleh kosong.";
if (empty($email_new)) $errors[] = "Email tidak boleh kosong.";
if (!filter_var($email_new, FILTER_VALIDATE_EMAIL)) $errors[] = "Format email tidak valid.";
if (!in_array($role_new, ['admin', 'editor'])) $errors[] = "Peran tidak valid.";
// Validasi password hanya jika diisi
if (!empty($password_new)) {
if (strlen($password_new) < 6) $errors[] = "Password baru minimal 6 karakter.";
if ($password_new !== $password_new_confirm) $errors[] = "Konfirmasi password baru tidak cocok.";
}
// Cek keunikan username dan email (jika berubah)
if (empty($errors)) {
$check_query_parts = [];
$check_params = [];
$check_types = "";
if ($username_new !== $user_data_original['username']) {
$check_query_parts[] = "username = ?";
$check_params[] = $username_new;
$check_types .= "s";
}
if ($email_new !== $user_data_original['email']) {
$check_query_parts[] = "email = ?";
$check_params[] = $email_new;
$check_types .= "s";
}
if (!empty($check_query_parts)) {
$sql_check_user = "SELECT id FROM users WHERE (" . implode(' OR ', $check_query_parts) . ") AND id != ?";
$check_params[] = $user_id_to_edit; // Tambahkan ID user saat ini untuk eksklusi
$check_types .= "i";
if($stmt_check = mysqli_prepare($conn, $sql_check_user)){
mysqli_stmt_bind_param($stmt_check, $check_types, ...$check_params);
mysqli_stmt_execute($stmt_check);
mysqli_stmt_store_result($stmt_check);
if(mysqli_stmt_num_rows($stmt_check) > 0){
if ($username_new !== $user_data_original['username'] && in_array("username = ?", $check_query_parts)) $errors[] = "Username sudah terdaftar.";
if ($email_new !== $user_data_original['email'] && in_array("email = ?", $check_query_parts)) $errors[] = "Email sudah terdaftar.";
}
mysqli_stmt_close($stmt_check);
}
}
}
if (empty($errors)) {
if (!empty($password_new)) {
$hashed_password_new = password_hash($password_new, PASSWORD_DEFAULT);
$sql_update = "UPDATE users SET username = ?, email = ?, password = ?, role = ? WHERE id = ?";
$types_update = "ssssi";
$params_update = [$username_new, $email_new, $hashed_password_new, $role_new, $user_id_to_edit];
} else {
// Jika password tidak diubah
$sql_update = "UPDATE users SET username = ?, email = ?, role = ? WHERE id = ?";
$types_update = "sssi";
$params_update = [$username_new, $email_new, $role_new, $user_id_to_edit];
}
if ($stmt_update = mysqli_prepare($conn, $sql_update)) {
mysqli_stmt_bind_param($stmt_update, $types_update, ...$params_update);
if (mysqli_stmt_execute($stmt_update)) {
set_flash_message('user_action', "Pengguna \"" . htmlspecialchars($username_new) . "\" berhasil diperbarui!", "success");
// Jika admin mengedit dirinya sendiri dan mengubah peran, mungkin perlu logika logout/re-login atau update sesi
if ($user_id_to_edit == $_SESSION['user_id'] && $_SESSION['role'] !== $role_new) {
$_SESSION['role'] = $role_new; // Update peran di sesi
}
if ($user_id_to_edit == $_SESSION['user_id'] && $_SESSION['username'] !== $username_new) {
$_SESSION['username'] = $username_new; // Update username di sesi
}
header("Location: manage_users.php");
exit;
} else {
$errors[] = "Gagal memperbarui pengguna: " . mysqli_stmt_error($stmt_update);
}
mysqli_stmt_close($stmt_update);
} else {
$errors[] = "Gagal mempersiapkan statement UPDATE: " . mysqli_error($conn);
}
}
// Update variabel untuk sticky form jika ada error
$username = $username_new;
$email = $email_new;
$role = $role_new;
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($page_title_admin); ?> - CMS Admin</title>
<link rel="stylesheet" href="../css/admin_style.css">
</head>
<body>
<header class="admin-header">
<h1>Admin Panel</h1>
<div>Selamat datang, <?php echo htmlspecialchars($current_username); ?> | <a href="logout.php">Logout</a></div>
</header>
<div class="admin-container">
<nav class="admin-nav">
<ul>
<li><a href="index.php">Dashboard</a></li>
<li><a href="manage_posts.php">Kelola Artikel</a></li>
<li><a href="manage_categories.php">Kelola Kategori</a></li>
<?php if ($current_user_role === 'admin'): ?>
<li><a href="manage_users.php">Kelola Pengguna</a></li>
<?php endif; ?>
</ul>
</nav>
<h2><?php echo htmlspecialchars($page_title_admin); ?>: "<?php echo htmlspecialchars($user_data_original['username']); ?>"</h2>
<?php if (!empty($errors)): ?>
<div class="flash-message error">
<strong>Error!</strong>
<ul><?php foreach ($errors as $error) echo "<li>" . htmlspecialchars($error) . "</li>"; ?></ul>
</div>
<?php endif; ?>
<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>?id=<?php echo $user_id_to_edit; ?>" method="post" class="admin-form">
<div class="form-group">
<label for="username">Username:</label>
<input type="text" name="username" id="username" value="<?php echo htmlspecialchars($username); ?>" required>
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" name="email" id="email" value="<?php echo htmlspecialchars($email); ?>" required>
</div>
<div class="form-group">
<label for="password">Password Baru (Kosongkan jika tidak ingin diubah):</label>
<input type="password" name="password" id="password">
<small>Minimal 6 karakter jika diisi.</small>
</div>
<div class="form-group">
<label for="password_confirm">Konfirmasi Password Baru:</label>
<input type="password" name="password_confirm" id="password_confirm">
</div>
<div class="form-group">
<label for="role">Peran:</label>
<select name="role" id="role" <?php echo ($user_id_to_edit == $current_user_id && $username == 'admin') ? 'disabled' : ''; ?>>
<!-- Admin super (misal, user 'admin' atau user dengan ID tertentu) tidak bisa mengubah perannya sendiri menjadi editor -->
<option value="editor" <?php echo ($role === 'editor') ? 'selected' : ''; ?>>Editor</option>
<option value="admin" <?php echo ($role === 'admin') ? 'selected' : ''; ?>>Admin</option>
</select>
<?php if ($user_id_to_edit == $current_user_id && $username == 'admin'): ?>
<small>Peran untuk super admin tidak dapat diubah.</small>
<?php endif; ?>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-submit">Simpan Perubahan</button>
<a href="manage_users.php" class="btn btn-cancel">Batal</a>
</div>
</form>
</div>
<?php if(isset($conn)) { mysqli_close($conn); } ?>
</body>
</html>
Perubahan Penting:
- Password Opsional: Pengguna bisa mengedit profil tanpa harus mengubah password. Password hanya diupdate jika field password baru diisi.
- Pengecekan Keunikan Username/Email yang Diubah: Hanya cek jika nilainya berbeda dari yang lama.
- Batasan Edit Peran: Contoh sederhana untuk mencegah admin utama mengubah perannya sendiri menjadi editor atau pengguna lain mengubah peran admin utama. Logika ini bisa lebih kompleks.
- Update Sesi: Jika admin mengedit profilnya sendiri dan mengubah peran atau username, sesi perlu diupdate agar informasi yang ditampilkan di header admin (misal,
Selamat datang, AdminBaru (Admin)
) menjadi akurat.
Langkah 5: Skrip Hapus Pengguna (admin/delete_user.php
)
Logika untuk menghapus akun pengguna (hanya bisa diakses oleh admin
).
Buat file admin/delete_user.php
:
<?php
// admin/delete_user.php
require_once 'auth_check.php';
require_once '../includes/db_connect.php';
require_once '../includes/functions.php';
if ($current_user_role !== 'admin') {
set_flash_message('auth_error', "Anda tidak memiliki izin untuk melakukan tindakan ini.", "error");
header("Location: index.php");
exit;
}
$user_id_to_delete = isset($_GET['id']) ? (int)$_GET['id'] : 0;
if ($user_id_to_delete > 0) {
// Admin tidak boleh menghapus dirinya sendiri
if ($user_id_to_delete == $current_user_id) {
set_flash_message('user_action', "Anda tidak dapat menghapus akun Anda sendiri.", "error");
header("Location: manage_users.php");
exit;
}
// (Opsional) Ambil username untuk pesan flash dan tambahan pencegahan
$username_to_delete = "Pengguna";
$sql_get_user = "SELECT username FROM users WHERE id = ?";
if($stmt_get_user = mysqli_prepare($conn, $sql_get_user)){
mysqli_stmt_bind_param($stmt_get_user, "i", $user_id_to_delete);
mysqli_stmt_execute($stmt_get_user);
$result_user = mysqli_stmt_get_result($stmt_get_user);
if($row_user = mysqli_fetch_assoc($result_user)){
$username_to_delete = $row_user['username'];
// Pencegahan tambahan untuk super admin (jika ada)
if ($username_to_delete === 'admin' && $user_id_to_delete != $current_user_id) { // Jika bukan menghapus diri sendiri tapi mencoba hapus 'admin'
set_flash_message('user_action', "Pengguna 'admin' utama tidak dapat dihapus.", "error");
header("Location: manage_users.php");
exit;
}
} else {
set_flash_message('user_action', "Gagal menghapus: Pengguna tidak ditemukan.", "error");
header("Location: manage_users.php");
exit;
}
mysqli_stmt_close($stmt_get_user);
}
// Pertimbangkan apa yang terjadi pada konten yang dibuat oleh pengguna ini.
// Opsi:
// 1. Hapus juga semua artikel yang dibuatnya (cascade delete, perlu setup di DB atau query tambahan).
// 2. Atur author_id artikel menjadi NULL atau ke user default 'anonymous'.
// Untuk tutorial ini, kita tidak akan menangani konten terkait secara otomatis. Ini bisa jadi fitur lanjutan.
// Kita hanya hapus user. Jika ada foreign key `author_id` di `posts` dengan `ON DELETE SET NULL`,
// maka artikel akan kehilangan penulisnya. Jika `ON DELETE RESTRICT`, penghapusan akan gagal jika user punya post.
// Kita asumsikan saat ini tidak ada constraint yang mencegah, atau kita akan menambahkan `ON DELETE SET NULL` pada `author_id` di tabel `posts`.
// Jika Anda ingin mengatur ulang `author_id` pada postingan pengguna yang dihapus:
// $sql_reassign_posts = "UPDATE posts SET author_id = NULL WHERE author_id = ?";
// (Jalankan ini sebelum menghapus user)
$sql_delete = "DELETE FROM users WHERE id = ?";
if ($stmt_delete = mysqli_prepare($conn, $sql_delete)) {
mysqli_stmt_bind_param($stmt_delete, "i", $user_id_to_delete);
if (mysqli_stmt_execute($stmt_delete)) {
if (mysqli_stmt_affected_rows($stmt_delete) > 0) {
set_flash_message('user_action', "Pengguna \"" . htmlspecialchars($username_to_delete) . "\" berhasil dihapus.", "success");
} else {
set_flash_message('user_action', "Gagal menghapus: Pengguna tidak ditemukan atau sudah dihapus.", "error");
}
} else {
set_flash_message('user_action', "Gagal menghapus pengguna: " . mysqli_stmt_error($stmt_delete), "error");
}
mysqli_stmt_close($stmt_delete);
} else {
set_flash_message('user_action', "Gagal mempersiapkan statement DELETE: " . mysqli_error($conn), "error");
}
} else {
set_flash_message('user_action', "ID Pengguna tidak valid untuk dihapus.", "error");
}
header("Location: manage_users.php");
exit;
?>
Perubahan Penting:
- Pencegahan Menghapus Diri Sendiri: Sangat penting.
- Pencegahan Menghapus Super Admin (Contoh): Logika tambahan untuk melindungi akun admin utama.
- Konten Terkait: Komentar penting tentang apa yang terjadi pada artikel yang dibuat oleh pengguna yang dihapus. Penanganan ini (misalnya, mengatur ulang
author_id
) tergantung pada kebijakan aplikasi Anda dan bagaimana foreign key diatur. UntukON DELETE SET NULL
padaposts.author_id
(jika Anda menambahkannya), ini akan otomatis.
Modifikasi Tabel posts
untuk author_id
(Jika belum di Part 1):
Jika Anda belum memiliki author_id
di tabel posts
atau belum mengatur foreign key-nya, Anda bisa melakukannya sekarang:
ALTER TABLE `posts`
ADD COLUMN `author_id` INT(11) UNSIGNED NULL AFTER `image_path`;
-- Tambahkan foreign key constraint JIKA tabel users sudah ada
-- ALTER TABLE `posts`
-- ADD CONSTRAINT `fk_post_author`
-- FOREIGN KEY (`author_id`)
-- REFERENCES `users`(`id`)
-- ON DELETE SET NULL ON UPDATE CASCADE;
Pastikan Anda menambahkan foreign key fk_post_author
setelah tabel users
dibuat. Jika Anda membuatnya sekarang, pastikan ON DELETE SET NULL
digunakan agar saat user dihapus, artikelnya tidak error.
Langkah 6: Mengintegrasikan author_id
ke Manajemen Artikel
Sekarang setelah kita memiliki pengguna, kita perlu memastikan author_id
disimpan dengan benar saat artikel dibuat/diedit.
1. Modifikasi admin/add_post.php
:
Kita sudah menambahkan $author_id = $_SESSION['user_id'];
dan menyimpannya saat INSERT. Ini sudah benar.
2. Modifikasi admin/edit_post.php
:
Saat mengedit, author_id
biasanya tidak diubah (artikel tetap milik penulis aslinya). Jadi, tidak perlu field untuk mengubah author_id
di form edit, kecuali jika admin memiliki hak untuk itu. Untuk sekarang, kita biarkan author_id
tidak bisa diubah di form edit.
3. Menampilkan Nama Penulis di Frontend:
Kita perlu melakukan JOIN tabel posts
dengan users
untuk mendapatkan nama penulis.
Modifikasi index.php
(daftar artikel):
// index.php (bagian query SQL)
$sql = "SELECT p.id, p.title, p.content, p.slug, p.created_at, p.image_path,
c.name as category_name, c.slug as category_slug,
u.username as author_name
FROM posts p
LEFT JOIN categories c ON p.category_id = c.id
LEFT JOIN users u ON p.author_id = u.id
WHERE p.status = 'published'
ORDER BY p.created_at DESC";
// ... (lanjutkan dengan mysqli_query dan loop) ...
// Di dalam loop while($post = mysqli_fetch_assoc($result)):
// Modifikasi <p class="post-meta">
?>
<p class="post-meta">
<span class="post-date">Dipublikasikan pada: <?php echo date('j F Y', strtotime($post['created_at'])); ?></span>
<?php if (!empty($post['author_name'])): ?>
| <span class="post-author">Oleh: <?php echo htmlspecialchars($post['author_name']); ?></span>
<?php endif; ?>
<?php if (!empty($post['category_name'])): ?>
| <span class="post-category">Kategori: <a href="<?php echo site_url('category.php?slug=' . htmlspecialchars($post['category_slug'])); ?>"><?php echo htmlspecialchars($post['category_name']); ?></a></span>
<?php endif; ?>
</p>
<?php
// ...
Modifikasi single.php
(detail artikel):
// single.php (bagian query SQL)
$sql = "SELECT p.id, p.title, p.content, p.slug, p.created_at, p.updated_at, p.image_path,
c.name as category_name, c.slug as category_slug,
u.username as author_name
FROM posts p
LEFT JOIN categories c ON p.category_id = c.id
LEFT JOIN users u ON p.author_id = u.id
WHERE p.slug = '$slug' AND p.status = 'published'";
// ... (lanjutkan dengan mysqli_query dan fetch) ...
// Di dalam <p class="post-meta-single">:
// Modifikasi atau tambahkan:
?>
<p class="post-meta-single">
<span class="post-date-single">Dipublikasikan: <?php echo date('l, j F Y H:i', strtotime($post['created_at'])); ?></span>
<?php if ($post['created_at'] != $post['updated_at']): ?>
<br><span class="post-update-date-single">Diperbarui: <?php echo date('l, j F Y H:i', strtotime($post['updated_at'])); ?></span>
<?php endif; ?>
<?php if (!empty($post['author_name'])): ?>
<br><span class="post-author-single">Oleh: <?php echo htmlspecialchars($post['author_name']); ?></span>
<?php endif; ?>
<?php if (!empty($post['category_name'])): ?>
<br><span class="post-category-single">Kategori: <a href="<?php echo site_url('category.php?slug=' . htmlspecialchars($post['category_slug'])); ?>"><?php echo htmlspecialchars($post['category_name']); ?></a></span>
<?php endif; ?>
</p>
<?php
// ...
Menguji Fitur Manajemen Pengguna dan Peran
- Login sebagai Admin:
- Buka
admin/manage_users.php
. Anda seharusnya bisa melihat daftar pengguna. - Coba tambah pengguna baru dengan peran “Editor”.
- Coba edit pengguna yang baru dibuat (ubah username, email, atau peran).
- Coba hapus pengguna “Editor” tersebut.
- Pastikan Anda tidak bisa menghapus akun admin Anda sendiri atau akun “admin” utama.
- Buka
- Login sebagai Editor (jika sudah dibuat):
- Logout dari akun admin.
- Login dengan akun “Editor” yang baru.
- Periksa navigasi admin. Link “Kelola Pengguna” seharusnya tidak muncul.
- Coba akses
admin/manage_users.php
secara langsung melalui URL. Anda seharusnya diarahkan atau melihat pesan error “tidak ada izin”. - Pastikan editor bisa mengakses dan mengelola artikel serta kategori.
- Frontend:
- Periksa halaman
index.php
dansingle.php
. Nama penulis artikel seharusnya muncul jikaauthor_id
sudah terisi dengan benar di tabelposts
.
- Periksa halaman
Refleksi Part 5 dan Apa Selanjutnya?
Selamat! Anda telah mencapai tonggak penting dengan mengimplementasikan manajemen pengguna dan sistem peran. CMS Anda sekarang jauh lebih aman dan siap untuk kolaborasi atau pengelolaan dengan tingkat akses yang berbeda.
Poin Kunci yang Telah Dicapai:
- CRUD penuh untuk manajemen pengguna di area admin.
- Implementasi sistem peran (
admin
,editor
). - Pembatasan akses fitur berdasarkan peran pengguna.
- Pencegahan tindakan kritis (menghapus diri sendiri, menghapus admin utama).
- Integrasi
author_id
pada artikel dan menampilkannya di frontend.
Apa Selanjutnya? Seri dasar CMS kita hampir lengkap! Beberapa area yang bisa menjadi fokus untuk bagian selanjutnya atau sebagai pengembangan mandiri:
- Part 6 (Opsional): Fitur Tambahan & Penyempurnaan
- Upload Gambar Unggulan untuk Artikel: Menggunakan
$_FILES
PHP. - Editor WYSIWYG untuk Konten Artikel: Integrasi TinyMCE atau sejenisnya.
- Sistem Komentar Sederhana: Untuk interaksi pengunjung.
- Pagination: Untuk halaman daftar artikel dan manajemen di admin.
- Pencarian: Fitur pencarian artikel di frontend.
- Pengaturan Situs Dasar: Menyimpan judul situs, deskripsi, dll. di database.
- Keamanan Lanjutan & Praktik Terbaik: XSS, CSRF, validasi lebih ketat.
- Upload Gambar Unggulan untuk Artikel: Menggunakan
Pilih topik yang paling menarik atau paling relevan untuk kebutuhan Anda. Untuk seri tutorial ini, Upload Gambar Unggulan dan Pagination adalah tambahan yang sangat umum dan berguna.
Teruslah belajar dan jangan takut untuk memodifikasi kode ini lebih lanjut!