Initial commit
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
web/public/images/*
|
||||||
|
web/vendor/*
|
||||||
|
web/node_modules/*
|
||||||
|
*.db
|
||||||
2
bin/buildcss
Executable file
2
bin/buildcss
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
docker-compose exec app npm run build:css
|
||||||
2
bin/init_db
Executable file
2
bin/init_db
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
docker-compose exec app php scripts/init_db.php
|
||||||
2
bin/minifyjs
Executable file
2
bin/minifyjs
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
docker-compose exec app npm run minify:js
|
||||||
2
bin/process_images
Executable file
2
bin/process_images
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
docker-compose exec app php scripts/process_images.php
|
||||||
32
docker-compose.yml
Normal file
32
docker-compose.yml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
version: '3.3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: ./php
|
||||||
|
container_name: photo_gallery_php
|
||||||
|
volumes:
|
||||||
|
- ./web:/var/www/html
|
||||||
|
user: "1000:1000"
|
||||||
|
#ports:
|
||||||
|
# - "9000:9000"
|
||||||
|
networks:
|
||||||
|
- photo-gallery-network
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
image: nginx:latest
|
||||||
|
container_name: photo_gallery_nginx
|
||||||
|
volumes:
|
||||||
|
- ./web:/var/www/html
|
||||||
|
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
depends_on:
|
||||||
|
- app
|
||||||
|
networks:
|
||||||
|
- photo-gallery-network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
photo-gallery-network:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
24
nginx/default.conf
Normal file
24
nginx/default.conf
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
root /var/www/html/public;
|
||||||
|
index index.php index.html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.php$ {
|
||||||
|
#include snippets/fastcgi-php.conf;
|
||||||
|
fastcgi_pass app:9000;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
include fastcgi_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.ht {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
23
php/Dockerfile
Normal file
23
php/Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
FROM php:8.2-fpm
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
libfreetype6-dev \
|
||||||
|
libjpeg62-turbo-dev \
|
||||||
|
libpng-dev \
|
||||||
|
libwebp-dev \
|
||||||
|
libsqlite3-dev \
|
||||||
|
&& docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \
|
||||||
|
&& docker-php-ext-install gd \
|
||||||
|
&& docker-php-ext-install mysqli
|
||||||
|
|
||||||
|
# Install Composer
|
||||||
|
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||||
|
|
||||||
|
RUN apt-get install npm vim git -y && \
|
||||||
|
useradd -M -d /var/www -s /usr/sbin/nologin www & \
|
||||||
|
chown -R www /var/www
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /var/www/html
|
||||||
|
|
||||||
5
web/.env
Normal file
5
web/.env
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
DB_FILE_PATH=database/gallery.db
|
||||||
|
PAGINATION_RANGE=7
|
||||||
|
IMG_THUMB_WIDTH=364
|
||||||
|
IMG_THUMB_HEIGHT=364
|
||||||
|
IMG_THUMB_QUALITY=40
|
||||||
12
web/composer.json
Normal file
12
web/composer.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "hpz/photogal",
|
||||||
|
"type": "project",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Hpz\\Photogal\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"vlucas/phpdotenv": "^5.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
479
web/composer.lock
generated
Normal file
479
web/composer.lock
generated
Normal file
@@ -0,0 +1,479 @@
|
|||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"content-hash": "328949c3f92d87a4170180a4a7e683fe",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "graham-campbell/result-type",
|
||||||
|
"version": "v1.1.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/GrahamCampbell/Result-Type.git",
|
||||||
|
"reference": "3ba905c11371512af9d9bdd27d99b782216b6945"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945",
|
||||||
|
"reference": "3ba905c11371512af9d9bdd27d99b782216b6945",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2.5 || ^8.0",
|
||||||
|
"phpoption/phpoption": "^1.9.3"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"GrahamCampbell\\ResultType\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "An Implementation Of The Result Type",
|
||||||
|
"keywords": [
|
||||||
|
"Graham Campbell",
|
||||||
|
"GrahamCampbell",
|
||||||
|
"Result Type",
|
||||||
|
"Result-Type",
|
||||||
|
"result"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/GrahamCampbell/Result-Type/issues",
|
||||||
|
"source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-07-20T21:45:45+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "phpoption/phpoption",
|
||||||
|
"version": "1.9.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/schmittjoh/php-option.git",
|
||||||
|
"reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54",
|
||||||
|
"reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2.5 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
|
"phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"bamarni-bin": {
|
||||||
|
"bin-links": true,
|
||||||
|
"forward-command": false
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.9-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PhpOption\\": "src/PhpOption/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"Apache-2.0"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Johannes M. Schmitt",
|
||||||
|
"email": "schmittjoh@gmail.com",
|
||||||
|
"homepage": "https://github.com/schmittjoh"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Option Type for PHP",
|
||||||
|
"keywords": [
|
||||||
|
"language",
|
||||||
|
"option",
|
||||||
|
"php",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/schmittjoh/php-option/issues",
|
||||||
|
"source": "https://github.com/schmittjoh/php-option/tree/1.9.3"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-07-20T21:41:07+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-ctype",
|
||||||
|
"version": "v1.30.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||||
|
"reference": "0424dff1c58f028c451efff2045f5d92410bd540"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540",
|
||||||
|
"reference": "0424dff1c58f028c451efff2045f5d92410bd540",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.1"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"ext-ctype": "*"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-ctype": "For best performance"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"thanks": {
|
||||||
|
"name": "symfony/polyfill",
|
||||||
|
"url": "https://github.com/symfony/polyfill"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Ctype\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Gert de Pagter",
|
||||||
|
"email": "BackEndTea@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill for ctype functions",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"ctype",
|
||||||
|
"polyfill",
|
||||||
|
"portable"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-05-31T15:07:36+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-mbstring",
|
||||||
|
"version": "v1.30.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
|
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c",
|
||||||
|
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.1"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"ext-mbstring": "*"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-mbstring": "For best performance"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"thanks": {
|
||||||
|
"name": "symfony/polyfill",
|
||||||
|
"url": "https://github.com/symfony/polyfill"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nicolas Grekas",
|
||||||
|
"email": "p@tchwork.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill for the Mbstring extension",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"mbstring",
|
||||||
|
"polyfill",
|
||||||
|
"portable",
|
||||||
|
"shim"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-06-19T12:30:46+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-php80",
|
||||||
|
"version": "v1.30.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||||
|
"reference": "77fa7995ac1b21ab60769b7323d600a991a90433"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433",
|
||||||
|
"reference": "77fa7995ac1b21ab60769b7323d600a991a90433",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.1"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"thanks": {
|
||||||
|
"name": "symfony/polyfill",
|
||||||
|
"url": "https://github.com/symfony/polyfill"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Php80\\": ""
|
||||||
|
},
|
||||||
|
"classmap": [
|
||||||
|
"Resources/stubs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Ion Bazan",
|
||||||
|
"email": "ion.bazan@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Nicolas Grekas",
|
||||||
|
"email": "p@tchwork.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"polyfill",
|
||||||
|
"portable",
|
||||||
|
"shim"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-05-31T15:07:36+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vlucas/phpdotenv",
|
||||||
|
"version": "v5.6.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/vlucas/phpdotenv.git",
|
||||||
|
"reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2",
|
||||||
|
"reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-pcre": "*",
|
||||||
|
"graham-campbell/result-type": "^1.1.3",
|
||||||
|
"php": "^7.2.5 || ^8.0",
|
||||||
|
"phpoption/phpoption": "^1.9.3",
|
||||||
|
"symfony/polyfill-ctype": "^1.24",
|
||||||
|
"symfony/polyfill-mbstring": "^1.24",
|
||||||
|
"symfony/polyfill-php80": "^1.24"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
|
"ext-filter": "*",
|
||||||
|
"phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-filter": "Required to use the boolean validator."
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"bamarni-bin": {
|
||||||
|
"bin-links": true,
|
||||||
|
"forward-command": false
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "5.6-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Dotenv\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Vance Lucas",
|
||||||
|
"email": "vance@vancelucas.com",
|
||||||
|
"homepage": "https://github.com/vlucas"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
|
||||||
|
"keywords": [
|
||||||
|
"dotenv",
|
||||||
|
"env",
|
||||||
|
"environment"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/vlucas/phpdotenv/issues",
|
||||||
|
"source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-07-20T21:52:34+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": [],
|
||||||
|
"platform-dev": [],
|
||||||
|
"plugin-api-version": "2.6.0"
|
||||||
|
}
|
||||||
1496
web/package-lock.json
generated
Normal file
1496
web/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
19
web/package.json
Normal file
19
web/package.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "html",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build:css": "npx tailwindcss -i ./src/styles/tailwind.css -o ./public/css/styles.css --minify",
|
||||||
|
"minify:js": "terser ./src/js/app.js -o ./public/js/app.min.js --compress"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"autoprefixer": "^10.4.20",
|
||||||
|
"postcss": "^8.4.41",
|
||||||
|
"tailwindcss": "^3.4.10",
|
||||||
|
"terser": "^5.31.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
8
web/postcss.config.js
Normal file
8
web/postcss.config.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// postcss.config.js
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
1
web/public/css/styles.css
Normal file
1
web/public/css/styles.css
Normal file
File diff suppressed because one or more lines are too long
100
web/public/gallery.php
Normal file
100
web/public/gallery.php
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
// public/gallery.php
|
||||||
|
|
||||||
|
use Hpz\Photogal\Database;
|
||||||
|
use Hpz\Photogal\Image;
|
||||||
|
|
||||||
|
require_once '../vendor/autoload.php';
|
||||||
|
|
||||||
|
$dotenv = Dotenv\Dotenv::createImmutable('../');
|
||||||
|
$dotenv->load();
|
||||||
|
if (!isset($_ENV['DB_FILE_PATH'])) {
|
||||||
|
die('DB_FILE_PATH is not set in the .env file');
|
||||||
|
}
|
||||||
|
if (!file_exists('../' . $_ENV['DB_FILE_PATH'])) {
|
||||||
|
die('Database file does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the selected album
|
||||||
|
$album = $_GET['album'] ?? '';
|
||||||
|
$album = urldecode($album);
|
||||||
|
|
||||||
|
if (empty($album)) {
|
||||||
|
header("Location: index.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pagination settings
|
||||||
|
$limit = 24; // Number of images per page
|
||||||
|
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||||
|
$offset = ($page - 1) * $limit;
|
||||||
|
|
||||||
|
|
||||||
|
// Create a new Database connection
|
||||||
|
$dbFilePath = '../' . $_ENV['DB_FILE_PATH'];
|
||||||
|
$database = new Database($dbFilePath);
|
||||||
|
$image = new Image($database);
|
||||||
|
|
||||||
|
// Get images and total count for the selected album
|
||||||
|
$images = $image->getImages($album, $limit, $offset);
|
||||||
|
$totalImages = $image->countImages($album);
|
||||||
|
$totalPages = ceil($totalImages / $limit);
|
||||||
|
|
||||||
|
// Limit the number of pages displayed in the pagination
|
||||||
|
$paginationRange = $_ENV['PAGINATION_RANGE'] ?? 5;
|
||||||
|
$startPage = max(1, $page - floor($paginationRange / 2));
|
||||||
|
$endPage = min($totalPages, $startPage + $paginationRange - 1);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Photo Gallery - <?= htmlspecialchars($album) ?></title>
|
||||||
|
<link href="/css/styles.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-100">
|
||||||
|
<div class="container mx-auto p-4">
|
||||||
|
<h1 class="text-3xl font-bold mb-4"><?= htmlspecialchars($album) ?> - Gallery</h1>
|
||||||
|
<a href="index.php" class="text-blue-500 mb-4 inline-block">← Back to Albums</a>
|
||||||
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||||
|
<?php foreach ($images as $image): ?>
|
||||||
|
<div class="relative bg-white shadow-md rounded-lg overflow-hidden group">
|
||||||
|
<img src="images/<?= $album ?>/thumbs/<?= $image['title'] ?>.webp" alt="<?= htmlspecialchars($image['title']) ?>" class="w-full h-full object-cover cursor-pointer" data-big="images/<?= $album ?>/big/<?= $image['title'] ?>.webp" data-original="images/<?= $album ?>/photos/<?= $image['filename'] ?>" onclick="openLightbox(this)">
|
||||||
|
<div class="absolute inset-0 bg-black bg-opacity-50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center title-overlay">
|
||||||
|
<h2 class="text-lg font-semibold text-white"><?= htmlspecialchars($image['title']) ?></h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination Controls -->
|
||||||
|
<div class="mt-6">
|
||||||
|
<?php if ($totalPages > 1): ?>
|
||||||
|
<nav class="flex justify-center">
|
||||||
|
<?php if ($page > 1): ?>
|
||||||
|
<a href="?album=<?= urlencode($album) ?>&page=<?= $page - 1 ?>" class="px-4 py-2 bg-gray-300 rounded-l hover:bg-gray-400">Previous</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php for ($i = $startPage; $i <= $endPage; $i++): ?>
|
||||||
|
<a href="?album=<?= urlencode($album) ?>&page=<?= $i ?>" class="px-4 py-2 <?= $i == $page ? 'bg-gray-500 text-white' : 'bg-gray-300' ?> hover:bg-gray-400"><?= $i ?></a>
|
||||||
|
<?php endfor; ?>
|
||||||
|
|
||||||
|
<?php if ($page < $totalPages): ?>
|
||||||
|
<a href="?album=<?= urlencode($album) ?>&page=<?= $page + 1 ?>" class="px-4 py-2 bg-gray-300 rounded-r hover:bg-gray-400">Next</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</nav>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Lightbox -->
|
||||||
|
<div id="lightbox" class="lightbox" onclick="closeLightbox()">
|
||||||
|
<img id="lightbox-img" src="" alt="Lightbox Image" onwheel="zoomImage(event)">
|
||||||
|
<div id="original-button" class="original-button" onclick="loadOriginal(event)">View Original</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/js/app.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
65
web/public/index.php
Normal file
65
web/public/index.php
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
// public/index.php
|
||||||
|
|
||||||
|
require_once '../vendor/autoload.php';
|
||||||
|
|
||||||
|
use Hpz\Photogal\Database;
|
||||||
|
use Hpz\Photogal\Image;
|
||||||
|
|
||||||
|
$dotenv = Dotenv\Dotenv::createImmutable('../');
|
||||||
|
$dotenv->load();
|
||||||
|
if (!isset($_ENV['DB_FILE_PATH'])) {
|
||||||
|
die('DB_FILE_PATH is not set in the .env file');
|
||||||
|
}
|
||||||
|
if (!file_exists('../' . $_ENV['DB_FILE_PATH'])) {
|
||||||
|
die('Database file does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new Database connection
|
||||||
|
$dbFilePath = '../' . $_ENV['DB_FILE_PATH'];
|
||||||
|
$database = new Database($dbFilePath);
|
||||||
|
$image = new Image($database);
|
||||||
|
|
||||||
|
// Get the list of albums
|
||||||
|
$albumsQuery = "SELECT DISTINCT album FROM images";
|
||||||
|
$albumsResult = $database->getConnection()->query($albumsQuery);
|
||||||
|
$albums = [];
|
||||||
|
while ($row = $albumsResult->fetchArray(SQLITE3_ASSOC)) {
|
||||||
|
$albums[] = $row['album'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the first image from each album
|
||||||
|
$albumPreviews = [];
|
||||||
|
foreach ($albums as $album) {
|
||||||
|
$albumPreviews[$album] = $image->getImages($album, 1, 0)[0] ?? null;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Photo Gallery - Select Album</title>
|
||||||
|
<link href="/css/styles.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-100">
|
||||||
|
<div class="container mx-auto p-4">
|
||||||
|
<h1 class="text-3xl font-bold mb-4">Select an Album</h1>
|
||||||
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||||
|
<?php foreach ($albumPreviews as $album => $image): ?>
|
||||||
|
<?php if ($image): ?>
|
||||||
|
<div class="bg-white shadow-md rounded-lg overflow-hidden">
|
||||||
|
<a href="gallery.php?album=<?= urlencode($album) ?>">
|
||||||
|
<img src="images/<?= $album ?>/thumbs/<?= $image['title'] ?>.webp" alt="<?= htmlspecialchars($album) ?>" class="w-full h-48 object-cover">
|
||||||
|
<div class="p-4">
|
||||||
|
<h2 class="text-xl font-semibold"><?= htmlspecialchars($album) ?></h2>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1
web/public/js/app.min.js
vendored
Normal file
1
web/public/js/app.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
function openLightbox(element){const lightbox=document.getElementById("lightbox"),lightboxImg=document.getElementById("lightbox-img");lightboxImg.src=element.dataset.big,lightboxImg.setAttribute("data-original",element.dataset.original),lightbox.classList.add("active")}function closeLightbox(){document.getElementById("lightbox").classList.remove("active");const lightboxImg=document.getElementById("lightbox-img");lightboxImg.src="",lightboxImg.style.transform="scale(1)"}function zoomImage(event){event.preventDefault();const img=event.target;let scale=img.style.transform?parseFloat(img.style.transform.replace("scale(","").replace(")","")):1;event.deltaY<0?scale+=.1:scale-=.1,scale<1&&(scale=1),img.style.transform=`scale(${scale})`,img.style.transformOrigin=`${event.offsetX}px ${event.offsetY}px`}function loadOriginal(event){event.stopPropagation();const lightboxImg=document.getElementById("lightbox-img"),originalSrc=lightboxImg.getAttribute("data-original");lightboxImg.src=originalSrc,lightboxImg.style.transform="scale(1)"}
|
||||||
28
web/scripts/init_db.php
Normal file
28
web/scripts/init_db.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
// .php
|
||||||
|
|
||||||
|
require_once 'vendor/autoload.php';
|
||||||
|
|
||||||
|
use Hpz\Photogal\Database;
|
||||||
|
use Hpz\Photogal\Image;
|
||||||
|
|
||||||
|
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/..');
|
||||||
|
$dotenv->load();
|
||||||
|
if (!isset($_ENV['DB_FILE_PATH'])) {
|
||||||
|
die('DB_FILE_PATH is not set in the .env file');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify the path to the SQLite database file
|
||||||
|
$dbFilePath = $_ENV['DB_FILE_PATH'];
|
||||||
|
|
||||||
|
// Create a new Database connection
|
||||||
|
$database = new Database($dbFilePath);
|
||||||
|
|
||||||
|
// Create the Image table
|
||||||
|
$image = new Image($database);
|
||||||
|
if ($image->createTable()) {
|
||||||
|
echo "Database and table initialized successfully.";
|
||||||
|
} else {
|
||||||
|
echo "Failed to initialize database and table.";
|
||||||
|
}
|
||||||
|
|
||||||
84
web/scripts/process_images.php
Normal file
84
web/scripts/process_images.php
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
// web/process_images.php
|
||||||
|
|
||||||
|
require_once 'vendor/autoload.php';
|
||||||
|
|
||||||
|
use Hpz\Photogal\Database;
|
||||||
|
use Hpz\Photogal\Image;
|
||||||
|
use Hpz\Photogal\ThumbnailGenerator;
|
||||||
|
|
||||||
|
ini_set('memory_limit', '512M');
|
||||||
|
|
||||||
|
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/..');
|
||||||
|
$dotenv->load();
|
||||||
|
if (!isset($_ENV['DB_FILE_PATH'])) {
|
||||||
|
die('DB_FILE_PATH is not set in the .env file');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify the paths
|
||||||
|
// Specify the paths
|
||||||
|
$albumsDir = 'public/images/';
|
||||||
|
$dbFilePath = $_ENV['DB_FILE_PATH'];
|
||||||
|
|
||||||
|
// Create a new Database connection
|
||||||
|
$database = new Database($dbFilePath);
|
||||||
|
$image = new Image($database);
|
||||||
|
|
||||||
|
// Function to process a directory
|
||||||
|
function processDirectory($albumDir, $albumName, Image $image)
|
||||||
|
{
|
||||||
|
$dirIterator = new RecursiveDirectoryIterator($albumDir);
|
||||||
|
$iterator = new RecursiveIteratorIterator($dirIterator);
|
||||||
|
|
||||||
|
foreach ($iterator as $file) {
|
||||||
|
if ($file->isFile()) {
|
||||||
|
$filePath = $file->getPathname();
|
||||||
|
$fileInfo = pathinfo($filePath);
|
||||||
|
$fileName = $fileInfo['basename'];
|
||||||
|
|
||||||
|
if (in_array(strtolower($fileInfo['extension']), ['jpg', 'jpeg', 'png', 'gif'])) {
|
||||||
|
$thumbDir = 'public/images/' . $albumName . '/thumbs/';
|
||||||
|
$bigDir = 'public/images/' . $albumName . '/big/';
|
||||||
|
$thumbPath = $thumbDir . $fileInfo['filename'] . '.webp';
|
||||||
|
$bigPath = $bigDir . $fileInfo['filename'] . '.webp';
|
||||||
|
|
||||||
|
if (!file_exists($thumbPath)) {
|
||||||
|
if (!is_dir($thumbDir)) {
|
||||||
|
mkdir($thumbDir, 0777, true);
|
||||||
|
}
|
||||||
|
ThumbnailGenerator::createThumbnail($filePath, $thumbPath, $_ENV['IMG_THUMB_WIDTH'], $_ENV['IMG_THUMB_HEIGHT'], $_ENV['IMG_THUMB_QUALITY']);
|
||||||
|
echo "Created thumbnail for $fileName in $albumName\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file_exists($bigPath)) {
|
||||||
|
if (!is_dir($bigDir)) {
|
||||||
|
mkdir($bigDir, 0777, true);
|
||||||
|
}
|
||||||
|
ThumbnailGenerator::createBigImage($filePath, $bigPath);
|
||||||
|
echo "Created big image for $fileName in $albumName\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$relativePath = $albumName . '/photos';
|
||||||
|
$modifiedDate = filemtime($filePath);
|
||||||
|
$addedDate = time();
|
||||||
|
$title = $fileInfo['filename'];
|
||||||
|
|
||||||
|
$exists = $image->imageExists($albumName, $fileName, $relativePath);
|
||||||
|
|
||||||
|
if (!$exists) {
|
||||||
|
$image->addImage($albumName, $fileName, $relativePath, $modifiedDate, $addedDate, $title);
|
||||||
|
echo "Added $fileName to database in album $albumName\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$albums = scandir($albumsDir);
|
||||||
|
foreach ($albums as $album) {
|
||||||
|
if ($album !== '.' && $album !== '..' && is_dir($albumsDir . $album)) {
|
||||||
|
if (strpos($album, 'videos') === false) {
|
||||||
|
processDirectory($albumsDir . $album . '/photos/', $album, $image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
web/src/Database.php
Normal file
27
web/src/Database.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
// src/Database.php
|
||||||
|
|
||||||
|
namespace Hpz\Photogal;
|
||||||
|
|
||||||
|
use SQLite3;
|
||||||
|
|
||||||
|
class Database
|
||||||
|
{
|
||||||
|
private $connection;
|
||||||
|
|
||||||
|
public function __construct($dbFilePath)
|
||||||
|
{
|
||||||
|
$this->connection = new SQLite3($dbFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConnection()
|
||||||
|
{
|
||||||
|
return $this->connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
$this->connection->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
95
web/src/Image.php
Normal file
95
web/src/Image.php
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
// src/Image.php
|
||||||
|
|
||||||
|
namespace Hpz\Photogal;
|
||||||
|
|
||||||
|
class Image
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
|
||||||
|
public function __construct(Database $database)
|
||||||
|
{
|
||||||
|
$this->db = $database->getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createTable()
|
||||||
|
{
|
||||||
|
$query = "
|
||||||
|
CREATE TABLE IF NOT EXISTS images (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
album TEXT NOT NULL,
|
||||||
|
filename TEXT NOT NULL,
|
||||||
|
path TEXT NOT NULL,
|
||||||
|
modified_date INTEGER NOT NULL,
|
||||||
|
added_date INTEGER NOT NULL,
|
||||||
|
title TEXT NOT NULL
|
||||||
|
);
|
||||||
|
";
|
||||||
|
return $this->db->exec($query);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addImage($album, $filename, $path, $modifiedDate, $addedDate, $title)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("
|
||||||
|
INSERT INTO images (album, filename, path, modified_date, added_date, title)
|
||||||
|
VALUES (:album, :filename, :path, :modified_date, :added_date, :title)
|
||||||
|
");
|
||||||
|
$stmt->bindValue(':album', $album, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(':filename', $filename, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(':path', $path, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(':modified_date', $modifiedDate, SQLITE3_INTEGER);
|
||||||
|
$stmt->bindValue(':added_date', $addedDate, SQLITE3_INTEGER);
|
||||||
|
$stmt->bindValue(':title', $title, SQLITE3_TEXT);
|
||||||
|
return $stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getImages($album = null, $limit = 24, $offset = 0)
|
||||||
|
{
|
||||||
|
$query = "SELECT * FROM images";
|
||||||
|
if ($album) {
|
||||||
|
$query .= " WHERE album = :album";
|
||||||
|
}
|
||||||
|
$query .= " ORDER BY modified_date DESC LIMIT :limit OFFSET :offset";
|
||||||
|
|
||||||
|
$stmt = $this->db->prepare($query);
|
||||||
|
if ($album) {
|
||||||
|
$stmt->bindValue(':album', $album, SQLITE3_TEXT);
|
||||||
|
}
|
||||||
|
$stmt->bindValue(':limit', $limit, SQLITE3_INTEGER);
|
||||||
|
$stmt->bindValue(':offset', $offset, SQLITE3_INTEGER);
|
||||||
|
$result = $stmt->execute();
|
||||||
|
|
||||||
|
$images = [];
|
||||||
|
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
|
||||||
|
$images[] = $row;
|
||||||
|
}
|
||||||
|
return $images;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function imageExists($album, $filename, $path)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("SELECT COUNT(*) as count FROM images WHERE album = :album AND filename = :filename AND path = :path");
|
||||||
|
$stmt->bindValue(':album', $album, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(':filename', $filename, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(':path', $path, SQLITE3_TEXT);
|
||||||
|
$result = $stmt->execute();
|
||||||
|
$row = $result->fetchArray(SQLITE3_ASSOC);
|
||||||
|
return $row['count'] > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function countImages($album = null)
|
||||||
|
{
|
||||||
|
$query = "SELECT COUNT(*) as count FROM images";
|
||||||
|
if ($album) {
|
||||||
|
$query .= " WHERE album = :album";
|
||||||
|
}
|
||||||
|
$stmt = $this->db->prepare($query);
|
||||||
|
if ($album) {
|
||||||
|
$stmt->bindValue(':album', $album, SQLITE3_TEXT);
|
||||||
|
}
|
||||||
|
$result = $stmt->execute();
|
||||||
|
$row = $result->fetchArray(SQLITE3_ASSOC);
|
||||||
|
return $row['count'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
91
web/src/ThumbnailGenerator.php
Normal file
91
web/src/ThumbnailGenerator.php
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
// src/ThumbnailGenerator.php
|
||||||
|
|
||||||
|
namespace Hpz\Photogal;
|
||||||
|
|
||||||
|
class ThumbnailGenerator
|
||||||
|
{
|
||||||
|
public static function createThumbnail($sourcePath, $destPath, $thumbWidth = 200, $thumbHeight = 200, $quality = 40)
|
||||||
|
{
|
||||||
|
list($width, $height, $type) = getimagesize($sourcePath);
|
||||||
|
|
||||||
|
$thumb = imagecreatetruecolor($thumbWidth, $thumbHeight);
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case IMAGETYPE_JPEG:
|
||||||
|
$source = imagecreatefromjpeg($sourcePath);
|
||||||
|
break;
|
||||||
|
case IMAGETYPE_PNG:
|
||||||
|
$source = imagecreatefrompng($sourcePath);
|
||||||
|
break;
|
||||||
|
case IMAGETYPE_GIF:
|
||||||
|
$source = imagecreatefromgif($sourcePath);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$srcAspect = $width / $height;
|
||||||
|
$thumbAspect = $thumbWidth / $thumbHeight;
|
||||||
|
|
||||||
|
if ($srcAspect >= $thumbAspect) {
|
||||||
|
$newHeight = (int)$thumbHeight;
|
||||||
|
$newWidth = (int)($width / ($height / $thumbHeight));
|
||||||
|
} else {
|
||||||
|
$newWidth = (int)$thumbWidth;
|
||||||
|
$newHeight = (int)($height / ($width / $thumbWidth));
|
||||||
|
}
|
||||||
|
|
||||||
|
$xOffset = (int)(($thumbWidth - $newWidth) / 2);
|
||||||
|
$yOffset = (int)(($thumbHeight - $newHeight) / 2);
|
||||||
|
|
||||||
|
imagecopyresampled(
|
||||||
|
$thumb,
|
||||||
|
$source,
|
||||||
|
$xOffset,
|
||||||
|
$yOffset,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
$newWidth,
|
||||||
|
$newHeight,
|
||||||
|
$width,
|
||||||
|
$height
|
||||||
|
);
|
||||||
|
|
||||||
|
return imagewebp($thumb, $destPath, $quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function createBigImage($sourcePath, $destPath, $maxDimension = 2000, $quality = 80)
|
||||||
|
{
|
||||||
|
list($width, $height, $type) = getimagesize($sourcePath);
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case IMAGETYPE_JPEG:
|
||||||
|
$source = imagecreatefromjpeg($sourcePath);
|
||||||
|
break;
|
||||||
|
case IMAGETYPE_PNG:
|
||||||
|
$source = imagecreatefrompng($sourcePath);
|
||||||
|
break;
|
||||||
|
case IMAGETYPE_GIF:
|
||||||
|
$source = imagecreatefromgif($sourcePath);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$srcAspect = $width / $height;
|
||||||
|
|
||||||
|
if ($width > $height) {
|
||||||
|
$newWidth = $maxDimension;
|
||||||
|
$newHeight = (int)($maxDimension / $srcAspect);
|
||||||
|
} else {
|
||||||
|
$newHeight = $maxDimension;
|
||||||
|
$newWidth = (int)($maxDimension * $srcAspect);
|
||||||
|
}
|
||||||
|
|
||||||
|
$bigImage = imagecreatetruecolor($newWidth, $newHeight);
|
||||||
|
imagecopyresampled($bigImage, $source, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
|
||||||
|
|
||||||
|
return imagewebp($bigImage, $destPath, $quality);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
web/src/js/app.js
Normal file
40
web/src/js/app.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
function openLightbox(element) {
|
||||||
|
const lightbox = document.getElementById('lightbox');
|
||||||
|
const lightboxImg = document.getElementById('lightbox-img');
|
||||||
|
lightboxImg.src = element.dataset.big;
|
||||||
|
lightboxImg.setAttribute('data-original', element.dataset.original);
|
||||||
|
lightbox.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeLightbox() {
|
||||||
|
const lightbox = document.getElementById('lightbox');
|
||||||
|
lightbox.classList.remove('active');
|
||||||
|
const lightboxImg = document.getElementById('lightbox-img');
|
||||||
|
lightboxImg.src = ''; // Unload the current image
|
||||||
|
lightboxImg.style.transform = 'scale(1)';
|
||||||
|
}
|
||||||
|
|
||||||
|
function zoomImage(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const img = event.target;
|
||||||
|
let scale = img.style.transform ? parseFloat(img.style.transform.replace('scale(', '').replace(')', '')) : 1;
|
||||||
|
|
||||||
|
if (event.deltaY < 0) {
|
||||||
|
scale += 0.1; // Zoom in
|
||||||
|
} else {
|
||||||
|
scale -= 0.1; // Zoom out
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scale < 1) scale = 1; // Prevent zoom out below original size
|
||||||
|
|
||||||
|
img.style.transform = `scale(${scale})`;
|
||||||
|
img.style.transformOrigin = `${event.offsetX}px ${event.offsetY}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadOriginal(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
const lightboxImg = document.getElementById('lightbox-img');
|
||||||
|
const originalSrc = lightboxImg.getAttribute('data-original');
|
||||||
|
lightboxImg.src = originalSrc;
|
||||||
|
lightboxImg.style.transform = 'scale(1)';
|
||||||
|
}
|
||||||
41
web/src/styles/tailwind.css
Normal file
41
web/src/styles/tailwind.css
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
.lightbox {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.9);
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lightbox img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
cursor: zoom-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lightbox.active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-overlay {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.original-button {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
right: 10px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
color: white;
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
8
web/tailwind.config.js
Normal file
8
web/tailwind.config.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: ["./public/gallery.php","./public/index.php"],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user