Sindbad~EG File Manager
<?php
require_once 'config/config.php';
$pageTitle = "Public Member Directory - " . APP_NAME;
$db = Database::getInstance()->getConnection();
// Start session
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// Check if user is verified
$isVerified = false;
$verifiedMemberId = null;
if (isset($_SESSION['directory_access_token']) && isset($_SESSION['directory_member_id'])) {
// Verify the session token
$stmt = $db->prepare("
SELECT member_id, verified_at
FROM public_directory_access
WHERE session_token = :token
AND is_verified = TRUE
AND verified_at > DATE_SUB(NOW(), INTERVAL 2 HOUR)
");
$stmt->execute(['token' => $_SESSION['directory_access_token']]);
$access = $stmt->fetch(PDO::FETCH_ASSOC);
if ($access) {
$isVerified = true;
$verifiedMemberId = $access['member_id'];
} else {
// Clear invalid session
unset($_SESSION['directory_access_token']);
unset($_SESSION['directory_member_id']);
}
}
// Handle logout
if (isset($_GET['logout'])) {
if (isset($_SESSION['directory_access_token'])) {
$stmt = $db->prepare("UPDATE public_directory_access SET session_token = NULL WHERE session_token = :token");
$stmt->execute(['token' => $_SESSION['directory_access_token']]);
}
unset($_SESSION['directory_access_token']);
unset($_SESSION['directory_member_id']);
unset($_SESSION['directory_verified_at']);
redirect('public-directory.php');
}
// Get verified member's location if verified
$memberLocation = null;
if ($isVerified && $verifiedMemberId) {
$stmt = $db->prepare("
SELECT m.area_id, m.district_id, m.assembly_id,
a.area_name, d.district_name, ass.assembly_name
FROM members m
LEFT JOIN areas a ON m.area_id = a.id
LEFT JOIN districts d ON m.district_id = d.id
LEFT JOIN assemblies ass ON m.assembly_id = ass.id
WHERE m.id = :member_id
");
$stmt->execute(['member_id' => $verifiedMemberId]);
$memberLocation = $stmt->fetch(PDO::FETCH_ASSOC);
}
// Handle search (only if verified)
$search = $_GET['search'] ?? '';
$members = [];
if ($isVerified && !empty($search)) {
$query = "
SELECT m.id, m.membershipcard_id, m.title, m.first_name, m.middle_name, m.last_name,
m.phone, m.email, m.member_type,
a.area_name, d.district_name, ass.assembly_name
FROM members m
LEFT JOIN areas a ON m.area_id = a.id
LEFT JOIN districts d ON m.district_id = d.id
LEFT JOIN assemblies ass ON m.assembly_id = ass.id
WHERE m.is_active = 1
";
$params = [];
// Apply location-based filter based on verified member's location
if ($memberLocation && $memberLocation['assembly_id']) {
$query .= " AND m.assembly_id = :assembly_id";
$params['assembly_id'] = $memberLocation['assembly_id'];
} elseif ($memberLocation && $memberLocation['district_id']) {
$query .= " AND m.district_id = :district_id";
$params['district_id'] = $memberLocation['district_id'];
} elseif ($memberLocation && $memberLocation['area_id']) {
$query .= " AND m.area_id = :area_id";
$params['area_id'] = $memberLocation['area_id'];
}
// Add search conditions
$query .= " AND (
m.first_name LIKE :search1 OR
m.middle_name LIKE :search2 OR
m.last_name LIKE :search3 OR
m.membershipcard_id LIKE :search4 OR
CONCAT(m.first_name, ' ', m.middle_name, ' ', m.last_name) LIKE :search5 OR
CONCAT(m.first_name, ' ', m.last_name) LIKE :search6
)";
$searchTerm = "%{$search}%";
for ($i = 1; $i <= 6; $i++) {
$params["search{$i}"] = $searchTerm;
}
$query .= " ORDER BY m.last_name, m.first_name LIMIT 50";
$stmt = $db->prepare($query);
$stmt->execute($params);
$members = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// Get settings
$stmt = $db->query("SELECT * FROM general_settings ORDER BY id DESC LIMIT 1");
$settings = $stmt->fetch();
$settings = array_merge([
'site_title' => APP_NAME,
'theme_primary_color' => '#3B82F6',
'theme_secondary_color' => '#10B981'
], $settings ?: []);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $pageTitle; ?></title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
:root {
--primary-color: <?php echo $settings['theme_primary_color']; ?>;
--secondary-color: <?php echo $settings['theme_secondary_color']; ?>;
}
.bg-primary { background-color: var(--primary-color); }
.text-primary { color: var(--primary-color); }
.gradient-bg { background: linear-gradient(135deg, #1E40AF 0%, #9333EA 50%, #F97316 100%); }
.card-hover { transition: all 0.3s ease; }
.card-hover:hover { transform: translateY(-2px); box-shadow: 0 10px 20px -5px rgba(0,0,0,0.1); }
</style>
</head>
<body class="bg-gray-50">
<!-- Header -->
<header class="bg-white shadow-lg">
<div class="container mx-auto px-4">
<div class="flex items-center justify-between h-16">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 rounded-xl flex items-center justify-center gradient-bg">
<i class="fas fa-church text-white"></i>
</div>
<div>
<h1 class="text-lg font-bold text-gray-800"><?php echo htmlspecialchars($settings['site_title']); ?></h1>
<p class="text-xs text-gray-500">Public Directory</p>
</div>
</div>
<nav class="flex items-center space-x-4">
<a href="index.php" class="text-gray-700 hover:text-blue-600 transition">
<i class="fas fa-home mr-1"></i>Home
</a>
<?php if ($isVerified): ?>
<a href="?logout=1" class="text-red-600 hover:text-red-700 transition">
<i class="fas fa-sign-out-alt mr-1"></i>Logout
</a>
<?php else: ?>
<a href="login.php" class="text-gray-700 hover:text-blue-600 transition">
<i class="fas fa-sign-in-alt mr-1"></i>Login
</a>
<?php endif; ?>
</nav>
</div>
</div>
</header>
<div class="container mx-auto px-4 py-8">
<div class="max-w-4xl mx-auto">
<?php if (!$isVerified): ?>
<!-- Verification Section -->
<div class="mb-8">
<div class="gradient-bg rounded-xl shadow-lg p-8 text-white text-center mb-8">
<i class="fas fa-shield-alt text-6xl mb-4 opacity-90"></i>
<h1 class="text-3xl font-bold mb-2">Secure Directory Access</h1>
<p class="text-blue-100">Enter your membership ID to receive a verification code</p>
</div>
</div>
<!-- Step 1: Enter Membership ID -->
<div id="step1" class="bg-white rounded-xl shadow-lg p-8 mb-6">
<div class="flex items-center mb-6">
<div class="w-10 h-10 rounded-full bg-blue-500 text-white flex items-center justify-center font-bold mr-3">1</div>
<h2 class="text-2xl font-bold text-gray-800">Enter Membership ID</h2>
</div>
<form id="requestCodeForm" class="space-y-4">
<div>
<label class="block text-sm font-semibold text-gray-700 mb-2">Membership Card ID</label>
<input type="text"
id="membership_id"
name="membership_id"
placeholder="Enter your membership card number"
required
class="w-full px-4 py-3 border-2 border-gray-200 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<div id="step1Message" class="hidden"></div>
<button type="submit"
id="sendCodeBtn"
class="w-full gradient-bg text-white py-3 rounded-lg font-semibold hover:shadow-lg transition-all duration-300">
<i class="fas fa-paper-plane mr-2"></i>
Send Verification Code
</button>
</form>
</div>
<!-- Step 2: Enter Verification Code -->
<div id="step2" class="bg-white rounded-xl shadow-lg p-8 hidden">
<div class="flex items-center mb-6">
<div class="w-10 h-10 rounded-full bg-green-500 text-white flex items-center justify-center font-bold mr-3">2</div>
<h2 class="text-2xl font-bold text-gray-800">Enter Verification Code</h2>
</div>
<div class="mb-6 p-4 bg-blue-50 border border-blue-200 rounded-lg">
<p class="text-sm text-blue-800">
<i class="fas fa-info-circle mr-2"></i>
We've sent a 6-digit code to <span id="emailDisplay" class="font-semibold"></span>
</p>
</div>
<form id="verifyCodeForm" class="space-y-4">
<input type="hidden" id="membership_id_verify" name="membership_id">
<div>
<label class="block text-sm font-semibold text-gray-700 mb-2">Verification Code</label>
<input type="text"
id="verification_code"
name="verification_code"
placeholder="Enter 6-digit code"
maxlength="6"
pattern="[0-9]{6}"
required
class="w-full px-4 py-3 border-2 border-gray-200 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-green-500 text-center text-2xl font-bold tracking-wider">
</div>
<div id="step2Message" class="hidden"></div>
<button type="submit"
id="verifyCodeBtn"
class="w-full bg-green-600 hover:bg-green-700 text-white py-3 rounded-lg font-semibold transition-all duration-300">
<i class="fas fa-check-circle mr-2"></i>
Verify & Access Directory
</button>
<button type="button"
onclick="location.reload()"
class="w-full bg-gray-200 hover:bg-gray-300 text-gray-700 py-3 rounded-lg font-semibold transition-all duration-300">
<i class="fas fa-arrow-left mr-2"></i>
Back
</button>
</form>
</div>
<?php else: ?>
<!-- Directory Search Section -->
<div class="mb-8">
<div class="gradient-bg rounded-xl shadow-lg p-8 text-white">
<div class="flex items-center justify-between mb-4">
<div>
<h1 class="text-3xl font-bold mb-2">Member Directory</h1>
<p class="text-blue-100">
Showing members from:
<?php
if ($memberLocation) {
if ($memberLocation['assembly_id']) {
echo htmlspecialchars($memberLocation['assembly_name']);
} elseif ($memberLocation['district_id']) {
echo htmlspecialchars($memberLocation['district_name']);
} elseif ($memberLocation['area_id']) {
echo htmlspecialchars($memberLocation['area_name']);
}
} else {
echo 'All locations';
}
?>
</p>
</div>
<div>
<span class="inline-flex items-center px-4 py-2 bg-white/20 rounded-lg text-sm">
<i class="fas fa-check-circle mr-2"></i>
Verified Access
</span>
</div>
</div>
</div>
</div>
<!-- Search Box -->
<div class="bg-white rounded-xl shadow-lg p-6 mb-8">
<form method="GET" action="" class="flex flex-col md:flex-row gap-4">
<div class="flex-1">
<input type="text"
name="search"
value="<?= htmlspecialchars($search) ?>"
placeholder="Search by name or membership ID..."
class="w-full px-6 py-4 border-2 border-gray-200 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-lg">
</div>
<button type="submit" class="gradient-bg text-white px-8 py-4 rounded-lg font-semibold hover:shadow-lg transition-all duration-300">
<i class="fas fa-search mr-2"></i>
Search
</button>
</form>
</div>
<!-- Search Results -->
<?php if (!empty($search)): ?>
<?php if (count($members) > 0): ?>
<div class="bg-white rounded-xl shadow-lg p-6">
<h2 class="text-xl font-bold text-gray-800 mb-6">
<i class="fas fa-users mr-2 text-blue-500"></i>
Found <?= count($members) ?> member<?= count($members) !== 1 ? 's' : '' ?>
</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<?php foreach ($members as $member): ?>
<div class="border border-gray-200 rounded-lg p-6 card-hover">
<div class="flex items-start space-x-4">
<div class="w-16 h-16 rounded-full bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center text-white text-xl font-bold flex-shrink-0">
<?= strtoupper(substr($member['first_name'], 0, 1) . substr($member['last_name'], 0, 1)) ?>
</div>
<div class="flex-1">
<h3 class="font-bold text-gray-800 text-lg mb-2">
<?= htmlspecialchars(trim(($member['title'] ?? '') . ' ' . $member['first_name'] . ' ' . ($member['middle_name'] ?? '') . ' ' . $member['last_name'])) ?>
</h3>
<div class="space-y-1 text-sm">
<p class="text-gray-600">
<i class="fas fa-id-card mr-2 text-blue-500"></i>
<?= htmlspecialchars($member['membershipcard_id'] ?? 'N/A') ?>
</p>
<p class="text-gray-600">
<i class="fas fa-phone mr-2 text-green-500"></i>
<?= htmlspecialchars($member['phone'] ?? 'N/A') ?>
</p>
<p class="text-gray-600">
<i class="fas fa-map-marker-alt mr-2 text-red-500"></i>
<?= htmlspecialchars($member['assembly_name'] ?? $member['district_name'] ?? $member['area_name'] ?? 'N/A') ?>
</p>
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold bg-blue-100 text-blue-800 mt-2">
<?= htmlspecialchars($member['member_type'] ?? 'Member') ?>
</span>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php else: ?>
<div class="bg-white rounded-xl shadow-lg p-12 text-center">
<i class="fas fa-search text-gray-300 text-6xl mb-4"></i>
<h3 class="text-2xl font-bold text-gray-800 mb-3">No Members Found</h3>
<p class="text-gray-600">No members found matching "<?= htmlspecialchars($search) ?>"</p>
</div>
<?php endif; ?>
<?php else: ?>
<div class="bg-white rounded-xl shadow-lg p-12 text-center">
<i class="fas fa-address-book text-gray-300 text-6xl mb-4"></i>
<h3 class="text-2xl font-bold text-gray-800 mb-3">Search for Members</h3>
<p class="text-gray-600">Use the search bar above to find members by name or membership ID</p>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
<footer class="bg-white border-t border-gray-200 mt-12">
<div class="container mx-auto px-4 py-6">
<p class="text-center text-gray-600 text-sm">
© <?php echo date('Y'); ?> <?php echo htmlspecialchars($settings['site_title']); ?>. All rights reserved.
</p>
</div>
</footer>
<script>
// Step 1: Request verification code
document.getElementById('requestCodeForm').addEventListener('submit', async function(e) {
e.preventDefault();
const membershipId = document.getElementById('membership_id').value.trim();
const btn = document.getElementById('sendCodeBtn');
const messageDiv = document.getElementById('step1Message');
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Sending...';
try {
const response = await fetch('api/send-directory-code.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ membership_id: membershipId })
});
const data = await response.json();
if (data.success) {
// Show success message
messageDiv.className = 'p-4 bg-green-50 border border-green-200 text-green-800 rounded-lg';
messageDiv.innerHTML = '<i class="fas fa-check-circle mr-2"></i>' + data.message;
messageDiv.classList.remove('hidden');
// Show step 2
setTimeout(() => {
document.getElementById('step1').classList.add('hidden');
document.getElementById('step2').classList.remove('hidden');
document.getElementById('membership_id_verify').value = membershipId;
document.getElementById('emailDisplay').textContent = data.email;
}, 1500);
} else {
messageDiv.className = 'p-4 bg-red-50 border border-red-200 text-red-800 rounded-lg';
messageDiv.innerHTML = '<i class="fas fa-exclamation-circle mr-2"></i>' + data.message;
messageDiv.classList.remove('hidden');
btn.disabled = false;
btn.innerHTML = '<i class="fas fa-paper-plane mr-2"></i>Send Verification Code';
}
} catch (error) {
messageDiv.className = 'p-4 bg-red-50 border border-red-200 text-red-800 rounded-lg';
messageDiv.innerHTML = '<i class="fas fa-exclamation-circle mr-2"></i>An error occurred. Please try again.';
messageDiv.classList.remove('hidden');
btn.disabled = false;
btn.innerHTML = '<i class="fas fa-paper-plane mr-2"></i>Send Verification Code';
}
});
// Step 2: Verify code
document.getElementById('verifyCodeForm').addEventListener('submit', async function(e) {
e.preventDefault();
const membershipId = document.getElementById('membership_id_verify').value;
const verificationCode = document.getElementById('verification_code').value.trim();
const btn = document.getElementById('verifyCodeBtn');
const messageDiv = document.getElementById('step2Message');
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Verifying...';
try {
const response = await fetch('api/verify-directory-code.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
membership_id: membershipId,
verification_code: verificationCode
})
});
const data = await response.json();
if (data.success) {
messageDiv.className = 'p-4 bg-green-50 border border-green-200 text-green-800 rounded-lg';
messageDiv.innerHTML = '<i class="fas fa-check-circle mr-2"></i>' + data.message;
messageDiv.classList.remove('hidden');
// Redirect to directory
setTimeout(() => {
window.location.href = 'public-directory.php';
}, 1500);
} else {
messageDiv.className = 'p-4 bg-red-50 border border-red-200 text-red-800 rounded-lg';
messageDiv.innerHTML = '<i class="fas fa-exclamation-circle mr-2"></i>' + data.message;
messageDiv.classList.remove('hidden');
btn.disabled = false;
btn.innerHTML = '<i class="fas fa-check-circle mr-2"></i>Verify & Access Directory';
}
} catch (error) {
messageDiv.className = 'p-4 bg-red-50 border border-red-200 text-red-800 rounded-lg';
messageDiv.innerHTML = '<i class="fas fa-exclamation-circle mr-2"></i>An error occurred. Please try again.';
messageDiv.classList.remove('hidden');
btn.disabled = false;
btn.innerHTML = '<i class="fas fa-check-circle mr-2"></i>Verify & Access Directory';
}
});
</script>
<?php
// Include Chat Hub Widget (Admin Chat + AI Chatbot)
if (file_exists(__DIR__ . '/includes/chat-hub-widget.php')) {
include 'includes/chat-hub-widget.php';
}
?>
</body>
</html>
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists