forked from Somnia/SomniaRooms
Photo management and various fixes.
- Added admin page to upload and manage media. - Fixed /setup route. - Updated dockerfiles to support admin media. - Improved deploy docs.
This commit is contained in:
+5
-1
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
```
|
||||||
+1
-1
@@ -6,4 +6,4 @@
|
|||||||
*/
|
*/
|
||||||
import './styles/app.css';
|
import './styles/app.css';
|
||||||
|
|
||||||
console.log('This log comes from assets/app.js - welcome to AssetMapper! 🎉');
|
console.log('SomniaRooms Admin Panel - Assets loaded! 🎉');
|
||||||
+1
-1
@@ -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
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
"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.*",
|
||||||
|
|||||||
Generated
+168
-1
@@ -4,7 +4,7 @@
|
|||||||
"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": "7d38345c4b2eee74e8856e7fd4e8f951",
|
"content-hash": "7bf0ee6f0a2aa748adae16901e21b29b",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "composer/semver",
|
"name": "composer/semver",
|
||||||
@@ -3981,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",
|
||||||
@@ -4282,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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
images/
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"> 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 %}
|
||||||
|
|
||||||
@@ -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 %}
|
||||||
|
|
||||||
Reference in New Issue
Block a user