This commit is contained in:
2025-01-01 23:30:28 +01:00
parent 1106b4d942
commit 82ce2594ac
13 changed files with 444 additions and 2 deletions

2
public/images/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

234
public/index.php Normal file
View File

@@ -0,0 +1,234 @@
<?php
declare(strict_types=1);
//ini_set('display_errors', '1');
//error_reporting(E_ALL);
// Global constants
const IMAGES_DIR = __DIR__ . DIRECTORY_SEPARATOR . 'images';
const THUMBS_DIR = __DIR__ . DIRECTORY_SEPARATOR . 'thumbs';
// Load configuration
$config = loadConfig();
// Path management
$uri = $_SERVER['REQUEST_URI'];
$routes = [
'categories' => function() {
sendPage("categories", [
'categories' => getCategories(),
]);
},
'category' => function(string $category) {
sendPage("category", [
'category' => $category,
'images' => getCategoryImages($category),
]);
},
'404' => function(string $uri) {
sendPage("404", [
'uri' => $uri,
], 404);
}
];
if ($uri === '/') {
$routes['categories']();
} else {
$category = urldecode(trim($uri, '/'));
if (categoryExists($category)) {
$routes['category']($category);
} else {
$routes['404']($uri);
}
}
/**
* Get details of all available categories.
* @returns Category[]
*/
function getCategories(): array {
$names = getCategoryFolderNames();
$categories = [];
foreach ($names as $name) {
$category = new Category(
name: $name,
thumb: getCategoryThumbnail($name)
);
if ($category->thumb) {
$categories[] = $category;
}
}
return $categories;
}
/**
* Get the thumbnail image uri for the given category.
*/
function getCategoryThumbnail(string $category): string {
$categoryImages = getCategoryImageFiles($category);
$firstImage = $categoryImages[0] ?? '';
if (empty($firstImage)) {
return '';
}
generateImageThumbnail($category, $firstImage);
return "thumbs/{$category}/{$firstImage}";
}
/**
* Generated a thumbnail for the given image filename withing
* the given category folder.
*/
function generateImageThumbnail(string $category, string $image): void {
$imagePath = buildPath(IMAGES_DIR, $category, $image);
$thumbPath = buildPath(THUMBS_DIR, $category, $image);
if (file_exists($thumbPath)) {
return;
}
if (!file_exists($imagePath)) {
error("Could not find image at {$imagePath}");
}
if (!str_ends_with(strtolower($imagePath), '.webp')) {
error("Image at {$imagePath} is not webp as expected");
}
$thumbDir = dirname($thumbPath);
if (!file_exists($thumbDir)) {
mkdir($thumbDir);
}
$originalImage = imagecreatefromwebp($imagePath);
$thumbImage = imagescale($originalImage, 1200);
imagewebp($thumbImage, $thumbPath, 50);
}
/**
* Get the categorized folder names within the image directory.
* @return string[]
*/
function getCategoryFolderNames(): array {
$dirs = glob(buildPath(IMAGES_DIR, '*'), GLOB_ONLYDIR);
$dirNames = array_map(fn(string $dir) => basename($dir), $dirs);
return array_reverse($dirNames);
}
/**
* Check that a given category exists.
*/
function categoryExists(string $category): bool {
$expectedPath = buildPath(IMAGES_DIR, $category);
return is_dir($expectedPath);
}
/**
* Get the image filenames for the images within the given
* category folder.
* @return string[]
*/
function getCategoryImageFiles(string $category): array {
$images = glob(buildPath(IMAGES_DIR, $category, '*.webp'));
return array_map(fn(string $dir) => basename($dir), $images);
}
/**
* Get the images within the given category.
* @return Image[]
*/
function getCategoryImages(string $category): array {
$files = getCategoryImageFiles($category);
$images = array_map(function(string $file) use ($category) {
$imagePath = buildPath('images', $category, $file);
[$width, $height] = getimagesize($imagePath);
return new Image(
name: $file,
width: $width,
height: $height,
uri: "./images/{$category}/{$file}",
thumb: "./thumbs/{$category}/{$file}"
);
}, $files);
foreach ($files as $fileName) {
generateImageThumbnail($category, $fileName);
}
return $images;
}
/**
* Build a directory path from the given path parts.
*/
function buildPath(...$parts): string {
return implode(DIRECTORY_SEPARATOR, $parts);
}
/**
* Render and send the page of the given name to the user.
*/
function sendPage(string $name, array $data = [], int $status = 200): void {
global $config;
$mergedData = array_merge($data, ['config' => $config]);
extract($mergedData);
header('Content-Type: text/html; charset=utf-8');
http_response_code($status);
include "templates/shared/header.php";
include "templates/{$name}.php";
include "templates/shared/footer.php";
}
/**
* Load the config file from the parent directory.
*/
function loadConfig(): array {
$configPath = buildPath(dirname(__DIR__), 'config.php');
$config = include $configPath;
return $config;
}
/**
* Error out stop the application, showing the given message.
*/
function error(string $message): never {
echo "An error occurred: {$message}";
http_response_code(500);
exit(1);
}
/**
* Dump the given arguments and exit.
* (Dump & die)
*/
function dd(...$args): never {
foreach ($args as $arg) {
print_r($arg);
}
exit(1);
}
class Category {
public function __construct(
public string $name,
public string $thumb
) {}
}
class Image {
public function __construct(
public string $name,
public int $width,
public int $height,
public string $uri,
public string $thumb,
) {}
}

82
public/styles.css Normal file
View File

@@ -0,0 +1,82 @@
body {
background: #222;
color: #FFF;
font-family: "Roboto", sans-serif;
font-size: 16px;
margin: 0;
padding: 0;
}
header {
font-size: 1rem;
background: #000;
margin: 0;
display: flex;
gap: 0.5rem;
padding: .5rem;
font-weight: 700;
position: sticky;
top: 0;
z-index: 5;
}
header h1 {
margin: 0;
font-size: 1rem;
}
header a {
color: #FFF;
text-decoration: none;
}
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(480px, 48%), 1fr));
gap: 1px;
}
.gallery-item {
position: relative;
display: block;
text-decoration: none;
color: #FFF;
aspect-ratio: 1/1;
}
.gallery-item img {
position: absolute;
object-fit: cover;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.gallery-item h3 {
position: absolute;
top: 0;
left: 0;
padding: .5rem 1rem;
margin: 0;
font-size: .9rem;
box-shadow: 0 0 2px 0 rgba(0, 0, 0, 1);
background: black;
opacity: 0.7;
}
.gallery-item:hover h3 {
opacity: 1;
}
.gallery-item h3 small {
font-weight: 400;
font-size: .8rem;
}
footer {
display: flex;
gap: 1rem;
padding: 1rem;
justify-content: center;
opacity: .6;
}
footer a {
color: #fff;
text-decoration: underline;
}

13
public/templates/404.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
/**
* @var string $uri
*/
?>
<h1>Not Found</h1>
<p>
Sorry, Nothing could be found at the requested
<?php echo $uri; ?>
path;
</p>

View File

@@ -0,0 +1,18 @@
<?php
/**
* @var Category[] $categories
*/
?>
<div class="gallery-grid">
<?php foreach ($categories as $category): ?>
<a href="./<?php echo $category->name; ?>" class="gallery-item">
<img src="<?php echo $category->thumb ?>" alt="<?php echo $category->name; ?>" loading="lazy">
<h3><?php echo $category->name; ?></h3>
</a>
<?php endforeach; ?>
</div>

View File

@@ -0,0 +1,28 @@
<?php
/**
* @var string $category
* @var Image[] $images
*/
?>
<header>
<a href="./">Back</a>
<span>|</span>
<h1>Category: <?php echo $category; ?></h1>
</header>
<div class="gallery-grid">
<?php foreach ($images as $image): ?>
<a href="<?php echo $image->uri; ?>" target="_blank" class="gallery-item">
<img src="<?php echo $image->thumb; ?>" alt="<?php echo $image->name; ?>" loading="lazy">
<h3>
<?php echo $image->name; ?>
<small>[<?php echo $image->width; ?>x<?php echo $image->height; ?>]</small>
</h3>
</a>
<?php endforeach; ?>
</div>

View File

@@ -0,0 +1,18 @@
<footer>
<p>
By <a href="<?php echo $config['author_link']; ?>" target="_blank" rel="noreferrer noopener"><?php echo $config['author_text']; ?></a>
</p>
<p>|</p>
<p>
Images are licensed as
<a href="<?php echo $config['license_link']; ?>" target="_blank" rel="noreferrer noopener"><?php echo $config['license_text']; ?></a>
</p>
<p>|</p>
<p>
<a href="https://github.com/ssddanbrown/gallery" target="_blank" rel="noreferrer noopener">Gallery Source Code</a>
</p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<base href="<?php echo $config['base_url']; ?>">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title><?php echo $config['title']; ?></title>
<link rel="stylesheet" href="styles.css">
</head>
<body>

2
public/thumbs/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore