315 lines
11 KiB
PHP
315 lines
11 KiB
PHP
<?php
|
|
/**
|
|
* auth.php
|
|
* Fungsi session, role pengguna, response JSON, validasi umum,
|
|
* prepared statement helper, perhitungan Haversine, dan sinkronisasi area.
|
|
*/
|
|
if (session_status() === PHP_SESSION_NONE) {
|
|
session_start();
|
|
}
|
|
|
|
require_once __DIR__ . '/koneksi.php';
|
|
|
|
function json_response($status = 'ok', $message = '', $data = [], $http_code = 200) {
|
|
http_response_code($http_code);
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
echo json_encode([
|
|
'status' => $status,
|
|
'message' => $message,
|
|
'data' => $data
|
|
], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
function require_post() {
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
json_response('err', 'Method harus POST.', [], 405);
|
|
}
|
|
}
|
|
|
|
function is_logged_in() {
|
|
return isset($_SESSION['user_id'], $_SESSION['role']);
|
|
}
|
|
|
|
function require_login($json = false) {
|
|
if (!is_logged_in()) {
|
|
if ($json) json_response('err', 'Silakan login terlebih dahulu.', [], 401);
|
|
header('Location: login.php');
|
|
exit;
|
|
}
|
|
}
|
|
|
|
function current_user() {
|
|
global $koneksi;
|
|
if (!is_logged_in()) return null;
|
|
$id = (int) $_SESSION['user_id'];
|
|
$stmt = $koneksi->prepare("SELECT id, nama, username, role, status_akun FROM users WHERE id=? LIMIT 1");
|
|
$stmt->bind_param('i', $id);
|
|
$stmt->execute();
|
|
$res = $stmt->get_result();
|
|
return $res->fetch_assoc();
|
|
}
|
|
|
|
function has_role($roles) {
|
|
if (!is_array($roles)) $roles = [$roles];
|
|
return is_logged_in() && in_array($_SESSION['role'], $roles, true);
|
|
}
|
|
|
|
function require_role($roles, $json = false) {
|
|
if (!has_role($roles)) {
|
|
if ($json) json_response('err', 'Akses ditolak. Role tidak sesuai.', [], 403);
|
|
http_response_code(403);
|
|
echo '<h2>403 - Akses ditolak</h2>';
|
|
exit;
|
|
}
|
|
}
|
|
|
|
function post($key, $default = '') {
|
|
return isset($_POST[$key]) ? trim((string) $_POST[$key]) : $default;
|
|
}
|
|
|
|
function getv($key, $default = '') {
|
|
return isset($_GET[$key]) ? trim((string) $_GET[$key]) : $default;
|
|
}
|
|
|
|
function clean_text($str, $max = 255) {
|
|
$str = trim((string) $str);
|
|
if (mb_strlen($str) > $max) $str = mb_substr($str, 0, $max);
|
|
return $str;
|
|
}
|
|
|
|
function valid_enum($value, $allowed) {
|
|
return in_array($value, $allowed, true);
|
|
}
|
|
|
|
function valid_latlng($lat, $lng) {
|
|
return is_numeric($lat) && is_numeric($lng) && $lat >= -90 && $lat <= 90 && $lng >= -180 && $lng <= 180;
|
|
}
|
|
|
|
function haversine_meter($lat1, $lon1, $lat2, $lon2) {
|
|
$R = 6371000; // meter
|
|
$dLat = deg2rad($lat2 - $lat1);
|
|
$dLon = deg2rad($lon2 - $lon1);
|
|
$a = sin($dLat / 2) * sin($dLat / 2) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLon / 2) * sin($dLon / 2);
|
|
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
|
|
return $R * $c;
|
|
}
|
|
|
|
function stmt_bind($stmt, $types, $params) {
|
|
if ($types === '' || empty($params)) return true;
|
|
$refs = [];
|
|
$refs[] = $types;
|
|
foreach ($params as $k => $v) {
|
|
$refs[] = &$params[$k];
|
|
}
|
|
return call_user_func_array([$stmt, 'bind_param'], $refs);
|
|
}
|
|
|
|
function fetch_all_stmt($stmt) {
|
|
$stmt->execute();
|
|
$result = $stmt->get_result();
|
|
$rows = [];
|
|
while ($row = $result->fetch_assoc()) $rows[] = $row;
|
|
return $rows;
|
|
}
|
|
|
|
function build_in_condition($column, $values, &$types, &$params) {
|
|
$ids = [];
|
|
foreach ((array)$values as $v) {
|
|
$iv = (int)$v;
|
|
if ($iv > 0) $ids[] = $iv;
|
|
}
|
|
$ids = array_values(array_unique($ids));
|
|
if (empty($ids)) return '1=0';
|
|
$placeholders = implode(',', array_fill(0, count($ids), '?'));
|
|
$types .= str_repeat('i', count($ids));
|
|
foreach ($ids as $id) $params[] = $id;
|
|
return "$column IN ($placeholders)";
|
|
}
|
|
|
|
function get_admin_rumah_ibadah_list($user_id = null, $only_diterima = true) {
|
|
global $koneksi;
|
|
if ($user_id === null) $user_id = (int) $_SESSION['user_id'];
|
|
$sql = "SELECT * FROM rumah_ibadah WHERE user_id=? AND COALESCE(status_hapus,'normal')<>'disetujui'";
|
|
if ($only_diterima) $sql .= " AND status_pengajuan='diterima'";
|
|
$sql .= " ORDER BY id ASC";
|
|
$stmt = $koneksi->prepare($sql);
|
|
$stmt->bind_param('i', $user_id);
|
|
return fetch_all_stmt($stmt);
|
|
}
|
|
|
|
function get_admin_rumah_ibadah_ids($user_id = null, $only_diterima = true) {
|
|
$rows = get_admin_rumah_ibadah_list($user_id, $only_diterima);
|
|
return array_map('intval', array_column($rows, 'id'));
|
|
}
|
|
|
|
function get_admin_rumah_ibadah($user_id = null) {
|
|
// Dipertahankan untuk kompatibilitas lama. Untuk logic baru, pakai get_admin_rumah_ibadah_list()/ids().
|
|
$rows = get_admin_rumah_ibadah_list($user_id, true);
|
|
return $rows[0] ?? null;
|
|
}
|
|
|
|
function is_admin_rumah_ibadah_id($rumah_ibadah_id, $user_id = null, $only_diterima = true) {
|
|
if ($user_id === null) $user_id = (int) $_SESSION['user_id'];
|
|
$ids = get_admin_rumah_ibadah_ids($user_id, $only_diterima);
|
|
return in_array((int)$rumah_ibadah_id, $ids, true);
|
|
}
|
|
|
|
function get_nearest_rumah_ibadah($lat, $lng, $max_meter = 300, $admin_user_id = null) {
|
|
global $koneksi;
|
|
$rows = [];
|
|
if ($admin_user_id !== null) {
|
|
$stmt = $koneksi->prepare("SELECT * FROM rumah_ibadah WHERE user_id=? AND status_pengajuan='diterima' AND COALESCE(status_hapus,'normal')<>'disetujui'");
|
|
$admin_user_id = (int)$admin_user_id;
|
|
$stmt->bind_param('i', $admin_user_id);
|
|
$rumahRows = fetch_all_stmt($stmt);
|
|
} else {
|
|
$rumahRows = [];
|
|
$res = $koneksi->query("SELECT * FROM rumah_ibadah WHERE status_pengajuan='diterima' AND COALESCE(status_hapus,'normal')<>'disetujui'");
|
|
while ($row = $res->fetch_assoc()) $rumahRows[] = $row;
|
|
}
|
|
|
|
foreach ($rumahRows as $row) {
|
|
$jarak = haversine_meter((float) $lat, (float) $lng, (float) $row['latitude'], (float) $row['longitude']);
|
|
if ($jarak <= $max_meter) {
|
|
$row['jarak_meter'] = round($jarak, 2);
|
|
$rows[] = $row;
|
|
}
|
|
}
|
|
usort($rows, fn($a, $b) => $a['jarak_meter'] <=> $b['jarak_meter']);
|
|
return $rows[0] ?? null;
|
|
}
|
|
|
|
function get_penerima_by_id($id) {
|
|
global $koneksi;
|
|
$stmt = $koneksi->prepare("SELECT pb.*, ri.nama_rumah_ibadah, ri.user_id AS admin_rumah_user_id, ri.latitude AS ri_latitude, ri.longitude AS ri_longitude FROM penerima_bantuan pb LEFT JOIN rumah_ibadah ri ON ri.id=pb.rumah_ibadah_id WHERE pb.id=? LIMIT 1");
|
|
$stmt->bind_param('i', $id);
|
|
$stmt->execute();
|
|
return $stmt->get_result()->fetch_assoc();
|
|
}
|
|
|
|
function get_rumah_ibadah_by_id($id) {
|
|
global $koneksi;
|
|
$stmt = $koneksi->prepare("SELECT ri.*, u.nama AS nama_admin, u.username AS username_admin FROM rumah_ibadah ri LEFT JOIN users u ON u.id=ri.user_id WHERE ri.id=? LIMIT 1");
|
|
$stmt->bind_param('i', $id);
|
|
$stmt->execute();
|
|
return $stmt->get_result()->fetch_assoc();
|
|
}
|
|
|
|
function can_manage_rumah_ibadah($rumah) {
|
|
if (!$rumah || !is_logged_in()) return false;
|
|
if ($_SESSION['role'] === 'super_admin') return true;
|
|
if ($_SESSION['role'] === 'admin_rumah_ibadah') {
|
|
return (int)$rumah['user_id'] === (int)$_SESSION['user_id'];
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function can_manage_penerima($penerima) {
|
|
if (!$penerima || !is_logged_in()) return false;
|
|
if ($_SESSION['role'] === 'super_admin') return true;
|
|
if ($_SESSION['role'] === 'penerima' && (int) $penerima['user_id'] === (int) $_SESSION['user_id']) return true;
|
|
if ($_SESSION['role'] === 'admin_rumah_ibadah') {
|
|
return !empty($penerima['rumah_ibadah_id']) && is_admin_rumah_ibadah_id((int)$penerima['rumah_ibadah_id'], null, true);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function insert_riwayat_status($penerima_id, $status_lama, $status_baru, $catatan) {
|
|
global $koneksi;
|
|
$uid = (int) $_SESSION['user_id'];
|
|
$stmt = $koneksi->prepare("INSERT INTO riwayat_status (penerima_id, user_id, status_lama, status_baru, catatan) VALUES (?,?,?,?,?)");
|
|
$stmt->bind_param('iisss', $penerima_id, $uid, $status_lama, $status_baru, $catatan);
|
|
$stmt->execute();
|
|
}
|
|
|
|
function get_user_by_username_role($username, $roles) {
|
|
global $koneksi;
|
|
if (!is_array($roles)) $roles = [$roles];
|
|
$username = clean_text($username, 50);
|
|
if ($username === '') return null;
|
|
$placeholders = implode(',', array_fill(0, count($roles), '?'));
|
|
$types = 's' . str_repeat('s', count($roles));
|
|
$params = array_merge([$username], $roles);
|
|
$stmt = $koneksi->prepare("SELECT id, nama, username, role, status_akun FROM users WHERE username=? AND role IN ($placeholders) AND status_akun='aktif' LIMIT 1");
|
|
stmt_bind($stmt, $types, $params);
|
|
$stmt->execute();
|
|
return $stmt->get_result()->fetch_assoc();
|
|
}
|
|
|
|
function has_active_penerima_pin($user_id, $exclude_penerima_id = 0) {
|
|
global $koneksi;
|
|
$user_id = (int)$user_id;
|
|
$exclude_penerima_id = (int)$exclude_penerima_id;
|
|
if ($user_id <= 0) return false;
|
|
$sql = "SELECT id FROM penerima_bantuan
|
|
WHERE user_id=?
|
|
AND status_pengajuan IN ('menunggu','diterima')
|
|
AND COALESCE(status_hapus,'normal')<>'disetujui'";
|
|
$types = 'i';
|
|
$params = [$user_id];
|
|
if ($exclude_penerima_id > 0) {
|
|
$sql .= " AND id<>?";
|
|
$types .= 'i';
|
|
$params[] = $exclude_penerima_id;
|
|
}
|
|
$sql .= " LIMIT 1";
|
|
$stmt = $koneksi->prepare($sql);
|
|
stmt_bind($stmt, $types, $params);
|
|
$stmt->execute();
|
|
return (bool)$stmt->get_result()->fetch_assoc();
|
|
}
|
|
|
|
function sync_single_penerima_area($penerima_id) {
|
|
global $koneksi;
|
|
$penerima_id = (int)$penerima_id;
|
|
$stmt = $koneksi->prepare("SELECT id, latitude, longitude FROM penerima_bantuan WHERE id=? AND status_pengajuan IN ('menunggu','diterima') AND COALESCE(status_hapus,'normal')<>'disetujui' LIMIT 1");
|
|
$stmt->bind_param('i', $penerima_id);
|
|
$stmt->execute();
|
|
$pb = $stmt->get_result()->fetch_assoc();
|
|
if (!$pb) return false;
|
|
|
|
$nearest = get_nearest_rumah_ibadah((float)$pb['latitude'], (float)$pb['longitude'], 300);
|
|
if ($nearest) {
|
|
$riId = (int)$nearest['id'];
|
|
$jarak = (float)$nearest['jarak_meter'];
|
|
$stmtUp = $koneksi->prepare("UPDATE penerima_bantuan SET rumah_ibadah_id=?, jarak_meter=? WHERE id=?");
|
|
$stmtUp->bind_param('idi', $riId, $jarak, $penerima_id);
|
|
} else {
|
|
$stmtUp = $koneksi->prepare("UPDATE penerima_bantuan SET rumah_ibadah_id=NULL, jarak_meter=NULL WHERE id=?");
|
|
$stmtUp->bind_param('i', $penerima_id);
|
|
}
|
|
return $stmtUp->execute();
|
|
}
|
|
|
|
function sync_all_penerima_area() {
|
|
global $koneksi;
|
|
$ids = [];
|
|
$res = $koneksi->query("SELECT id FROM penerima_bantuan WHERE status_pengajuan IN ('menunggu','diterima') AND COALESCE(status_hapus,'normal')<>'disetujui'");
|
|
while ($row = $res->fetch_assoc()) $ids[] = (int)$row['id'];
|
|
$updated = 0;
|
|
foreach ($ids as $id) {
|
|
if (sync_single_penerima_area($id)) $updated++;
|
|
}
|
|
return $updated;
|
|
}
|
|
|
|
function sync_penerima_area_by_rumah_ibadah($rumah_ibadah_id) {
|
|
// Tetap menerima parameter lama, tetapi sinkronisasi dihitung ulang secara global agar radius beririsan memilih RI terdekat.
|
|
return sync_all_penerima_area();
|
|
}
|
|
|
|
function detach_penerima_from_rumah_ibadah($rumah_ibadah_id) {
|
|
global $koneksi;
|
|
$rumah_ibadah_id = (int) $rumah_ibadah_id;
|
|
$stmt = $koneksi->prepare("UPDATE penerima_bantuan SET rumah_ibadah_id=NULL, jarak_meter=NULL WHERE rumah_ibadah_id=? AND status_pengajuan IN ('menunggu','diterima')");
|
|
$stmt->bind_param('i', $rumah_ibadah_id);
|
|
$stmt->execute();
|
|
$affected = $stmt->affected_rows;
|
|
// Setelah dilepas, cari ulang rumah ibadah aktif terdekat lain jika ada.
|
|
sync_all_penerima_area();
|
|
return $affected;
|
|
}
|
|
|
|
?>
|