From 82ce2594ac761a83ac5f3819c072bc741a1989d3 Mon Sep 17 00:00:00 2001 From: jbergner Date: Wed, 1 Jan 2025 23:30:28 +0100 Subject: [PATCH] init --- .gitignore | 3 + README.md | 3 +- config.php.example | 10 ++ license | 21 +++ public/images/.gitignore | 2 + public/index.php | 234 +++++++++++++++++++++++++++++ public/styles.css | 82 ++++++++++ public/templates/404.php | 13 ++ public/templates/categories.php | 18 +++ public/templates/category.php | 28 ++++ public/templates/shared/footer.php | 18 +++ public/templates/shared/header.php | 12 ++ public/thumbs/.gitignore | 2 + 13 files changed, 444 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 config.php.example create mode 100644 license create mode 100644 public/images/.gitignore create mode 100644 public/index.php create mode 100644 public/styles.css create mode 100644 public/templates/404.php create mode 100644 public/templates/categories.php create mode 100644 public/templates/category.php create mode 100644 public/templates/shared/footer.php create mode 100644 public/templates/shared/header.php create mode 100644 public/thumbs/.gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6dad750 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +.DS_Store +config.php \ No newline at end of file diff --git a/README.md b/README.md index d062874..33c0f0f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1 @@ -# docker_image_gallery - +# docker_image_gallery \ No newline at end of file diff --git a/config.php.example b/config.php.example new file mode 100644 index 0000000..3e99800 --- /dev/null +++ b/config.php.example @@ -0,0 +1,10 @@ + 'http://localhost:8080', + 'title' => 'Danb Gallery', + 'author_text' => 'Dan Brown', + 'author_link' => 'https://danb.me', + 'license_link' => 'https://creativecommons.org/publicdomain/zero/1.0/', + 'license_text' => 'CC0 - Public Domain', +]; \ No newline at end of file diff --git a/license b/license new file mode 100644 index 0000000..3bf2f7a --- /dev/null +++ b/license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Dan Brown + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/public/images/.gitignore b/public/images/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/public/images/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..51949c1 --- /dev/null +++ b/public/index.php @@ -0,0 +1,234 @@ + 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, + ) {} +} \ No newline at end of file diff --git a/public/styles.css b/public/styles.css new file mode 100644 index 0000000..aa21016 --- /dev/null +++ b/public/styles.css @@ -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; +} \ No newline at end of file diff --git a/public/templates/404.php b/public/templates/404.php new file mode 100644 index 0000000..0867fcb --- /dev/null +++ b/public/templates/404.php @@ -0,0 +1,13 @@ + + +

Not Found

+ +

+ Sorry, Nothing could be found at the requested + + path; +

diff --git a/public/templates/categories.php b/public/templates/categories.php new file mode 100644 index 0000000..b1287ef --- /dev/null +++ b/public/templates/categories.php @@ -0,0 +1,18 @@ + + + diff --git a/public/templates/category.php b/public/templates/category.php new file mode 100644 index 0000000..c10dfa7 --- /dev/null +++ b/public/templates/category.php @@ -0,0 +1,28 @@ + + +
+ Back + | +

Category:

+
+ + diff --git a/public/templates/shared/footer.php b/public/templates/shared/footer.php new file mode 100644 index 0000000..ec3d8db --- /dev/null +++ b/public/templates/shared/footer.php @@ -0,0 +1,18 @@ + + + + + \ No newline at end of file diff --git a/public/templates/shared/header.php b/public/templates/shared/header.php new file mode 100644 index 0000000..ae53856 --- /dev/null +++ b/public/templates/shared/header.php @@ -0,0 +1,12 @@ + + + + + + + + <?php echo $config['title']; ?> + + + + \ No newline at end of file diff --git a/public/thumbs/.gitignore b/public/thumbs/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/public/thumbs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file