219 lines
11 KiB
PHP
219 lines
11 KiB
PHP
<?php
|
|
require_once 'includes/db.php';
|
|
require_once 'includes/auth.php';
|
|
|
|
if (!isLoggedIn()) {
|
|
header('Location: login.php');
|
|
exit;
|
|
}
|
|
|
|
$user_id = $_SESSION['user_id'];
|
|
$success = '';
|
|
$error = '';
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
if (isset($_POST['change_password'])) {
|
|
$old_pass = $_POST['old_password'];
|
|
$new_pass = $_POST['new_password'];
|
|
$confirm_pass = $_POST['confirm_password'];
|
|
|
|
$stmt = $pdo->prepare("SELECT password FROM users WHERE id = ?");
|
|
$stmt->execute([$user_id]);
|
|
$user = $stmt->fetch();
|
|
|
|
if (password_verify($old_pass, $user['password'])) {
|
|
if ($new_pass === $confirm_pass) {
|
|
if (strlen($new_pass) >= 6) {
|
|
$hashed = password_hash($new_pass, PASSWORD_DEFAULT);
|
|
$pdo->prepare("UPDATE users SET password = ? WHERE id = ?")->execute([$hashed, $user_id]);
|
|
$success = "Password changed successfully!";
|
|
} else {
|
|
$error = "New password must be at least 6 characters.";
|
|
}
|
|
} else {
|
|
$error = "New passwords do not match.";
|
|
}
|
|
} else {
|
|
$error = "Incorrect current password.";
|
|
}
|
|
}
|
|
|
|
if (isset($_POST['update_avatar'])) {
|
|
if (isset($_FILES['avatar']) && $_FILES['avatar']['error'] === 0) {
|
|
$ext = strtolower(pathinfo($_FILES['avatar']['name'], PATHINFO_EXTENSION));
|
|
$allowed = ['jpg', 'jpeg', 'png', 'webp'];
|
|
if (in_array($ext, $allowed)) {
|
|
$filename = 'avatar_' . $user_id . '_' . time() . '.' . $ext;
|
|
if (move_uploaded_file($_FILES['avatar']['tmp_name'], 'uploads/' . $filename)) {
|
|
// Delete old avatar if exists
|
|
$stmt = $pdo->prepare("SELECT avatar_url FROM users WHERE id = ?");
|
|
$stmt->execute([$user_id]);
|
|
$old = $stmt->fetchColumn();
|
|
if ($old && strpos($old, 'uploads/') === 0) @unlink($old);
|
|
|
|
$avatar_url = 'uploads/' . $filename;
|
|
$pdo->prepare("UPDATE users SET avatar_url = ? WHERE id = ?")->execute([$avatar_url, $user_id]);
|
|
$success = "Avatar updated!";
|
|
}
|
|
} else {
|
|
$error = "Invalid image format.";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get user data
|
|
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
|
|
$stmt->execute([$user_id]);
|
|
$user_data = $stmt->fetch();
|
|
$avatar = $user_data['avatar_url'] ?: '';
|
|
|
|
// Get bookmarks
|
|
$stmt = $pdo->prepare("SELECT v.* FROM videos v JOIN bookmarks b ON v.id = b.video_id WHERE b.user_id = ? ORDER BY b.created_at DESC");
|
|
$stmt->execute([$user_id]);
|
|
$bookmarks = $stmt->fetchAll();
|
|
|
|
require_once 'includes/header.php';
|
|
?>
|
|
|
|
<div style="max-width: 1000px; margin: 40px auto; padding: 0 24px; display: grid; grid-template-columns: 300px 1fr; gap: 40px;">
|
|
<!-- Sidebar -->
|
|
<div>
|
|
<div style="background: var(--bg-card); padding: 24px; border-radius: 16px; border: 1px solid var(--glass-border); position: sticky; top: 100px;">
|
|
<div style="text-align: center; margin-bottom: 24px;">
|
|
<div style="width: 100px; height: 100px; background: var(--primary-color); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 16px; overflow: hidden; border: 3px solid var(--glass-border);">
|
|
<?php if ($avatar): ?>
|
|
<img src="<?= htmlspecialchars($avatar) ?>" style="width: 100%; height: 100%; object-fit: cover;">
|
|
<?php else: ?>
|
|
<span style="font-size: 2.5rem; color: white;"><?= strtoupper(substr($_SESSION['username'], 0, 1)) ?></span>
|
|
<?php endif; ?>
|
|
</div>
|
|
<h3><?= htmlspecialchars($_SESSION['username']) ?></h3>
|
|
<p style="color: var(--text-muted); font-size: 0.9rem;">Member since <?= date('M Y', strtotime($user_data['created_at'])) ?></p>
|
|
</div>
|
|
|
|
<form method="POST" enctype="multipart/form-data" style="margin-bottom: 24px;">
|
|
<input type="hidden" name="update_avatar" value="1">
|
|
<label class="btn" style="background: var(--glass); font-size: 0.8rem; cursor: pointer; display: block; text-align: center;">
|
|
<i class="fas fa-camera"></i> Change Avatar
|
|
<input type="file" name="avatar" style="display: none;" onchange="this.form.submit()">
|
|
</label>
|
|
</form>
|
|
|
|
<div style="display: flex; flex-direction: column; gap: 8px;">
|
|
<a href="#bookmarks" class="btn" style="background: var(--glass); justify-content: flex-start;"><i class="fas fa-bookmark" style="width: 20px;"></i> My Bookmarks</a>
|
|
<a href="#security" class="btn" style="background: var(--glass); justify-content: flex-start;"><i class="fas fa-shield-alt" style="width: 20px;"></i> Security</a>
|
|
<a href="logout.php" class="btn" style="background: rgba(255,64,129,0.1); color: #ff4081; justify-content: flex-start;"><i class="fas fa-sign-out-alt" style="width: 20px;"></i> Logout</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Content -->
|
|
<div>
|
|
<!-- Bookmarks Section -->
|
|
<section id="bookmarks" style="margin-bottom: 60px;">
|
|
<h2 style="margin-bottom: 24px;">My Bookmarks</h2>
|
|
<?php
|
|
$stmt = $pdo->prepare("SELECT v.*, b.video_timestamp FROM videos v JOIN bookmarks b ON v.id = b.video_id WHERE b.user_id = ? ORDER BY b.created_at DESC");
|
|
$stmt->execute([$user_id]);
|
|
$bookmarks = $stmt->fetchAll();
|
|
|
|
function formatTime($seconds) {
|
|
if ($seconds <= 0) return "";
|
|
$mins = floor($seconds / 60);
|
|
$secs = floor($seconds % 60);
|
|
return sprintf("%d:%02d", $mins, $secs);
|
|
}
|
|
?>
|
|
<?php if (empty($bookmarks)): ?>
|
|
<div style="background: var(--bg-card); padding: 40px; border-radius: 16px; border: 1px solid var(--glass-border); text-align: center; color: var(--text-muted);">
|
|
<i class="fas fa-bookmark" style="font-size: 3rem; margin-bottom: 16px; display: block;"></i>
|
|
<p>You haven't bookmarked any sermons yet.</p>
|
|
</div>
|
|
<?php else: ?>
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 20px;">
|
|
<?php foreach ($bookmarks as $v):
|
|
$time_str = formatTime($v['video_timestamp']);
|
|
?>
|
|
<div style="background: var(--bg-card); border-radius: 12px; border: 1px solid var(--glass-border); overflow: hidden; position: relative;">
|
|
<a href="watch.php?id=<?= $v['id'] ?><?= $v['video_timestamp'] > 0 ? '&t='.$v['video_timestamp'] : '' ?>" style="text-decoration: none; color: inherit;">
|
|
<div style="height: 150px; background-image: url('<?= $v['thumbnail_url'] ?: 'assets/images/default_thumb.png' ?>'); background-size: cover; background-position: center; position: relative;">
|
|
<?php if ($time_str): ?>
|
|
<div style="position: absolute; bottom: 8px; right: 8px; background: rgba(0,0,0,0.8); color: white; padding: 2px 6px; border-radius: 4px; font-size: 0.75rem;">
|
|
At <?= $time_str ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
<div style="padding: 12px;">
|
|
<h4 style="margin-bottom: 4px; font-size: 0.95rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"><?= htmlspecialchars($v['title']) ?></h4>
|
|
<div style="font-size: 0.8rem; color: var(--text-muted);">
|
|
Saved on <?= date('M d, Y', strtotime($v['created_at'])) ?>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
<button onclick="removeBookmark(<?= $v['id'] ?>)" style="position: absolute; top: 8px; right: 8px; background: rgba(255,64,129,0.9); color: white; border: none; width: 28px; height: 28px; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 10px rgba(0,0,0,0.3);">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</section>
|
|
|
|
<script>
|
|
async function removeBookmark(id) {
|
|
if (!confirm('Remove this bookmark?')) return;
|
|
const res = await fetch('api/toggle_bookmark.php', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
|
body: `video_id=${id}`
|
|
});
|
|
const data = await res.json();
|
|
if (data.success) {
|
|
location.reload();
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<!-- Security Section -->
|
|
<section id="security">
|
|
<h2 style="margin-bottom: 24px;">Security</h2>
|
|
<div style="background: var(--bg-card); padding: 32px; border-radius: 16px; border: 1px solid var(--glass-border);">
|
|
<h3>Change Password</h3>
|
|
<p style="color: var(--text-muted); margin-bottom: 24px; font-size: 0.9rem;">Keep your account secure by using a strong password.</p>
|
|
|
|
<?php if ($success): ?>
|
|
<div style="background: rgba(76,175,80,0.1); color: #4caf50; padding: 12px; border-radius: 8px; margin-bottom: 20px; border: 1px solid rgba(76,175,80,0.2);">
|
|
<?= $success ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($error): ?>
|
|
<div style="background: rgba(255,64,129,0.1); color: #ff4081; padding: 12px; border-radius: 8px; margin-bottom: 20px; border: 1px solid rgba(255,64,129,0.2);">
|
|
<?= $error ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<form method="POST">
|
|
<input type="hidden" name="change_password" value="1">
|
|
<div class="form-group">
|
|
<label class="form-label">Current Password</label>
|
|
<input type="password" name="old_password" class="form-control" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">New Password</label>
|
|
<input type="password" name="new_password" class="form-control" required minlength="6">
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">Confirm New Password</label>
|
|
<input type="password" name="confirm_password" class="form-control" required minlength="6">
|
|
</div>
|
|
<button type="submit" class="btn btn-primary" style="margin-top: 12px;">Update Password</button>
|
|
</form>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
|
|
<?php require_once 'includes/footer.php'; ?>
|