admin panel and password protection
This commit is contained in:
101
admin.php
Normal file
101
admin.php
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Path to password storage
|
||||||
|
$passwordFile = __DIR__ . '/passwords.json';
|
||||||
|
if (!file_exists($passwordFile)) {
|
||||||
|
file_put_contents($passwordFile, '{}');
|
||||||
|
}
|
||||||
|
$passwords = json_decode(file_get_contents($passwordFile), true);
|
||||||
|
|
||||||
|
// Simple admin login (hardcoded for demo)
|
||||||
|
$adminPassword = 'admin123';
|
||||||
|
$loggedIn = isset($_SESSION['admin']) && $_SESSION['admin'] === true;
|
||||||
|
|
||||||
|
if (isset($_POST['admin_login'])) {
|
||||||
|
if ($_POST['admin_password'] === $adminPassword) {
|
||||||
|
$_SESSION['admin'] = true;
|
||||||
|
$loggedIn = true;
|
||||||
|
} else {
|
||||||
|
$error = 'Incorrect admin password.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($loggedIn && isset($_POST['set_album_password'])) {
|
||||||
|
$album = $_POST['album_name'];
|
||||||
|
$pw = $_POST['album_password'];
|
||||||
|
if ($album && $pw !== null) {
|
||||||
|
$passwords[$album] = $pw;
|
||||||
|
file_put_contents($passwordFile, json_encode($passwords));
|
||||||
|
$success = "Password set for album '$album'.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['logout'])) {
|
||||||
|
session_destroy();
|
||||||
|
header('Location: admin.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get album list
|
||||||
|
$dir = __DIR__ . '/images/';
|
||||||
|
$albums = [];
|
||||||
|
if (is_dir($dir)) {
|
||||||
|
foreach (scandir($dir) as $album) {
|
||||||
|
if ($album === '.' || $album === '..' || !is_dir($dir . $album)) continue;
|
||||||
|
$albums[] = $album;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Admin - Album Passwords</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; background: #f0f0f0; }
|
||||||
|
.container { max-width: 500px; margin: 40px auto; background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.12); }
|
||||||
|
h2 { text-align: center; }
|
||||||
|
label { font-weight: bold; }
|
||||||
|
input, select { width: 100%; padding: 8px; margin: 8px 0 16px 0; border-radius: 4px; border: 1px solid #ccc; }
|
||||||
|
button { padding: 8px 16px; background: #0078d4; color: #fff; border: none; border-radius: 4px; font-weight: bold; cursor: pointer; }
|
||||||
|
.msg { color: green; }
|
||||||
|
.error { color: red; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h2>Administrator Mode</h2>
|
||||||
|
<?php if (!$loggedIn): ?>
|
||||||
|
<form method="post">
|
||||||
|
<label for="admin_password">Admin Password:</label>
|
||||||
|
<input type="password" name="admin_password" id="admin_password" required>
|
||||||
|
<button type="submit" name="admin_login">Login</button>
|
||||||
|
<?php if (isset($error)) echo "<div class='error'>$error</div>"; ?>
|
||||||
|
</form>
|
||||||
|
<?php else: ?>
|
||||||
|
<form method="post">
|
||||||
|
<label for="album_name">Select Album:</label>
|
||||||
|
<select name="album_name" id="album_name" required>
|
||||||
|
<?php foreach ($albums as $album): ?>
|
||||||
|
<option value="<?= htmlspecialchars($album) ?>"><?= htmlspecialchars($album) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<label for="album_password">Set/View Password:</label>
|
||||||
|
<input type="text" name="album_password" id="album_password" required>
|
||||||
|
<button type="submit" name="set_album_password">Set Password</button>
|
||||||
|
</form>
|
||||||
|
<?php if (isset($success)) echo "<div class='msg'>$success</div>"; ?>
|
||||||
|
<form method="post" style="margin-top:16px;">
|
||||||
|
<button type="submit" name="logout">Logout</button>
|
||||||
|
</form>
|
||||||
|
<h3>Current Album Passwords:</h3>
|
||||||
|
<ul>
|
||||||
|
<?php foreach ($passwords as $album => $pw): ?>
|
||||||
|
<li><strong><?= htmlspecialchars($album) ?>:</strong> <?= htmlspecialchars($pw) ?></li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
102
index.html
102
index.html
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Christiana Varner - Portland State University - Graduation 2025</title>
|
<title>Album Viewer</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
@@ -24,7 +24,9 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1 style="text-align:center;">Christiana Varner - Portland State University - Graduation 2025</h1>
|
|
||||||
|
<h1 style="text-align:center;">Album Viewer</h1>
|
||||||
|
<!-- Album selector removed. Album is chosen via URL parameter. -->
|
||||||
<a href="Download_All_Photos.zip" id="download-all" style="position:fixed;top:20px;left:20px;z-index:1000;display:inline-block;padding:8px 16px;background:#0078d4;color:#fff;text-decoration:none;border-radius:4px;font-weight:bold;">Download All</a>
|
<a href="Download_All_Photos.zip" id="download-all" style="position:fixed;top:20px;left:20px;z-index:1000;display:inline-block;padding:8px 16px;background:#0078d4;color:#fff;text-decoration:none;border-radius:4px;font-weight:bold;">Download All</a>
|
||||||
<button id="select-images" style="position:fixed;top:20px;left:160px;z-index:1000;display:inline-block;padding:8px 16px;background:#0078d4;color:#fff;border:none;border-radius:4px;font-weight:bold;cursor:pointer;">Select</button>
|
<button id="select-images" style="position:fixed;top:20px;left:160px;z-index:1000;display:inline-block;padding:8px 16px;background:#0078d4;color:#fff;border:none;border-radius:4px;font-weight:bold;cursor:pointer;">Select</button>
|
||||||
<div class="gallery" id="gallery"></div>
|
<div class="gallery" id="gallery"></div>
|
||||||
@@ -33,16 +35,19 @@
|
|||||||
const gallery = document.getElementById('gallery');
|
const gallery = document.getElementById('gallery');
|
||||||
const selectBtn = document.getElementById('select-images');
|
const selectBtn = document.getElementById('select-images');
|
||||||
let downloadBtn = null;
|
let downloadBtn = null;
|
||||||
|
let albums = {};
|
||||||
|
|
||||||
fetch('list-images.php')
|
function getQueryParam(name) {
|
||||||
.then(response => response.json())
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
.then(images => {
|
return urlParams.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderGallery(images) {
|
||||||
|
gallery.innerHTML = '';
|
||||||
images.forEach((imgObj, idx) => {
|
images.forEach((imgObj, idx) => {
|
||||||
// Container for image and checkbox
|
|
||||||
const itemDiv = document.createElement('div');
|
const itemDiv = document.createElement('div');
|
||||||
itemDiv.style.position = 'relative';
|
itemDiv.style.position = 'relative';
|
||||||
|
|
||||||
// Checkbox (hidden by default)
|
|
||||||
const checkbox = document.createElement('input');
|
const checkbox = document.createElement('input');
|
||||||
checkbox.type = 'checkbox';
|
checkbox.type = 'checkbox';
|
||||||
checkbox.className = 'img-checkbox';
|
checkbox.className = 'img-checkbox';
|
||||||
@@ -52,7 +57,6 @@
|
|||||||
checkbox.value = imgObj.full;
|
checkbox.value = imgObj.full;
|
||||||
checkbox.style.display = 'none';
|
checkbox.style.display = 'none';
|
||||||
|
|
||||||
// Image link
|
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = imgObj.full;
|
link.href = imgObj.full;
|
||||||
link.target = '_blank';
|
link.target = '_blank';
|
||||||
@@ -61,11 +65,8 @@
|
|||||||
img.alt = imgObj.full;
|
img.alt = imgObj.full;
|
||||||
link.appendChild(img);
|
link.appendChild(img);
|
||||||
|
|
||||||
// Make image clickable for selection in select mode
|
|
||||||
itemDiv.addEventListener('click', function(e) {
|
itemDiv.addEventListener('click', function(e) {
|
||||||
// Only toggle if in select mode (checkboxes visible)
|
|
||||||
if (checkbox.style.display === 'block') {
|
if (checkbox.style.display === 'block') {
|
||||||
// Prevent link from opening when selecting
|
|
||||||
if (e.target === img || e.target === itemDiv) {
|
if (e.target === img || e.target === itemDiv) {
|
||||||
checkbox.checked = !checkbox.checked;
|
checkbox.checked = !checkbox.checked;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -77,16 +78,87 @@
|
|||||||
itemDiv.appendChild(link);
|
itemDiv.appendChild(link);
|
||||||
gallery.appendChild(itemDiv);
|
gallery.appendChild(itemDiv);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch('list-images.php')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
albums = data;
|
||||||
|
const albumName = getQueryParam('album');
|
||||||
|
if (!albumName) {
|
||||||
|
// Landing page: show album links
|
||||||
|
gallery.innerHTML = '<div style="text-align:center;padding:40px 0;"><h2>Welcome!</h2><p>Select an album to view photos:</p></div>';
|
||||||
|
const albumList = document.createElement('div');
|
||||||
|
albumList.style.display = 'flex';
|
||||||
|
albumList.style.flexWrap = 'wrap';
|
||||||
|
albumList.style.justifyContent = 'center';
|
||||||
|
albumList.style.gap = '20px';
|
||||||
|
Object.keys(albums).forEach(album => {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = `index.html?album=${encodeURIComponent(album)}`;
|
||||||
|
link.textContent = album + (albums[album].protected ? ' 🔒' : '');
|
||||||
|
link.style.display = 'inline-block';
|
||||||
|
link.style.padding = '16px 32px';
|
||||||
|
link.style.background = '#0078d4';
|
||||||
|
link.style.color = '#fff';
|
||||||
|
link.style.borderRadius = '8px';
|
||||||
|
link.style.fontWeight = 'bold';
|
||||||
|
link.style.fontSize = '20px';
|
||||||
|
link.style.textDecoration = 'none';
|
||||||
|
link.style.boxShadow = '0 2px 8px rgba(0,0,0,0.12)';
|
||||||
|
albumList.appendChild(link);
|
||||||
|
});
|
||||||
|
gallery.appendChild(albumList);
|
||||||
|
selectBtn.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!albums[albumName]) {
|
||||||
|
gallery.innerHTML = '<p style="color:red;">Album not found. Please specify a valid album in the URL (e.g., ?album=Album1).</p>';
|
||||||
|
selectBtn.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Check if album is protected
|
||||||
|
if (albums[albumName].protected) {
|
||||||
|
let password = localStorage.getItem('album_pw_' + albumName) || '';
|
||||||
|
function requestImages(pw) {
|
||||||
|
fetch(`list-images.php?album=${encodeURIComponent(albumName)}&password=${encodeURIComponent(pw)}`)
|
||||||
|
.then(resp => {
|
||||||
|
if (resp.status === 403) {
|
||||||
|
throw new Error('Password required');
|
||||||
|
}
|
||||||
|
return resp.json();
|
||||||
|
})
|
||||||
|
.then(images => {
|
||||||
|
renderGallery(images);
|
||||||
|
selectBtn.style.display = 'inline-block';
|
||||||
|
localStorage.setItem('album_pw_' + albumName, pw);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
gallery.innerHTML = `<div style='text-align:center;padding:40px 0;'><h2>Password Required</h2><form id='pwform'><input type='password' id='album_pw' placeholder='Enter album password' style='padding:8px;font-size:18px;border-radius:4px;border:1px solid #ccc;width:220px;'><button type='submit' style='margin-left:12px;padding:8px 16px;background:#0078d4;color:#fff;border:none;border-radius:4px;font-weight:bold;cursor:pointer;'>View Album</button></form><div id='pwerror' style='color:red;margin-top:12px;'></div></div>`;
|
||||||
|
selectBtn.style.display = 'none';
|
||||||
|
document.getElementById('pwform').onsubmit = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const pwTry = document.getElementById('album_pw').value;
|
||||||
|
requestImages(pwTry);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
requestImages(password);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Not protected, fetch images
|
||||||
|
fetch(`list-images.php?album=${encodeURIComponent(albumName)}`)
|
||||||
|
.then(resp => resp.json())
|
||||||
|
.then(images => {
|
||||||
|
renderGallery(images);
|
||||||
|
selectBtn.style.display = 'inline-block';
|
||||||
|
});
|
||||||
|
|
||||||
// Add select button logic
|
|
||||||
selectBtn.addEventListener('click', () => {
|
selectBtn.addEventListener('click', () => {
|
||||||
// Show checkboxes
|
|
||||||
document.querySelectorAll('.img-checkbox').forEach(cb => {
|
document.querySelectorAll('.img-checkbox').forEach(cb => {
|
||||||
cb.style.display = 'block';
|
cb.style.display = 'block';
|
||||||
});
|
});
|
||||||
// Hide select button
|
|
||||||
selectBtn.style.display = 'none';
|
selectBtn.style.display = 'none';
|
||||||
// Create and show download selected button
|
|
||||||
downloadBtn = document.createElement('button');
|
downloadBtn = document.createElement('button');
|
||||||
downloadBtn.id = 'download-selected';
|
downloadBtn.id = 'download-selected';
|
||||||
downloadBtn.textContent = 'Download Selected';
|
downloadBtn.textContent = 'Download Selected';
|
||||||
|
|||||||
@@ -1,24 +1,57 @@
|
|||||||
<?php
|
<?php
|
||||||
$dir = __DIR__ . '/images/';
|
|
||||||
$thumbDir = $dir . 'thumbnails/';
|
|
||||||
$extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'];
|
|
||||||
$images = [];
|
|
||||||
|
|
||||||
if (is_dir($dir)) {
|
|
||||||
foreach (scandir($dir) as $file) {
|
$dir = __DIR__ . '/images/';
|
||||||
|
$extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'];
|
||||||
|
$albums = [];
|
||||||
|
$passwordFile = __DIR__ . '/passwords.json';
|
||||||
|
$passwords = file_exists($passwordFile) ? json_decode(file_get_contents($passwordFile), true) : [];
|
||||||
|
|
||||||
|
// If album is requested, check password
|
||||||
|
if (isset($_GET['album'])) {
|
||||||
|
$album = $_GET['album'];
|
||||||
|
$pw = isset($_GET['password']) ? $_GET['password'] : null;
|
||||||
|
if (!is_dir($dir . $album)) {
|
||||||
|
http_response_code(404);
|
||||||
|
echo json_encode(['error' => 'Album not found']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if (isset($passwords[$album]) && $passwords[$album] !== '') {
|
||||||
|
if ($pw !== $passwords[$album]) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(['error' => 'Password required']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$albumImages = [];
|
||||||
|
$thumbDir = $dir . $album . '/thumbnails/';
|
||||||
|
$albumDir = $dir . $album . '/';
|
||||||
|
foreach (scandir($albumDir) as $file) {
|
||||||
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
|
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
|
||||||
if (in_array($ext, $extensions)) {
|
if (in_array($ext, $extensions)) {
|
||||||
$thumbPath = 'images/thumbnails/' . $file;
|
$thumbPath = 'images/' . $album . '/thumbnails/' . $file;
|
||||||
$fullPath = 'images/' . $file;
|
$fullPath = 'images/' . $album . '/' . $file;
|
||||||
// Check if thumbnail exists
|
|
||||||
if (file_exists($thumbDir . $file)) {
|
if (file_exists($thumbDir . $file)) {
|
||||||
$images[] = ['thumb' => $thumbPath, 'full' => $fullPath];
|
$albumImages[] = ['thumb' => $thumbPath, 'full' => $fullPath];
|
||||||
} else {
|
} else {
|
||||||
$images[] = ['thumb' => $fullPath, 'full' => $fullPath];
|
$albumImages[] = ['thumb' => $fullPath, 'full' => $fullPath];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
echo json_encode($images);
|
echo json_encode($albumImages);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, return album list (for landing page)
|
||||||
|
if (is_dir($dir)) {
|
||||||
|
foreach (scandir($dir) as $album) {
|
||||||
|
if ($album === '.' || $album === '..' || !is_dir($dir . $album)) continue;
|
||||||
|
$albums[$album] = [
|
||||||
|
'protected' => isset($passwords[$album]) && $passwords[$album] !== '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode($albums);
|
||||||
?>
|
?>
|
||||||
1
passwords.json
Normal file
1
passwords.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
Reference in New Issue
Block a user