4 Commits
Author SHA1 Message Date
Daniel-Garmig ee058aa3b3 Photo management and various fixes.
CI / Docker Lint (push) Failing after 23s
- Added admin page to upload and manage media.
- Fixed /setup route.
- Updated dockerfiles to support admin media.
- Improved deploy docs.
2024-09-14 20:53:35 +02:00
Daniel-Garmig f1031c14df Admin panel - RoomCategory - Bedtype fixed 2024-09-13 20:45:35 +02:00
Daniel-Garmig 98e3627f29 Added Symfony AssetMapper. 2024-09-12 20:06:22 +02:00
Daniel-Garmig 22e46cb841 Removed Prettier. 2024-09-12 19:15:14 +02:00
27 changed files with 952 additions and 38 deletions
+5
View File
@@ -11,3 +11,8 @@
# Draw.io backup file. # Draw.io backup file.
*bkp *bkp
###> symfony/asset-mapper ###
/public/assets/
/assets/vendor/
###< symfony/asset-mapper ###
-4
View File
@@ -1,4 +0,0 @@
{
"parser": "php",
"plugins": ["@prettier/plugin-php"]
}
+3 -3
View File
@@ -1,5 +1,5 @@
{ {
"prettier.documentSelectors": [ "php.suggest.basic": false,
"**/*.{js,jsx,ts,tsx,vue,html,css,scss,less,json,md,mdx,graphql,yaml,yml,php}" "editor.formatOnSave": true,
] "editor.formatOnSaveMode": "modificationsIfAvailable"
} }
+5 -1
View File
@@ -85,10 +85,14 @@ RUN set -eux; \
# copy sources # copy sources
COPY --link . ./ COPY --link . ./
RUN rm -Rf frankenphp/ RUN rm -Rf frankenphp/
RUN rm -Rf .gitea/
RUN rm -Rf .vscode/
RUN rm -Rf public/images
RUN set -eux; \ RUN set -eux; \
mkdir -p var/cache var/log; \ mkdir -p var/cache var/log; \
composer dump-autoload --classmap-authoritative --no-dev; \ composer dump-autoload --classmap-authoritative --no-dev; \
composer dump-env prod; \ composer dump-env prod; \
composer run-script --no-dev post-install-cmd; \ composer run-script --no-dev post-install-cmd; \
chmod +x bin/console; sync; chmod +x bin/console; sync; \
php bin/console asset-map:compile
+63 -4
View File
@@ -9,31 +9,90 @@ Powered by:
## Commands: ## Commands:
Build containers: Build containers:
``` ```bash
docker compose build --no-cache docker compose build --no-cache
``` ```
Start Containers: Start Containers:
``` ```bash
docker compose up --pull always -d --wait docker compose up --pull always -d --wait
docker compose --env-file .\.env.local up -d --wait docker compose --env-file .\.env.local up -d --wait
``` ```
Stop containers Stop containers
``` ```bash
docker compose down --remove-orphans docker compose down --remove-orphans
``` ```
To use xdebug in VSCode you may need to configure some environment vars. A .env file (`.env.local`) can be used as:
```shell
XDEBUG_MODE=develop,debug
XDEBUG_DISCOVER_CLIENT_HOST=1
XDEBUG_IDEKEY=docker
XDEBUG_LOG=/dev/stdout
```
### Prod: ### Prod:
You can use docker compose for prod or build and deploy a prod image. When deploying to a production environment a production build should be used.
Then, deployment can be done as containers or using docker-compose.
**Build Prod image** **Build Prod image**
To build a production image:
``` ```
docker build -t somniarooms:prod --target frankenphp_prod . docker build -t somniarooms:prod --target frankenphp_prod .
``` ```
**Deploy prod image** **Deploy prod image**
You may use a env file to configure the SomniaRoom container:
```shell
SERVER_NAME=localhost
DATABASE_URL=postgresql://app:!ChangeMe!@somniadb:5432/somniarooms?serverVersion=16&charset=utf8
APP_SECRET=MYSECRET
MERCURE_PUBLISHER_JWT_KEY=MYCADDY_MERCURE_JWT_SECRET
MERCURE_SUBSCRIBER_JWT_KEY=MYCADDY_MERCURE_JWT_SECRET
FRANKENPHP_CONFIG="" #-> Avoid using FrankenPHP workers, as don't play well with Easyadmin.
``` ```
You need a database for using SomniaRooms.
To run one on a container you can use this that match this example config:
```bash
docker run -d --name somniarooms-db --hostname somniadb -p 5432:5432 --env=POSTGRES_DB=somniarooms --env=POSTGRES_PASSWORD=!ChangeMe! --env=POSTGRES_USER=app --env=PGDATA=/var/lib/postgresql/data --volume=somniarooms_db:/var/lib/postgresql/data --network somniarooms-network postgres:16-alpine
```
Then SomniaRooms can be deployed on a container (change --env-file value to the created file):
```bash
docker run -d -it -p 80:80 -p 443:443 --name somniarooms --env-file ./env.prod.local somniarooms:prod docker run -d -it -p 80:80 -p 443:443 --name somniarooms --env-file ./env.prod.local somniarooms:prod
``` ```
When deploying to prod is recomended to mount some directories as a docker volume or hostpath. This makes sure data is preserved after deleting the container.
- Image directory (`/app/public/images`)
If you are deploying your DB as a container, check the database docs and make sure database data won't be lost when updating or deleting the container. This example configuration store postgress data on a unnamed docket volume.
```bash
docker run -d -it -p 80:80 -p 443:443 --name somniarooms --env-file ./.env.prod.local --volume=somniarooms_images:/app/public/images:rw --network somniarooms-network somniarooms:prod
```
If you want your app and DB to comunicate using it's hostname (like on this example) you should create a docker network.
```bash
docker network create somniarooms-network
```
## Running the app!
Go to the app address (`https://localhost`) and it will show Symfony welcome page (or a error when running on prod).
Go to addres `https://localhost/setup` to inicialize app users and data.
Then, you the admin panel is at `https://localhost/admin`
You may login using default admin user:
```
user: admin
pass: admin
```
+9
View File
@@ -0,0 +1,9 @@
/*
* Welcome to your app's main JavaScript file!
*
* This file will be included onto the page via the importmap() Twig function,
* which should already be in your base.html.twig.
*/
import './styles/app.css';
console.log('SomniaRooms Admin Panel - Assets loaded! 🎉');
View File
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
php: php:
image: ${IMAGES_PREFIX:-}app-php image: somniarooms
restart: unless-stopped restart: unless-stopped
environment: environment:
SERVER_NAME: ${SERVER_NAME:-localhost}, php:80 SERVER_NAME: ${SERVER_NAME:-localhost}, php:80
+9 -2
View File
@@ -18,18 +18,24 @@
"phpdocumentor/reflection-docblock": "^5.4", "phpdocumentor/reflection-docblock": "^5.4",
"phpstan/phpdoc-parser": "^1.28", "phpstan/phpdoc-parser": "^1.28",
"runtime/frankenphp-symfony": "^0.2.0", "runtime/frankenphp-symfony": "^0.2.0",
"symfony/asset": "7.0.*",
"symfony/asset-mapper": "7.0.*",
"symfony/console": "7.0.*", "symfony/console": "7.0.*",
"symfony/dotenv": "7.0.*", "symfony/dotenv": "7.0.*",
"symfony/flex": "^2", "symfony/flex": "^2",
"symfony/form": "7.0.*", "symfony/form": "7.0.*",
"symfony/framework-bundle": "7.0.*", "symfony/framework-bundle": "7.0.*",
"symfony/mime": "7.0.*",
"symfony/property-access": "7.0.*", "symfony/property-access": "7.0.*",
"symfony/property-info": "7.0.*", "symfony/property-info": "7.0.*",
"symfony/runtime": "7.0.*", "symfony/runtime": "7.0.*",
"symfony/security-bundle": "7.0.*", "symfony/security-bundle": "7.0.*",
"symfony/serializer": "7.0.*", "symfony/serializer": "7.0.*",
"symfony/twig-bundle": "7.0.*",
"symfony/var-exporter": "7.0.4", "symfony/var-exporter": "7.0.4",
"symfony/yaml": "7.0.*" "symfony/yaml": "7.0.*",
"twig/extra-bundle": "^2.12|^3.0",
"twig/twig": "^2.12|^3.0"
}, },
"config": { "config": {
"allow-plugins": { "allow-plugins": {
@@ -62,7 +68,8 @@
"scripts": { "scripts": {
"auto-scripts": { "auto-scripts": {
"cache:clear": "symfony-cmd", "cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd" "assets:install %PUBLIC_DIR%": "symfony-cmd",
"importmap:install": "symfony-cmd"
}, },
"post-install-cmd": [ "post-install-cmd": [
"@auto-scripts" "@auto-scripts"
Generated
+587 -17
View File
@@ -4,8 +4,89 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "0e4c12b24935945c9c0a907893d84c6d", "content-hash": "7bf0ee6f0a2aa748adae16901e21b29b",
"packages": [ "packages": [
{
"name": "composer/semver",
"version": "3.4.2",
"source": {
"type": "git",
"url": "https://github.com/composer/semver.git",
"reference": "c51258e759afdb17f1fd1fe83bc12baaef6309d6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/semver/zipball/c51258e759afdb17f1fd1fe83bc12baaef6309d6",
"reference": "c51258e759afdb17f1fd1fe83bc12baaef6309d6",
"shasum": ""
},
"require": {
"php": "^5.3.2 || ^7.0 || ^8.0"
},
"require-dev": {
"phpstan/phpstan": "^1.4",
"symfony/phpunit-bridge": "^4.2 || ^5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Composer\\Semver\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nils Adermann",
"email": "[email protected]",
"homepage": "http://www.naderman.de"
},
{
"name": "Jordi Boggiano",
"email": "[email protected]",
"homepage": "http://seld.be"
},
{
"name": "Rob Bast",
"email": "[email protected]",
"homepage": "http://robbast.nl"
}
],
"description": "Semver library that offers utilities, version constraint parsing and validation.",
"keywords": [
"semantic",
"semver",
"validation",
"versioning"
],
"support": {
"irc": "ircs://irc.libera.chat:6697/composer",
"issues": "https://github.com/composer/semver/issues",
"source": "https://github.com/composer/semver/tree/3.4.2"
},
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"time": "2024-07-12T11:35:52+00:00"
},
{ {
"name": "doctrine/cache", "name": "doctrine/cache",
"version": "2.2.0", "version": "2.2.0",
@@ -1908,16 +1989,16 @@
}, },
{ {
"name": "symfony/asset", "name": "symfony/asset",
"version": "v7.0.3", "version": "v7.0.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/asset.git", "url": "https://github.com/symfony/asset.git",
"reference": "3ae493792fc17cc31b84e231f30f2d154575f171" "reference": "0f106714bb8d857560edd2ada7f387d2f437c830"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/asset/zipball/3ae493792fc17cc31b84e231f30f2d154575f171", "url": "https://api.github.com/repos/symfony/asset/zipball/0f106714bb8d857560edd2ada7f387d2f437c830",
"reference": "3ae493792fc17cc31b84e231f30f2d154575f171", "reference": "0f106714bb8d857560edd2ada7f387d2f437c830",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1957,7 +2038,7 @@
"description": "Manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files", "description": "Manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/asset/tree/v7.0.3" "source": "https://github.com/symfony/asset/tree/v7.0.8"
}, },
"funding": [ "funding": [
{ {
@@ -1973,7 +2054,85 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-01-23T15:02:46+00:00" "time": "2024-05-31T14:55:39+00:00"
},
{
"name": "symfony/asset-mapper",
"version": "v7.0.10",
"source": {
"type": "git",
"url": "https://github.com/symfony/asset-mapper.git",
"reference": "06e0ef3522bc1942192892094907d27f1a660cae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/asset-mapper/zipball/06e0ef3522bc1942192892094907d27f1a660cae",
"reference": "06e0ef3522bc1942192892094907d27f1a660cae",
"shasum": ""
},
"require": {
"composer/semver": "^3.0",
"php": ">=8.2",
"symfony/filesystem": "^6.4|^7.0",
"symfony/http-client": "^6.4|^7.0"
},
"conflict": {
"symfony/framework-bundle": "<6.4"
},
"require-dev": {
"symfony/asset": "^6.4|^7.0",
"symfony/browser-kit": "^6.4|^7.0",
"symfony/console": "^6.4|^7.0",
"symfony/event-dispatcher-contracts": "^3.0",
"symfony/finder": "^6.4|^7.0",
"symfony/framework-bundle": "^6.4|^7.0",
"symfony/http-foundation": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/web-link": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\AssetMapper\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "[email protected]"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Maps directories of assets & makes them available in a public directory with versioned filenames.",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/asset-mapper/tree/v7.0.10"
},
"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-07-09T18:35:37+00:00"
}, },
{ {
"name": "symfony/cache", "name": "symfony/cache",
@@ -3381,6 +3540,176 @@
], ],
"time": "2024-03-27T19:55:25+00:00" "time": "2024-03-27T19:55:25+00:00"
}, },
{
"name": "symfony/http-client",
"version": "v7.0.10",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
"reference": "3ae495c67ba9c3b504fecd070a6c28b4143088cf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client/zipball/3ae495c67ba9c3b504fecd070a6c28b4143088cf",
"reference": "3ae495c67ba9c3b504fecd070a6c28b4143088cf",
"shasum": ""
},
"require": {
"php": ">=8.2",
"psr/log": "^1|^2|^3",
"symfony/http-client-contracts": "^3.4.1",
"symfony/service-contracts": "^2.5|^3"
},
"conflict": {
"php-http/discovery": "<1.15",
"symfony/http-foundation": "<6.4"
},
"provide": {
"php-http/async-client-implementation": "*",
"php-http/client-implementation": "*",
"psr/http-client-implementation": "1.0",
"symfony/http-client-implementation": "3.0"
},
"require-dev": {
"amphp/amp": "^2.5",
"amphp/http-client": "^4.2.1",
"amphp/http-tunnel": "^1.0",
"amphp/socket": "^1.1",
"guzzlehttp/promises": "^1.4|^2.0",
"nyholm/psr7": "^1.0",
"php-http/httplug": "^1.0|^2.0",
"psr/http-client": "^1.0",
"symfony/dependency-injection": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/messenger": "^6.4|^7.0",
"symfony/process": "^6.4|^7.0",
"symfony/stopwatch": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\HttpClient\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "[email protected]"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously",
"homepage": "https://symfony.com",
"keywords": [
"http"
],
"support": {
"source": "https://github.com/symfony/http-client/tree/v7.0.10"
},
"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-07-17T06:06:58+00:00"
},
{
"name": "symfony/http-client-contracts",
"version": "v3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client-contracts.git",
"reference": "20414d96f391677bf80078aa55baece78b82647d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/20414d96f391677bf80078aa55baece78b82647d",
"reference": "20414d96f391677bf80078aa55baece78b82647d",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
"psr-4": {
"Symfony\\Contracts\\HttpClient\\": ""
},
"exclude-from-classmap": [
"/Test/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "[email protected]"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Generic abstractions related to HTTP clients",
"homepage": "https://symfony.com",
"keywords": [
"abstractions",
"contracts",
"decoupling",
"interfaces",
"interoperability",
"standards"
],
"support": {
"source": "https://github.com/symfony/http-client-contracts/tree/v3.5.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-04-18T09:32:20+00:00"
},
{ {
"name": "symfony/http-foundation", "name": "symfony/http-foundation",
"version": "v7.0.6", "version": "v7.0.6",
@@ -3652,6 +3981,90 @@
], ],
"time": "2024-01-23T15:02:46+00:00" "time": "2024-01-23T15:02:46+00:00"
}, },
{
"name": "symfony/mime",
"version": "v7.0.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
"reference": "60757ea7d562ae1756c1f430a6f7872156a15f32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mime/zipball/60757ea7d562ae1756c1f430a6f7872156a15f32",
"reference": "60757ea7d562ae1756c1f430a6f7872156a15f32",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/polyfill-intl-idn": "^1.10",
"symfony/polyfill-mbstring": "^1.0"
},
"conflict": {
"egulias/email-validator": "~3.0.0",
"phpdocumentor/reflection-docblock": "<3.2.2",
"phpdocumentor/type-resolver": "<1.4.0",
"symfony/mailer": "<6.4",
"symfony/serializer": "<6.4.3|>7.0,<7.0.3"
},
"require-dev": {
"egulias/email-validator": "^2.1.10|^3.1|^4",
"league/html-to-markdown": "^5.0",
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"symfony/dependency-injection": "^6.4|^7.0",
"symfony/process": "^6.4|^7.0",
"symfony/property-access": "^6.4|^7.0",
"symfony/property-info": "^6.4|^7.0",
"symfony/serializer": "^6.4.3|^7.0.3"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Mime\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "[email protected]"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Allows manipulating MIME messages",
"homepage": "https://symfony.com",
"keywords": [
"mime",
"mime-type"
],
"support": {
"source": "https://github.com/symfony/mime/tree/v7.0.9"
},
"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-28T09:58:46+00:00"
},
{ {
"name": "symfony/options-resolver", "name": "symfony/options-resolver",
"version": "v7.0.0", "version": "v7.0.0",
@@ -3953,6 +4366,89 @@
], ],
"time": "2024-01-29T20:12:16+00:00" "time": "2024-01-29T20:12:16+00:00"
}, },
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "c36586dcf89a12315939e00ec9b4474adcb1d773"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773",
"reference": "c36586dcf89a12315939e00ec9b4474adcb1d773",
"shasum": ""
},
"require": {
"php": ">=7.2",
"symfony/polyfill-intl-normalizer": "^1.10"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Intl\\Idn\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Laurent Bassin",
"email": "[email protected]"
},
{
"name": "Trevor Rowbotham",
"email": "[email protected]"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"idn",
"intl",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.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-09-09T11:45:10+00:00"
},
{ {
"name": "symfony/polyfill-intl-normalizer", "name": "symfony/polyfill-intl-normalizer",
"version": "v1.29.0", "version": "v1.29.0",
@@ -6025,25 +6521,99 @@
"time": "2024-01-23T15:02:46+00:00" "time": "2024-01-23T15:02:46+00:00"
}, },
{ {
"name": "twig/twig", "name": "twig/extra-bundle",
"version": "v3.9.3", "version": "v3.13.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/twigphp/Twig.git", "url": "https://github.com/twigphp/twig-extra-bundle.git",
"reference": "a842d75fed59cdbcbd3a3ad7fb9eb768fc350d58" "reference": "21a9a7aa9f79d4493bb6fed4eb2794339f9551f5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/a842d75fed59cdbcbd3a3ad7fb9eb768fc350d58", "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/21a9a7aa9f79d4493bb6fed4eb2794339f9551f5",
"reference": "a842d75fed59cdbcbd3a3ad7fb9eb768fc350d58", "reference": "21a9a7aa9f79d4493bb6fed4eb2794339f9551f5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.2.5", "php": ">=8.0.2",
"symfony/framework-bundle": "^5.4|^6.4|^7.0",
"symfony/twig-bundle": "^5.4|^6.4|^7.0",
"twig/twig": "^3.0|^4.0"
},
"require-dev": {
"league/commonmark": "^1.0|^2.0",
"symfony/phpunit-bridge": "^6.4|^7.0",
"twig/cache-extra": "^3.0",
"twig/cssinliner-extra": "^3.0",
"twig/html-extra": "^3.0",
"twig/inky-extra": "^3.0",
"twig/intl-extra": "^3.0",
"twig/markdown-extra": "^3.0",
"twig/string-extra": "^3.0"
},
"type": "symfony-bundle",
"autoload": {
"psr-4": {
"Twig\\Extra\\TwigExtraBundle\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "[email protected]",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
}
],
"description": "A Symfony bundle for extra Twig extensions",
"homepage": "https://twig.symfony.com",
"keywords": [
"bundle",
"extra",
"twig"
],
"support": {
"source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.13.0"
},
"funding": [
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/twig/twig",
"type": "tidelift"
}
],
"time": "2024-09-01T20:39:12+00:00"
},
{
"name": "twig/twig",
"version": "v3.14.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "126b2c97818dbff0cdf3fbfc881aedb3d40aae72"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/126b2c97818dbff0cdf3fbfc881aedb3d40aae72",
"reference": "126b2c97818dbff0cdf3fbfc881aedb3d40aae72",
"shasum": ""
},
"require": {
"php": ">=8.0.2",
"symfony/deprecation-contracts": "^2.5|^3", "symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "^1.8", "symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3", "symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php80": "^1.22" "symfony/polyfill-php81": "^1.29"
}, },
"require-dev": { "require-dev": {
"psr/container": "^1.0|^2.0", "psr/container": "^1.0|^2.0",
@@ -6089,7 +6659,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/twigphp/Twig/issues", "issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.9.3" "source": "https://github.com/twigphp/Twig/tree/v3.14.0"
}, },
"funding": [ "funding": [
{ {
@@ -6101,7 +6671,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-04-18T11:59:33+00:00" "time": "2024-09-09T17:55:12+00:00"
}, },
{ {
"name": "webmozart/assert", "name": "webmozart/assert",
+1
View File
@@ -10,4 +10,5 @@ return [
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle::class => ['all' => true], EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle::class => ['all' => true],
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
]; ];
+5
View File
@@ -0,0 +1,5 @@
framework:
asset_mapper:
# The paths to make available to the asset mapper.
paths:
- assets/
+4
View File
@@ -20,5 +20,9 @@ services:
- '../src/Entity/' - '../src/Entity/'
- '../src/Kernel.php' - '../src/Kernel.php'
App\Service\FileUploaderService:
arguments:
$targetDirectory: 'images/'
# add more service definitions when explicit configuration is needed # add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones # please note that last definitions always *replace* previous ones
+19
View File
@@ -0,0 +1,19 @@
<?php
/**
* Returns the importmap for this application.
*
* - "path" is a path inside the asset mapper system. Use the
* "debug:asset-map" command to see the full list of paths.
*
* - "entrypoint" (JavaScript only) set to true for any module that will
* be used as an "entrypoint" (and passed to the importmap() Twig function).
*
* The "importmap:require" command can be used to add new entries to this file.
*/
return [
'app' => [
'path' => './assets/app.js',
'entrypoint' => true,
],
];
+1
View File
@@ -0,0 +1 @@
images/
@@ -32,7 +32,6 @@ class RoomCategoryCrudController extends AbstractCrudController
{ {
yield TextField::new('name'); yield TextField::new('name');
yield TextField::new('description'); yield TextField::new('description');
yield TextField::new('bedType');
yield IntegerField::new('maxGuestNum'); yield IntegerField::new('maxGuestNum');
yield CollectionField::new('roomCategoryPhotos')->useEntryCrudForm(); yield CollectionField::new('roomCategoryPhotos')->useEntryCrudForm();
} }
@@ -18,7 +18,7 @@ class RoomCategoryPhotoCrudController extends AbstractCrudController
{ {
yield TextField::new('path'); yield TextField::new('path');
yield TextField::new('altText'); yield TextField::new('altText');
yield IntegerField::new('order'); yield IntegerField::new('photoOrder');
} }
} }
@@ -46,6 +46,9 @@ class SomniaDashboardController extends AbstractDashboardController
yield MenuItem::linkToCrud('Bed Types', 'fas fa-bed', BedType::class); yield MenuItem::linkToCrud('Bed Types', 'fas fa-bed', BedType::class);
yield MenuItem::linkToRoute('Bed Type - category', 'fas fa-bed', 'admin_roomCategoryBedType'); yield MenuItem::linkToRoute('Bed Type - category', 'fas fa-bed', 'admin_roomCategoryBedType');
yield MenuItem::Section('Resources');
yield MenuItem::linkToRoute('Photos', 'fas fa-image', 'admin_photos');
yield MenuItem::Section('App Management'); yield MenuItem::Section('App Management');
yield MenuItem::linkToCrud('Config Vars', 'fas fa-gear', ConfigVariable::class); yield MenuItem::linkToCrud('Config Vars', 'fas fa-gear', ConfigVariable::class);
yield MenuItem::linkToCrud('Users', 'fas fa-user', User::class) yield MenuItem::linkToCrud('Users', 'fas fa-user', User::class)
@@ -0,0 +1,119 @@
<?php
namespace App\Controller;
use App\Service\FileUploaderService;
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Asset\PathPackage;
use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Validator\Constraints\File;
#[IsGranted("ROLE_ADMIN")]
class PhotoManagerAdminController extends AbstractController
{
private AdminUrlGenerator $adminUrlGenerator;
private FileUploaderService $fileUploader;
public function __construct(
AdminUrlGenerator $adminUrlGenerator,
FileUploaderService $fu
) {
$this->adminUrlGenerator = $adminUrlGenerator;
$this->fileUploader = $fu;
}
#[Route('/admin/photos', name: 'admin_photos')]
public function index(): Response
{
$fileData = [];
$dir = $this->fileUploader->getTargetDirectory();
$pathPackage = new PathPackage($dir, new EmptyVersionStrategy());
foreach (glob($dir . '*.*') as $file) {
$filename = basename($file);
$fileData[] = [
"filename" => $filename,
"url" => $pathPackage->getUrl($filename),
];
}
//return new JsonResponse(["dir" => $dir, "response" => $fileData]);
return $this->render('admin/photo_manager/index.html.twig', [
'photoList' => $fileData,
]);
}
#[Route('/admin/photos/upload', name: 'admin_photos_upload')]
public function uploadPhoto(
Request $request
): Response {
$form = $this->createFormBuilder()
->add('file', FileType::class, [
'mapped' => false,
'required' => false,
'constraints' => [
new File([
'maxSize' => '2048k',
'mimeTypes' => [
'image/jpeg',
'image/png',
'image/svg+xml',
'image/webp',
],
'mimeTypesMessage' => 'Please upload a valid image!',
])
],
])
->add('submit', SubmitType::class)
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** @var UploadedFile $uploadedPhoto */
$uploadedPhoto = $form->get('file')->getData();
if ($uploadedPhoto) {
$filename = $this->fileUploader->upload($uploadedPhoto);
}
$url = $this->adminUrlGenerator->setRoute('admin_photos')->generateUrl();
return $this->redirect($url);
}
return $this->render('admin/adminEdit.html.twig', [
'form' => $form,
'ElementName' => "Photo",
'ElementType' => "New Photo",
]);
}
#[Route('/admin/photos/remove/{filename}', name: 'admin_photos_remove')]
public function remove(string $filename): Response
{
$filePath = $this->fileUploader->getTargetDirectory() . $filename;
$deleted = $this->fileUploader->deleteFile($filePath);
if (!$deleted) {
throw new HttpException("404", "File can't be deleted");
}
$url = $this->adminUrlGenerator->setRoute('admin_photos')->generateUrl();
return $this->redirect($url);
}
}
+2 -1
View File
@@ -4,6 +4,7 @@ namespace App\Controller;
use App\Entity\User; use App\Entity\User;
use App\Entity\ConfigVariable; use App\Entity\ConfigVariable;
use App\Enum\ConfigSectionType;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@@ -53,7 +54,7 @@ class SecurityController extends AbstractController
$var_setup = new ConfigVariable(); $var_setup = new ConfigVariable();
$var_setup->setKey("SETUP_STATUS"); $var_setup->setKey("SETUP_STATUS");
$var_setup->setValue("1"); $var_setup->setValue("1");
$var_setup->setSection("SYSTEM"); $var_setup->setSection(ConfigSectionType::Internal);
$entityManager->persist($var_setup); $entityManager->persist($var_setup);
+46
View File
@@ -0,0 +1,46 @@
<?php
namespace App\Service;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\String\Slugger\SluggerInterface;
class FileUploaderService
{
public function __construct(
private string $targetDirectory,
private SluggerInterface $slugger,
) {}
public function upload(UploadedFile $file): string
{
$originalFilename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
$safeFilename = $this->slugger->slug($originalFilename);
$fileName = $safeFilename . '-' . uniqid() . '.' . $file->guessExtension();
try {
$file->move($this->getTargetDirectory(), $fileName);
} catch (FileException $e) {
// ... handle exception if something happens during file upload
}
return $fileName;
}
public function deleteFile(string $filepath): bool
{
if (!$filepath || !is_file($filepath)) {
return false;
}
$deleted = unlink($filepath);
return $deleted;
}
public function getTargetDirectory(): string
{
return $this->targetDirectory;
}
}
+18
View File
@@ -47,6 +47,21 @@
"./config/packages/nelmio_cors.yaml" "./config/packages/nelmio_cors.yaml"
] ]
}, },
"symfony/asset-mapper": {
"version": "7.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.4",
"ref": "6c28c471640cc2c6e60812ebcb961c526ef8997f"
},
"files": [
"./assets/app.js",
"./assets/styles/app.css",
"./config/packages/asset_mapper.yaml",
"./importmap.php"
]
},
"symfony/console": { "symfony/console": {
"version": "7.0", "version": "7.0",
"recipe": { "recipe": {
@@ -184,5 +199,8 @@
"./config/packages/web_profiler.yaml", "./config/packages/web_profiler.yaml",
"./config/routes/web_profiler.yaml" "./config/routes/web_profiler.yaml"
] ]
},
"twig/extra-bundle": {
"version": "v3.13.0"
} }
} }
+3 -1
View File
@@ -1,7 +1,9 @@
{# templates/admin/adminEdit.html.twig #} {# templates/admin/adminEdit.html.twig #}
{% extends '@EasyAdmin/page/content.html.twig' %} {% extends '@EasyAdmin/page/content.html.twig' %}
{% block content_title '{{elementType}} - Edit' %} {% block content_title ElementType %}
{% block main %} {% block main %}
<div> <div>
<h3>{{ElementName}}</h3> <h3>{{ElementName}}</h3>
@@ -0,0 +1,28 @@
{# templates/admin/photo_manager/index.html.twig #}
{% extends '@EasyAdmin/page/content.html.twig' %}
{% block content_title 'Photo Manager' %}
{% block main %}
<a class="btn btn-primary m-3" href="{{ ea_url().setRoute('admin_photos_upload') }}">
Add new
</a>
<div style="display: flex; flex-direction: row; flex-wrap: wrap; gap: 18px;">
{% for photo in photoList %}
<div class="card text-center" style="width: 40%;">
<p>{{photo.filename}}</p>
<img src="{{ asset(photo.url) }}" style="max-width: 80%;"/>
<br>
<a class="btn btn-outline-primary m-2">
<i class="fas fa-clipboard">&nbsp;&nbsp;Copy!</i>
</a>
<a class="btn btn-primary m-2 mt-1" href="{{ ea_url().setRoute('admin_photos_remove', { filename: photo.filename }) }}">
Delete
</a>
</div>
{% endfor %}
</div>
{% endblock %}
@@ -1,6 +1,13 @@
{# templates/admin/somniaDashboard.html.html.twig #} {# templates/admin/somniaDashboard.html.html.twig #}
{% extends '@EasyAdmin/layout.html.twig' %} {% extends '@EasyAdmin/layout.html.twig' %}
{% block javascripts %}
{% block importmap %}
{{ importmap('app') }}
{% endblock %}
{% endblock %}
{% block main %} {% block main %}
{# ... #} {# ... #}
{% endblock main %} {% endblock main %}
+1
View File
@@ -8,6 +8,7 @@
{% endblock %} {% endblock %}
{% block javascripts %} {% block javascripts %}
{% block importmap %}{{ importmap('app') }}{% endblock %}
{% endblock %} {% endblock %}
</head> </head>
<body> <body>
@@ -0,0 +1,10 @@
{# templates/bundles/EasyAdminBundle/layout.html.twig #}
{# DO THIS: the '!' symbol tells Symfony to extend from the original template #}
{% extends '@!EasyAdmin/layout.html.twig' %}
{% block javascripts %}
{% block importmap %}
{{ importmap('app') }}
{% endblock %}{% endblock %}