بِسْمِ اللَّهِ Mudah-mudahan tutorial Myth:Auth Bagian 2 Manajemen User (Aktivasi & Update Password) ini bermanfaat
Library Myth:Auth menyediakan layanan user diantaranya registrasi, aktivasi, reset password, grup berikut pengaturan role, dan lain-lain. Dimana layanan ini semua sudah disediakan secara interaktif tanpa harus ada campur tangan administrator website/aplikasi. Tapi dengan library ini memungkinkan untuk layanan tersebut dilakukan secara manual dalam hal ini administrator, untuk aktivasi atau reset password. Tentunya dengan tambahan coding didalamnya
Nah pada tutorial kali saya akan membagi sedikit pengetahuan saya untuk otak-atik seputar Manajemen User pada library Myth:Auth. Di library Myth:Auth sendiri sudah memahami permasalahan tersebut, makanya library Myth:Auth juga menyediakan fungsi-fungsi atau komponen yang bisa diolah/dimanfaatkan lagi oleh developer Codeigniter.
Persiapan Myth:Auth Bagian 2 Manajemen User (Aktivasi & Update Password)
Untuk bisa melanjutkan pastikan sudah ada ini:
Editor yang digunakan adalah Visual Studio Code
Project Codeigniter dengan library Myth:Auth didalamnya, tutorialnya bisa dilihat Codeigniter 4 Login multiuser dengan library Myth:Auth
Bagi yang sudah Codeigniter 4 – Merubah tampilan Myth:Auth
Sudah mengerjakan Codeigniter 4 – Myth:Auth Bagian 1 groups dan permissions
Pertama
Atur sehingga Daftar/List User hanya bisa diakses oleh administrator. Caranya dengan menambahkan pembatasan atau restricting berdasarkan alamat URL seperti ini pada file \app\Config\Routes.php.
$routes->get('users/index', 'Users::index', ['filter' => 'role:administrator']);
Konfigurasi selanjutnya pada file \vendor\myth\auth\src\Filters\RoleFilter.php, disini akan mengarahkan ke halaman error “access_denied.php“. Halaman tersebut akan dibuat nanti pada bagian View. Berikut scriptnya
$redirectURL = session('redirect_url') ?? '/home/access_denied';
return redirect()->to($redirectURL);
//throw new PermissionException(lang('Auth.notEnoughPrivilege'));
Kedua
Buat Controller \app\Controllers\Users.php kemudian isi dengan script berikut:
<?php
namespace App\Controllers;
use \Myth\Auth\Models\UserModel;
use \Myth\Auth\Password;
class Users extends BaseController
{
public function index()
{
$userModel = new UserModel();
$data['users'] = $userModel->findAll();
$data['title'] = 'Users';
return view('users/index', $data);
}
public function activate()
{
$userModel = new UserModel();
$data = [
'activate_hash' => null,
'active' => $this->request->getVar('active') == '0' || '' ? '1' : '0',
];
$userModel->update($this->request->getVar('id'), $data);
return redirect()->to(base_url('/users/index'));
}
public function changePassword($id = null)
{
if ($id==null)
{
return redirect()->to(base_url('/users/index'));
} else
{
$data = [
'id' => $id,
'title' => 'Update Password',
];
return view('users/set_password', $data);
}
}
public function setPassword()
{
$id = $this->request->getVar('id');
$rules = [
'password' => 'required|strong_password',
'pass_confirm' => 'required|matches[password]',
];
if (! $this->validate($rules))
{
$data = [
'id' => $id,
'title' => 'Update Password',
'validation' => $this->validator,
];
return view('users/set_password', $data);
}
else
{
$userModel = new UserModel();
$data = [
'password_hash' => Password::hash($this->request->getVar('password')),
'reset_hash' => null,
'reset_at' => null,
'reset_expires' => null,
];
$userModel->update($this->request->getVar('id'), $data);
return redirect()->to(base_url('/users/index'));
}
}
}
Pada Controller User saya memanfaatkan komponen yang sudah tersedia pada library Myth:Auth yaitu UserModel dan Password.
- UserModel untuk pengolahan data User yaitu aktivasi dan update/ubah password
- Password untuk mendapatkan enkripsi hash
Tambahkan helper ‘auth’ pada file \app\Controllers\BaseController.php, sehingga seperti ini
protected $helpers = ['form','auth'];
Helper ini digunakan untuk mengecek user login atau tidak, juga bisa mendapatkan informasi tentang user yang sedang login, grup user yang sedang login, sampai dengan hak akses/permission dari user tersebut.
Ketiga
Update template index yang ada di \app\Views\template\index.php dengan script ini, disini untuk menu atau yang lain sesuai kan dengan kebutuhan.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title><?= $title; ?></title>
<!-- Custom fonts for this template-->
<link href="<?= base_url() ?>/assets/vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i"
rel="stylesheet">
<!-- Custom styles for this template-->
<link href="<?= base_url() ?>/assets/css/sb-admin-2.min.css" rel="stylesheet">
</head>
<body id="page-top">
<!-- Page Wrapper -->
<div id="wrapper">
<!-- Sidebar -->
<ul class="navbar-nav bg-gradient-primary sidebar sidebar-dark accordion" id="accordionSidebar">
<!-- Sidebar - Brand -->
<a class="sidebar-brand d-flex align-items-center justify-content-center" href="<?= base_url() ?>">
<div class="sidebar-brand-icon rotate-n-15">
<i class="fas fa-laugh-wink"></i>
</div>
<div class="sidebar-brand-text mx-3">Website <sup>CI</sup></div>
</a>
<!-- Divider -->
<hr class="sidebar-divider my-0">
<!-- Nav Item - Dashboard -->
<li class="nav-item">
<a class="nav-link" href="<?= base_url() ?>">
<i class="fas fa-fw fa-tachometer-alt"></i>
<span>Dasbor</span></a>
</li>
<!-- Divider -->
<hr class="sidebar-divider">
<!-- Heading -->
<div class="sidebar-heading">
Master Data
</div>
<!-- Nav Item - Pages Collapse Menu -->
<li class="nav-item">
<a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#collapse1"
aria-expanded="true" aria-controls="collapse1">
<i class="fas fa-fw fa-newspaper"></i>
<span>Berita</span>
</a>
<div id="collapse1" class="collapse" aria-labelledby="heading1" data-parent="#accordionSidebar">
<div class="bg-white py-2 collapse-inner rounded">
<a class="collapse-item" href="<?= base_url() ?>/news/index">Daftar/List</a>
<a class="collapse-item" href="<?= base_url() ?>/news/add">Tambah</a>
</div>
</div>
</li>
<li class="nav-item">
<a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#collapse2"
aria-expanded="true" aria-controls="collapse2">
<i class="fas fa-fw fa-newspaper"></i>
<span>Users</span>
</a>
<div id="collapse2" class="collapse" aria-labelledby="heading2" data-parent="#accordionSidebar">
<div class="bg-white py-2 collapse-inner rounded">
<a class="collapse-item" href="<?= base_url() ?>/users/index">Daftar/List</a>
</div>
</div>
</li>
<!-- Divider -->
<hr class="sidebar-divider d-none d-md-block">
<!-- Sidebar Toggler (Sidebar) -->
<div class="text-center d-none d-md-inline">
<button class="rounded-circle border-0" id="sidebarToggle"></button>
</div>
</ul>
<!-- End of Sidebar -->
<!-- Content Wrapper -->
<div id="content-wrapper" class="d-flex flex-column">
<!-- Main Content -->
<div id="content">
<!-- Topbar -->
<nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top shadow">
<!-- Sidebar Toggle (Topbar) -->
<button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3">
<i class="fa fa-bars"></i>
</button>
<!-- Topbar Search -->
<form
class="d-none d-sm-inline-block form-inline mr-auto ml-md-3 my-2 my-md-0 mw-100 navbar-search">
<div class="input-group">
<input type="text" class="form-control bg-light border-0 small" placeholder="Search for..."
aria-label="Search" aria-describedby="basic-addon2">
<div class="input-group-append">
<button class="btn btn-primary" type="button">
<i class="fas fa-search fa-sm"></i>
</button>
</div>
</div>
</form>
<!-- Topbar Navbar -->
<ul class="navbar-nav ml-auto">
<!-- Nav Item - Search Dropdown (Visible Only XS) -->
<li class="nav-item dropdown no-arrow d-sm-none">
<a class="nav-link dropdown-toggle" href="#" id="searchDropdown" role="button"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-search fa-fw"></i>
</a>
<!-- Dropdown - Messages -->
<div class="dropdown-menu dropdown-menu-right p-3 shadow animated--grow-in"
aria-labelledby="searchDropdown">
<form class="form-inline mr-auto w-100 navbar-search">
<div class="input-group">
<input type="text" class="form-control bg-light border-0 small"
placeholder="Search for..." aria-label="Search"
aria-describedby="basic-addon2">
<div class="input-group-append">
<button class="btn btn-primary" type="button">
<i class="fas fa-search fa-sm"></i>
</button>
</div>
</div>
</form>
</div>
</li>
<div class="topbar-divider d-none d-sm-block"></div>
<?php if (isset(user()->username)) { ?>
<!-- Nav Item - User Information -->
<li class="nav-item dropdown no-arrow">
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="mr-2 d-none d-lg-inline text-gray-600 small"><?= user()->username; ?></span>
<img class="img-profile rounded-circle"
src="<?= base_url(); ?>/assets/img/undraw_profile.svg">
</a>
<!-- Dropdown - User Information -->
<div class="dropdown-menu dropdown-menu-right shadow animated--grow-in"
aria-labelledby="userDropdown">
<a class="dropdown-item" href="<?= base_url() ?>/users/profile">
<i class="fas fa-user fa-sm fa-fw mr-2 text-gray-400"></i>
Profile
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#logoutModal">
<i class="fas fa-sign-out-alt fa-sm fa-fw mr-2 text-gray-400"></i>
Logout
</a>
</div>
</li>
<?php } else { ?>
<li class="nav-item dropdown no-arrow">
<a class="nav-link dropdown-toggle" href="<?= route_to('login') ?>" >
<span class="mr-2 d-none d-lg-inline text-gray-600 small">Login</span>
<div class="rotate-n-0">
<i class="fas fa-unlock"></i>
</div>
</a>
</li>
<?php } ?>
</ul>
</nav>
<!-- End of Topbar -->
<!-- Begin Page Content -->
<div class="container-fluid">
<!-- Page Heading -->
<?= $this->renderSection('page-content'); ?>
</div>
<!-- /.container-fluid -->
</div>
<!-- End of Main Content -->
<!-- Footer -->
<footer class="sticky-footer bg-white">
<div class="container my-auto">
<div class="copyright text-center my-auto">
<span>Copyright © Your Website 2020</span>
</div>
</div>
</footer>
<!-- End of Footer -->
</div>
<!-- End of Content Wrapper -->
</div>
<!-- End of Page Wrapper -->
<!-- Scroll to Top Button-->
<a class="scroll-to-top rounded" href="#page-top">
<i class="fas fa-angle-up"></i>
</a>
<?= $this->renderSection('div-modal'); ?>
<!-- Logout Modal-->
<div class="modal fade" id="logoutModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Ready to Leave?</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">Select "Logout" below if you are ready to end your current session.</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="<?= route_to('logout') ?>">Logout</a>
</div>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript-->
<script src="<?= base_url() ?>/assets/vendor/jquery/jquery.min.js"></script>
<script src="<?= base_url() ?>/assets/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- Core plugin JavaScript-->
<script src="<?= base_url() ?>/assets/vendor/jquery-easing/jquery.easing.min.js"></script>
<!-- Custom scripts for all pages-->
<script src="<?= base_url() ?>/assets/js/sb-admin-2.min.js"></script>
<?= $this->renderSection('script-js'); ?>
</body>
</html>
Masuk kedalam folder \app\Views, kemudian buat folder users dan buat file index.php didalam folder tersebut. Isi index.php dengan script berikut
<?= $this->extend('template/index') ?>
<?= $this->section('page-content') ?>
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Daftar User</h6>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
<thead>
<tr>
<th>ID</th>
<th>Username</th>
<th>Email</th>
<th>Aktif</th>
<th style="width: 90px;"></th>
</tr>
</thead>
<tfoot>
<tr>
<th>ID</th>
<th>Username</th>
<th>Email</th>
<th>Aktif</th>
<th style="width: 90px;"></th>
</tr>
</tfoot>
<tbody>
<?php foreach($users as $row): ?>
<tr>
<td><?= $row->id; ?></td>
<td><?= $row->username; ?></td>
<td><?= $row->email; ?></td>
<td>
<a href="#" class="btn btn-sm btn-circle btn-active-users" data-id="<?= $row->id;?>" data-active="<?= $row->active == 1 ? 1 : 0 ;?>" title="Klik untuk Mengaktifkan atau Menonaktifkan">
<?= $row->active == 1 ? '<i class="fas fa-check-circle"></i>' : '<i class="fas fa-times-circle"></i>' ; ?>
</a>
</td>
<td>
<a href="<?= base_url(); ?>/users/changePassword/<?= $row->id;?>" class="btn btn-warning btn-circle btn-sm" title="Ubah Password" >
<i class="fas fa-key"></i>
</a>
<a href="#" class="btn btn-success btn-circle btn-sm btn-change-role" data-id="<?= $row->id;?>" title="Ubah Role">
<i class="fas fa-tasks"></i>
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('div-modal') ?>
<form action="<?= base_url(); ?>/users/activate" method="post">
<div class="modal fade" id="activateModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Update User</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">Pilih "Ya" untuk mengupdate User</div>
<div class="modal-footer">
<input type="hidden" name="id" class="id">
<input type="hidden" name="active" class="active">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Tidak</button>
<button type="submit" class="btn btn-primary">Ya</button>
</div>
</div>
</div>
</div>
</form>
<?= $this->endSection() ?>
<?= $this->section('script-js') ?>
<script type="text/javascript">
$(document).ready(function(){
// get Delete Page
$('.btn-active-users').on('click',function(){
// get data from button edit
const id = $(this).data('id');
const active = $(this).data('active');
// Set data to Form Edit
$('.id').val(id);
$('.active').val(active);
// Call Modal Edit
$('#activateModal').modal('show');
});
});
</script>
<?= $this->endSection() ?>
Sebagai gambaran, kalau sudah jadi akan seperti ini
Pada kolom Aktif adalah status user sudah aktivasi apa belum. Disini admin bisa mengubahnya dengan klik gambar contreng ( V ) atau gambar silang ( X )
– gambar contreng ( V ) = user sudah aktivasi
– gambar silang ( X ) = user belum aktivasi
Pada waktu klik pada gambar tersebut akan ada konfirmasi seperti ini
Tombol kunci untuk mengubah password, tapi untuk tombol yang paling kanan belum berfungsi
Kemudian buat file dengan nama set_password.php pada folder users, untuk update password. Scriptnya sebagai berikut
<?= $this->extend('template/index') ?>
<?= $this->section('page-content') ?>
<?php if (isset($validation)) { ?>
<div class="col-md-12">
<?php foreach ($validation->getErrors() as $error) : ?>
<div class="alert alert-warning" role="alert">
<i class="mdi mdi-alert-outline me-2"></i>
<?= esc($error) ?>
</div>
<?php endforeach ?>
</div>
<?php } ?>
<form action="<?= base_url(); ?>/users/setPassword" method="post">
<input type="hidden" name="id" class="id" value="<?= $id; ?>">
<div class="form-group row">
<div class="col-6">
<input type="password" name="password" class="form-control form-control-user <?php if(session('errors.password')) : ?>is-invalid<?php endif ?>" placeholder="<?=lang('Auth.password')?>" autocomplete="off">
</div>
</div>
<div class="form-group row">
<div class="col-6">
<input type="password" name="pass_confirm" class="form-control form-control-user <?php if(session('errors.pass_confirm')) : ?>is-invalid<?php endif ?>" placeholder="<?=lang('Auth.repeatPassword')?>" autocomplete="off">
</div>
</div>
<button type="submit" class="btn btn-primary">Simpan</button>
</form>
<?= $this->endSection() ?>
Satu lagi buat view untuk halaman redirect jika user tidak mempunya hak akses tempatkan di folder errors (\app\Views\errors\access_denied.php). Scriptnya sebagai berikut
<?= $this->extend('template/index') ?>
<?= $this->section('page-content') ?>
<div class="text-center">
<div class="error mx-auto" data-text="403">403</div>
<p class="lead text-gray-800 mb-5">Akses tidak diijinkan</p>
<a href="<?= base_url() ?>">← Kembali ke Halama Utama</a>
</div>
<?= $this->endSection() ?>
Terakhir
Silakan dicoba dan dikembangkan lagi, mungkin ada ide yang lebih dari yang saya buat.
Mudah-mudahan bermanfaat.