Compare commits
192 commits
Author | SHA1 | Date | |
---|---|---|---|
Simon Vieille | 79d549254f | ||
Simon Vieille | c79e96e291 | ||
Simon Vieille | 092b490ae2 | ||
Simon Vieille | fcecf74f90 | ||
c00723a246 | |||
e81b11c5ec | |||
af2212ac8c | |||
b57b0e3770 | |||
ba76a83163 | |||
2874f579dc | |||
287e1d66cb | |||
b23a4fe9c5 | |||
59bbd24b76 | |||
2dfe6ef1ca | |||
66f82ecc13 | |||
8432fc123e | |||
627927a11e | |||
67871179a8 | |||
dbec7a884e | |||
8c6ad05cbd | |||
cc0296e6e2 | |||
4f94bd5640 | |||
9d8d6b510a | |||
edb51485e1 | |||
c596f1d62d | |||
813aa8c618 | |||
39e92e949a | |||
e550bc54e2 | |||
ab808ee511 | |||
1e8baa5ee7 | |||
7f5fcc98d7 | |||
0f4767b8e0 | |||
70bac192e2 | |||
2b72994cde | |||
74d12bba3b | |||
1cacfd0bb7 | |||
740f90294d | |||
da12738b94 | |||
089cd2bb85 | |||
b22db21ea4 | |||
b9b6d968aa | |||
b8dd7aa3bf | |||
6a56bc2c36 | |||
1abc6a7b60 | |||
a9c64637ec | |||
72299ba2a5 | |||
a2ce7ed609 | |||
41abaa20d8 | |||
f7e374cebc | |||
c7e3867a93 | |||
1366e92ca0 | |||
301f22bb4a | |||
7397a1cde8 | |||
e232e44dcd | |||
edb8de9800 | |||
d0eb452847 | |||
Simon Vieille | 75a29b6372 | ||
Simon Vieille | 9bd1ff142b | ||
Simon Vieille | 69ad422624 | ||
Simon Vieille | e0af777a34 | ||
Simon Vieille | 310c5076a0 | ||
Simon Vieille | 8fcc6faebb | ||
Simon Vieille | 65b3583411 | ||
Simon Vieille | 58de4e8908 | ||
Simon Vieille | 450972ca1e | ||
Simon Vieille | a0b4137fb3 | ||
Simon Vieille | 05506fb5db | ||
Simon Vieille | ab14728bec | ||
Simon Vieille | 21cec0647a | ||
Simon Vieille | c364cafd17 | ||
Simon Vieille | 673132adf2 | ||
Simon Vieille | 58b5252986 | ||
Simon Vieille | 674846ab53 | ||
Simon Vieille | 5f942a904b | ||
Simon Vieille | 5ad22f21d2 | ||
Simon Vieille | be7003d613 | ||
Simon Vieille | 325401f37d | ||
Simon Vieille | 6addbc7de4 | ||
Simon Vieille | bda7a4a195 | ||
Simon Vieille | a79011c276 | ||
Simon Vieille | 1113c0a91f | ||
Simon Vieille | cbf05e3ce3 | ||
Simon Vieille | 2ecb972a79 | ||
Simon Vieille | b26738fe49 | ||
Simon Vieille | 69e2e919de | ||
Simon Vieille | c4c5aa867a | ||
Simon Vieille | 5b68c61cc0 | ||
Simon Vieille | 7052f2516d | ||
Simon Vieille | 52be00fc77 | ||
Simon Vieille | 3626a65f1f | ||
Simon Vieille | 70cae6f7a0 | ||
Simon Vieille | 5f1111d2a0 | ||
Simon Vieille | 1ee868b354 | ||
Simon Vieille | 2307efa341 | ||
Simon Vieille | f19fdb0ecf | ||
Simon Vieille | 4ce765ed72 | ||
Simon Vieille | 3c52a00bc4 | ||
Simon Vieille | cd88d7626a | ||
Simon Vieille | 3c274a14b9 | ||
Simon Vieille | 371a9f8bb6 | ||
Simon Vieille | bb62d1ce78 | ||
Simon Vieille | b043e42b96 | ||
Simon Vieille | 2a91b5917f | ||
Simon Vieille | 2e9b942024 | ||
Simon Vieille | 22d7389183 | ||
Simon Vieille | d0e187746a | ||
Simon Vieille | 933e360e26 | ||
Simon Vieille | c4bc470e8c | ||
Simon Vieille | 9dd0f7376b | ||
Simon Vieille | 52536ccefb | ||
Simon Vieille | f240db7ba4 | ||
Simon Vieille | aa808d07e8 | ||
Simon Vieille | bb01163e84 | ||
Simon Vieille | 1dbed2d52f | ||
Simon Vieille | 489a0411a0 | ||
Simon Vieille | 2a5d0ae1f4 | ||
Simon Vieille | 2adff5f583 | ||
Simon Vieille | b40c77d7a3 | ||
Simon Vieille | 27bccc9acd | ||
Simon Vieille | be31af32bc | ||
Simon Vieille | 246c4621c0 | ||
Simon Vieille | 4d235c4ec5 | ||
Simon Vieille | 2be2da285a | ||
Simon Vieille | 1498ab5308 | ||
Simon Vieille | 68c6ee8081 | ||
Simon Vieille | bec4edfa45 | ||
Simon Vieille | 7623546c10 | ||
Simon Vieille | 4cbd601476 | ||
Simon Vieille | 63237d50e7 | ||
Simon Vieille | 2d8637617b | ||
Simon Vieille | 81d1fa9cad | ||
Simon Vieille | 47b83ee968 | ||
Simon Vieille | 6086ceaf35 | ||
Simon Vieille | 94e2fc966d | ||
Simon Vieille | b809ee9c88 | ||
Simon Vieille | a3ef3540c1 | ||
Simon Vieille | 47e6f06bd7 | ||
Simon Vieille | a3a1822339 | ||
Simon Vieille | 7523f479d5 | ||
Simon Vieille | 59d5fed75e | ||
Simon Vieille | 4e1003159c | ||
Simon Vieille | fd36561cf1 | ||
Simon Vieille | 73b82578e2 | ||
Simon Vieille | e7b4f7f81e | ||
Simon Vieille | dc34a954f6 | ||
Simon Vieille | 3017880a64 | ||
Simon Vieille | 0424a9457e | ||
Simon Vieille | bd6a5bb44e | ||
Simon Vieille | b32133e7cb | ||
Simon Vieille | a37c6626c6 | ||
Simon Vieille | 4e1166c19d | ||
Simon Vieille | 098a0f17c0 | ||
Simon Vieille | f7ddd54d99 | ||
Simon Vieille | 39637d4094 | ||
Simon Vieille | ce5c69d467 | ||
Simon Vieille | 23802898b5 | ||
Simon Vieille | 632e6b7c7a | ||
Simon Vieille | 89925c2a34 | ||
Simon Vieille | 4be92913a5 | ||
Simon Vieille | dd11bf3898 | ||
Simon Vieille | a0c0e53816 | ||
Simon Vieille | f3a48cea18 | ||
Simon Vieille | 3705435785 | ||
Simon Vieille | 3b6e89dc63 | ||
Simon Vieille | 8e3944fa2e | ||
Simon Vieille | 4b4a28aee4 | ||
Simon Vieille | cd3d00389d | ||
Simon Vieille | 729266e0a6 | ||
Simon Vieille | a563ab3caf | ||
Simon Vieille | 3d8a4de100 | ||
Simon Vieille | 7355685009 | ||
Simon Vieille | 8d02608681 | ||
Simon Vieille | e288db77f6 | ||
Simon Vieille | 56eb728821 | ||
Simon Vieille | a3cf59417c | ||
Simon Vieille | bf4bd48775 | ||
Simon Vieille | f4b0b2869c | ||
Simon Vieille | 0cffb7d588 | ||
Simon Vieille | da4d5eb244 | ||
Simon Vieille | 321c7ed960 | ||
Simon Vieille | c239832aa3 | ||
Simon Vieille | d842c142ff | ||
Simon Vieille | 44ea80de14 | ||
Simon Vieille | 7f1616c4d5 | ||
Simon Vieille | 31f18f73f1 | ||
Simon Vieille | 8b1e38a647 | ||
Simon Vieille | 5d8996d624 | ||
Simon Vieille | c80482d7e3 | ||
Simon Vieille | ec4ce4c85b | ||
Simon Vieille | 5694d6262f | ||
Simon Vieille | 42511199e4 | ||
Simon Vieille | 165e3a723f |
7
.env
7
.env
|
@ -9,6 +9,7 @@
|
|||
# Real environment variables win over .env files.
|
||||
#
|
||||
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
|
||||
# https://symfony.com/doc/current/configuration/secrets.html
|
||||
#
|
||||
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
|
||||
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
|
||||
|
@ -19,7 +20,7 @@ APP_SECRET=
|
|||
###< symfony/framework-bundle ###
|
||||
|
||||
###> symfony/mailer ###
|
||||
MAILER_DSN=smtp://localhost
|
||||
# MAILER_DSN=smtp://localhost
|
||||
###< symfony/mailer ###
|
||||
|
||||
###> doctrine/doctrine-bundle ###
|
||||
|
@ -27,6 +28,6 @@ MAILER_DSN=smtp://localhost
|
|||
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
|
||||
#
|
||||
DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
|
||||
# DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7"
|
||||
# DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=13&charset=utf8"
|
||||
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8&charset=utf8mb4"
|
||||
# DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=14&charset=utf8"
|
||||
###< doctrine/doctrine-bundle ###
|
||||
|
|
|
@ -4,3 +4,4 @@ APP_SECRET='$ecretf0rt3st'
|
|||
SYMFONY_DEPRECATIONS_HELPER=999999
|
||||
PANTHER_APP_ENV=panther
|
||||
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
|
||||
DATABASE_URL="sqlite:///%kernel.project_dir%/var/data_tests.db"
|
||||
|
|
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -12,7 +12,6 @@
|
|||
###< symfony/framework-bundle ###
|
||||
|
||||
###> symfony/phpunit-bridge ###
|
||||
.phpunit
|
||||
.phpunit.result.cache
|
||||
/phpunit.xml
|
||||
###< symfony/phpunit-bridge ###
|
||||
|
@ -29,3 +28,8 @@ yarn-error.log
|
|||
/public/media/
|
||||
/migrations/*
|
||||
!/migrations/.gitkeep
|
||||
|
||||
###> phpunit/phpunit ###
|
||||
/phpunit.xml
|
||||
.phpunit.result.cache
|
||||
###< phpunit/phpunit ###
|
||||
|
|
49
.woodpecker.yml
Normal file
49
.woodpecker.yml
Normal file
|
@ -0,0 +1,49 @@
|
|||
matrix:
|
||||
PHP_VERSION:
|
||||
- 8.0
|
||||
- 8.1
|
||||
|
||||
services:
|
||||
db:
|
||||
image: mariadb:10.3
|
||||
environment:
|
||||
- MARIADB_ROOT_PASSWORD=root
|
||||
|
||||
steps:
|
||||
db_wait:
|
||||
image: gitnet.fr/deblan/timeout:latest
|
||||
commands:
|
||||
- /bin/timeout -t 30 -v -c 'while true; do nc -z -v db 3306 2>&1 | grep succeeded && exit 0; sleep 0.5; done'
|
||||
|
||||
db_create:
|
||||
image: mariadb:10.3
|
||||
commands:
|
||||
- mysql -hdb -uroot -proot -e "CREATE DATABASE app"
|
||||
|
||||
config:
|
||||
image: deblan/php:${PHP_VERSION}
|
||||
commands:
|
||||
- echo APP_ENV=prod >> .env.local
|
||||
- echo APP_SECRET=$(openssl rand -hex 32) >> .env.local
|
||||
- echo DATABASE_URL=mysql://root:root@db/app >> .env.local
|
||||
|
||||
composer:
|
||||
image: deblan/php:${PHP_VERSION}
|
||||
commands:
|
||||
- apt-get update && apt-get -y install git
|
||||
- composer install --no-scripts
|
||||
|
||||
db_migrate:
|
||||
image: deblan/php:${PHP_VERSION}
|
||||
environment:
|
||||
- PHP=php
|
||||
commands:
|
||||
- ./bin/doctrine-migrate
|
||||
|
||||
node:
|
||||
image: node:16-slim
|
||||
commands:
|
||||
- yarn
|
||||
- test -d public/js || mkdir public/js
|
||||
- test -f public/js/fos_js_routes.json || echo "{}" > public/js/fos_js_routes.json
|
||||
- npm run build
|
139
CHANGELOG.md
139
CHANGELOG.md
|
@ -1,9 +1,148 @@
|
|||
## [Unreleased]
|
||||
|
||||
## [v1.23.0] - 2023-09-28
|
||||
### Changed
|
||||
* upgrade murph/murph-core
|
||||
|
||||
## [v1.22.0] - 2023-09-28
|
||||
### Added
|
||||
* update woodpecker ci base file
|
||||
### Fixed
|
||||
* fix #1: add UniqueEntity constraint in the User entity
|
||||
### Changed
|
||||
* upgrade murph/murph-core
|
||||
|
||||
## [1.21.0] - 2023-08-11
|
||||
### Changed
|
||||
* upgrade murph/murph-core
|
||||
|
||||
## [1.20.0] - 2023-07-27
|
||||
### Fixed
|
||||
* fix collection widget: allow_add/allow_delete and prototype
|
||||
### Added
|
||||
* add user admin controller and simples views in default files
|
||||
* add chdir in the console entrypoint
|
||||
### Changed
|
||||
* upgrade murph/murph-core
|
||||
|
||||
## [1.19.0] - 2023-04-15
|
||||
### Changed
|
||||
* upgrade murph/murph-core
|
||||
|
||||
## [1.18.0] - 2023-01-13
|
||||
### Added
|
||||
* feat(dep): update dependencies
|
||||
* feat(update): apply new recipe for phpunit
|
||||
* feat(update): apply recipes:update doctrine/doctrine-bundle
|
||||
* feat(update): apply recipes:update doctrine/doctrine-migrations-bundle
|
||||
* feat(update): apply recipes:update liip/imagine-bundle
|
||||
* feat(update): apply recipes:update stof/doctrine-extensions-bundle
|
||||
* feat(update): apply recipes:update symfony/apache-pack
|
||||
* feat(update): apply recipes:update symfony/console
|
||||
* feat(update): apply recipes:update symfony/debug-bundle
|
||||
* feat(update): apply recipes:update symfony/flex
|
||||
* feat(update): apply recipes:update symfony/mailer
|
||||
* feat(update): apply recipes:update symfony/framework-bundle
|
||||
* feat(update): apply recipes:update symfony/monolog-bundle
|
||||
* feat(update): apply recipes:update symfony/routing
|
||||
* feat(update): apply recipes:update symfony/security-bundle
|
||||
* feat(update): apply recipes:update symfony/translation
|
||||
* feat(update): apply recipes:update symfony/twig-bundle
|
||||
* feat(update): apply recipes:update symfony/validator
|
||||
* feat(update): apply recipes:update symfony/web-profiler-bundle
|
||||
* feat(update): apply recipes:update symfony/webpack-encore-bundle
|
||||
* feat(update): apply recipes:update scheb/2fa-bundle
|
||||
### Fixed
|
||||
* fix(config): fix typo in 2fa conf
|
||||
* fix(config): fix firewall config
|
||||
|
||||
|
||||
## [1.17.0] - 2022-11-19
|
||||
### Changed
|
||||
* upgrade murph/murph-core
|
||||
* replace annotation with attributes
|
||||
* use encore from node_modules in npm scripts
|
||||
|
||||
## [1.16.0]
|
||||
### Added
|
||||
* add a admin dashboard controller
|
||||
* add meta description in base.html.twig
|
||||
### Fixed
|
||||
### Changed
|
||||
* upgrade murph/murph-core
|
||||
|
||||
## [1.15.0]
|
||||
### Changed
|
||||
* upgrade murph/murph-core
|
||||
|
||||
## [1.14.3]
|
||||
### Added
|
||||
* add blocks in default template
|
||||
### Changed
|
||||
* upgrade murph/murph-core
|
||||
|
||||
## [1.14.2]
|
||||
|
||||
## [1.14.1]
|
||||
### Fixed
|
||||
* fix missing envvar in makefile (npm)
|
||||
|
||||
## [1.14.0]
|
||||
### Changed
|
||||
* upgrade murph/murph-core
|
||||
|
||||
## [1.13.0]
|
||||
### Changed
|
||||
* upgrade murph/murph-core
|
||||
|
||||
## [1.12.0]
|
||||
### Changed
|
||||
* upgrade murph/murph-core
|
||||
|
||||
## [1.11.0]
|
||||
### Changed
|
||||
* upgrade murph/murph-core
|
||||
* use murph-npm to install npm requirements
|
||||
|
||||
## [1.10.0]
|
||||
### Added
|
||||
* add translated title in dashboard template
|
||||
### Fixed
|
||||
* remove useless env var from makefile
|
||||
### Changed
|
||||
* upgrade murph/murph-core
|
||||
|
||||
## [1.9.1] - 2022-03-14
|
||||
### Added
|
||||
* add murph version in autoload file
|
||||
### Changed
|
||||
* remove AdminController constructor
|
||||
|
||||
## [1.9.0] - 2022-03-13
|
||||
### Added
|
||||
* add murph version in admin ui
|
||||
### Changed
|
||||
* the core is now installed with composer
|
||||
|
||||
## [1.8.0] - 2022-03-10
|
||||
### Added
|
||||
* add security roles in app configuration
|
||||
* add option to restrict node access to specific roles
|
||||
### Changed
|
||||
* rename `core/EventSuscriber` with `core/EventSubscriber`
|
||||
|
||||
## [1.7.3] - 2022-03-06
|
||||
### Added
|
||||
* add ability to rename file in the file manager
|
||||
### Fixed
|
||||
* fix user factory
|
||||
* fix user creation from ui
|
||||
|
||||
## [1.7.2] - 2022-03-03
|
||||
### Added
|
||||
* add templates to render sections and items in the admin menu
|
||||
### Fixed
|
||||
* fix the analytic table when a path is a long
|
||||
|
||||
## [1.7.1] - 2022-03-01
|
||||
### Added
|
||||
|
|
1
Makefile
1
Makefile
|
@ -1,6 +1,5 @@
|
|||
COMPOSER_BIN ?= composer
|
||||
PHP_BIN ?= php8.1
|
||||
SSH_BIN ?= ssh
|
||||
YARN_BIN ?= yarn
|
||||
NPM_BIN ?= npm
|
||||
|
||||
|
|
18
README.md
18
README.md
|
@ -1,8 +1,16 @@
|
|||
# MURPH
|
||||
# Murph
|
||||
|
||||
Muprh is an open-source CMF built on top of Symfony that helps you to build your own CMS with several domains and languages. It comes with a fully implemented and customizable tree manager, a CRUD generator, a 2FA authentication, settings and tasks managers.
|
||||
Murph is an **open-source CMF** built on top of Symfony that helps you to **build your own CMS with several domains and languages**. It comes with:
|
||||
|
||||
Symfony developers will love build on Murph 💪
|
||||
End users will be fond of the interface and the powerful tools 💜
|
||||
* A fully implemented and customizable **tree manager** 🌳
|
||||
* A **CRUD generator** ✏️
|
||||
* A global **settings manager** and a navigation settings manager ⚙️
|
||||
* A **tasks manager** 🧹
|
||||
* A basic **web analytics** 📊
|
||||
* **2FA authentication** 🔒
|
||||
|
||||
## [Read the documentation](https://doc.murph-project.org/)
|
||||
**Symfony developers will love build on Murph 🧪**
|
||||
|
||||
**End users will be fond of the interface and the powerful tools 💜**
|
||||
|
||||
📗 [Read the documentation](https://doc.murph-project.org/)
|
||||
|
|
12
SECURITY.md
Normal file
12
SECURITY.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ---------- | --------- |
|
||||
| >= 2.0 | :x: |
|
||||
| >= 1.0 | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you discover a security vulnerability within Murph, send an email to security [at] murph-project.org.
|
62
UPGRADE.md
62
UPGRADE.md
|
@ -1,5 +1,67 @@
|
|||
## General process
|
||||
|
||||
Upgrade dependencies:
|
||||
|
||||
* `composer update`
|
||||
* `yarn upgrade`
|
||||
|
||||
Build:
|
||||
|
||||
* `make build`
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## Upgrade to v1.17.0
|
||||
|
||||
Replace all annotations with PHP8 attributes and change the doctrine configuration:
|
||||
|
||||
```
|
||||
# config/packages/doctrine.yaml
|
||||
doctrine:
|
||||
...
|
||||
orm:
|
||||
...
|
||||
mappings:
|
||||
App\Core\Entity:
|
||||
type: attribute
|
||||
...
|
||||
App\Entity:
|
||||
type: attribute
|
||||
...
|
||||
```
|
||||
|
||||
## Upgrade to v1.15.0
|
||||
|
||||
```
|
||||
cd public/vendor
|
||||
ln -rs ../../node_modules/grapesjs-plugin-export .
|
||||
ln -rs ../../node_modules/grapesjs-parser-postcss .
|
||||
```
|
||||
|
||||
## Upgrade to v1.10.0
|
||||
### Commands
|
||||
|
||||
```
|
||||
make doctrine-migration
|
||||
```
|
||||
|
||||
|
||||
## Upgrade to v1.9.0
|
||||
|
||||
This version uses the package `murph/murph-core`.
|
||||
|
||||
|
||||
## Upgrade to v1.8.0
|
||||
### Commands
|
||||
|
||||
```
|
||||
make doctrine-migration
|
||||
```
|
||||
|
||||
### Files
|
||||
|
||||
Event subscribers in `src/EventSubscriber` must update namespaces.
|
||||
|
||||
## Upgrade to v1.7.0
|
||||
### Commands
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* Custom variables */
|
||||
|
||||
@import "../../core/Resources/assets/css/admin.scss";
|
||||
@import "../../vendor/murph/murph-core/src/core/Resources/assets/css/admin.scss";
|
||||
|
||||
/* Custom CSS */
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
import '../../core/Resources/assets/js/admin.js'
|
||||
import '../../vendor/murph/murph-core/src/core/Resources/assets/js/admin.js'
|
||||
|
|
40
bin/console
40
bin/console
|
@ -3,41 +3,17 @@
|
|||
|
||||
use App\Kernel;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Dotenv\Dotenv;
|
||||
use Symfony\Component\ErrorHandler\Debug;
|
||||
|
||||
if (!in_array(PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
|
||||
echo 'Warning: The console should be invoked via the CLI version of PHP, not the '.PHP_SAPI.' SAPI'.PHP_EOL;
|
||||
if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) {
|
||||
throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".');
|
||||
}
|
||||
|
||||
set_time_limit(0);
|
||||
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
|
||||
|
||||
require dirname(__DIR__).'/vendor/autoload.php';
|
||||
chdir(__DIR__.'/../');
|
||||
|
||||
if (!class_exists(Application::class) || !class_exists(Dotenv::class)) {
|
||||
throw new LogicException('You need to add "symfony/framework-bundle" and "symfony/dotenv" as Composer dependencies.');
|
||||
}
|
||||
return function (array $context) {
|
||||
$kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
|
||||
|
||||
$input = new ArgvInput();
|
||||
if (null !== $env = $input->getParameterOption(['--env', '-e'], null, true)) {
|
||||
putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env);
|
||||
}
|
||||
|
||||
if ($input->hasParameterOption('--no-debug', true)) {
|
||||
putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0');
|
||||
}
|
||||
|
||||
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
|
||||
|
||||
if ($_SERVER['APP_DEBUG']) {
|
||||
umask(0000);
|
||||
|
||||
if (class_exists(Debug::class)) {
|
||||
Debug::enable();
|
||||
}
|
||||
}
|
||||
|
||||
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
|
||||
$application = new Application($kernel);
|
||||
$application->run($input);
|
||||
return new Application($kernel);
|
||||
};
|
||||
|
|
20
bin/phpunit
20
bin/phpunit
|
@ -1,13 +1,19 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
if (!file_exists(dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php')) {
|
||||
echo "Unable to find the `simple-phpunit.php` script in `vendor/symfony/phpunit-bridge/bin/`.\n";
|
||||
exit(1);
|
||||
if (!ini_get('date.timezone')) {
|
||||
ini_set('date.timezone', 'UTC');
|
||||
}
|
||||
|
||||
if (false === getenv('SYMFONY_PHPUNIT_DIR')) {
|
||||
putenv('SYMFONY_PHPUNIT_DIR='.__DIR__.'/.phpunit');
|
||||
}
|
||||
if (is_file(dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit')) {
|
||||
define('PHPUNIT_COMPOSER_INSTALL', dirname(__DIR__).'/vendor/autoload.php');
|
||||
require PHPUNIT_COMPOSER_INSTALL;
|
||||
PHPUnit\TextUI\Command::main();
|
||||
} else {
|
||||
if (!is_file(dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php')) {
|
||||
echo "Unable to find the `simple-phpunit.php` script in `vendor/symfony/phpunit-bridge/bin/`.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php';
|
||||
require dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php';
|
||||
}
|
||||
|
|
|
@ -7,66 +7,14 @@
|
|||
"prefer-stable": true,
|
||||
"require": {
|
||||
"php": ">=8.0.0",
|
||||
"ext-ctype": "*",
|
||||
"ext-iconv": "*",
|
||||
"bjeavons/zxcvbn-php": "^1.3",
|
||||
"cocur/slugify": "^4.1",
|
||||
"composer/package-versions-deprecated": "1.11.99.1",
|
||||
"doctrine/annotations": "^1.0",
|
||||
"doctrine/doctrine-bundle": "^2.5",
|
||||
"doctrine/doctrine-migrations-bundle": "^3.2",
|
||||
"doctrine/orm": "^2.11",
|
||||
"friendsofsymfony/jsrouting-bundle": "^2.8",
|
||||
"jaybizzle/crawler-detect": "^1.2",
|
||||
"knplabs/doctrine-behaviors": "^2.6",
|
||||
"knplabs/knp-paginator-bundle": "^5.8",
|
||||
"liip/imagine-bundle": "^2.7",
|
||||
"matomo/device-detector": "^5.0",
|
||||
"phpdocumentor/reflection-docblock": "^5.3",
|
||||
"scheb/2fa-google-authenticator": "^5.13",
|
||||
"scheb/2fa-qr-code": "^5.13",
|
||||
"sensio/framework-extra-bundle": "^6.2",
|
||||
"sensiolabs/ansi-to-html": "^1.2",
|
||||
"spe/filesize-extension-bundle": "~2.0.0",
|
||||
"stof/doctrine-extensions-bundle": "^1.7",
|
||||
"symfony/apache-pack": "^1.0",
|
||||
"symfony/asset": "5.4.*",
|
||||
"symfony/console": "5.4.*",
|
||||
"symfony/dotenv": "5.4.*",
|
||||
"symfony/event-dispatcher": "5.4.*",
|
||||
"symfony/expression-language": "5.4.*",
|
||||
"symfony/finder": "5.4.*",
|
||||
"symfony/flex": "^1.3.1",
|
||||
"symfony/form": "5.4.*",
|
||||
"symfony/framework-bundle": "5.4.*",
|
||||
"symfony/http-client": "5.4.*",
|
||||
"symfony/intl": "5.4.*",
|
||||
"symfony/mailer": "5.4.*",
|
||||
"symfony/mime": "5.4.*",
|
||||
"symfony/monolog-bundle": "^3.1",
|
||||
"symfony/notifier": "5.4.*",
|
||||
"symfony/process": "5.4.*",
|
||||
"symfony/property-access": "5.4.*",
|
||||
"symfony/property-info": "5.4.*",
|
||||
"symfony/proxy-manager-bridge": "5.4.*",
|
||||
"symfony/security-bundle": "5.4.*",
|
||||
"symfony/serializer": "5.4.*",
|
||||
"symfony/string": "5.4.*",
|
||||
"symfony/translation": "5.4.*",
|
||||
"symfony/twig-bundle": "^5.2",
|
||||
"symfony/validator": "5.4.*",
|
||||
"symfony/web-link": "5.4.*",
|
||||
"symfony/webpack-encore-bundle": "^1.11",
|
||||
"symfony/yaml": "5.4.*",
|
||||
"twig/extra-bundle": "^2.12|^3.3",
|
||||
"twig/twig": "^2.12|^3.3"
|
||||
"murph/murph-core": "^1.23"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/browser-kit": "^5.4",
|
||||
"symfony/css-selector": "^5.4",
|
||||
"symfony/debug-bundle": "^5.4",
|
||||
"symfony/maker-bundle": "^1.0",
|
||||
"symfony/phpunit-bridge": "^5.4",
|
||||
"symfony/phpunit-bridge": "^6.2",
|
||||
"symfony/stopwatch": "^5.4",
|
||||
"symfony/var-dumper": "^5.4",
|
||||
"symfony/web-profiler-bundle": "^5.4"
|
||||
|
@ -78,13 +26,13 @@
|
|||
},
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"symfony/flex": true
|
||||
"symfony/flex": true,
|
||||
"symfony/runtime": true
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "src/",
|
||||
"App\\Core\\": "core/"
|
||||
"App\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
|
|
|
@ -6,7 +6,7 @@ return [
|
|||
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
|
||||
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
||||
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
|
||||
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true],
|
||||
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true],
|
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
||||
|
|
|
@ -9,11 +9,16 @@ core:
|
|||
name: 'Simple page'
|
||||
templates:
|
||||
- {name: "Default", file: "page/simple/default.html.twig"}
|
||||
# security:
|
||||
# roles:
|
||||
# - {name: 'Role foo', role: 'ROLE_FOO'}
|
||||
# - {name: 'Role bar', role: 'ROLE_BAR'}
|
||||
file_manager:
|
||||
# mimes:
|
||||
# - image/png
|
||||
# - image/jpg
|
||||
# - image/jpeg
|
||||
# - image/webp
|
||||
# - image/gif
|
||||
# - image/svg+xml
|
||||
# - video/mp4
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
framework:
|
||||
assets:
|
||||
json_manifest_path: '%kernel.project_dir%/public/build/manifest.json'
|
5
config/packages/debug.yaml
Normal file
5
config/packages/debug.yaml
Normal file
|
@ -0,0 +1,5 @@
|
|||
when@dev:
|
||||
debug:
|
||||
# Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser.
|
||||
# See the "server:dump" command to start a new server.
|
||||
dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%"
|
|
@ -1,4 +0,0 @@
|
|||
debug:
|
||||
# Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser.
|
||||
# See the "server:dump" command to start a new server.
|
||||
dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%"
|
|
@ -1,19 +0,0 @@
|
|||
monolog:
|
||||
handlers:
|
||||
main:
|
||||
type: stream
|
||||
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||
level: debug
|
||||
channels: ["!event"]
|
||||
# uncomment to get logging in your browser
|
||||
# you may have to allow bigger header sizes in your Web server configuration
|
||||
#firephp:
|
||||
# type: firephp
|
||||
# level: info
|
||||
#chromephp:
|
||||
# type: chromephp
|
||||
# level: info
|
||||
console:
|
||||
type: console
|
||||
process_psr_3_messages: false
|
||||
channels: ["!event", "!doctrine", "!console"]
|
|
@ -1,6 +0,0 @@
|
|||
web_profiler:
|
||||
toolbar: true
|
||||
intercept_redirects: false
|
||||
|
||||
framework:
|
||||
profiler: { only_exceptions: false }
|
|
@ -4,7 +4,7 @@ doctrine:
|
|||
|
||||
# IMPORTANT: You MUST configure your server version,
|
||||
# either here or in the DATABASE_URL env var (see .env file)
|
||||
#server_version: '13'
|
||||
#server_version: '14'
|
||||
orm:
|
||||
auto_generate_proxy_classes: true
|
||||
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
|
||||
|
@ -12,13 +12,13 @@ doctrine:
|
|||
mappings:
|
||||
App\Core\Entity:
|
||||
is_bundle: false
|
||||
type: annotation
|
||||
dir: '%kernel.project_dir%/core/Entity'
|
||||
type: attribute
|
||||
dir: '%kernel.project_dir%/vendor/murph/murph-core/src/core/Entity'
|
||||
prefix: 'App\Core\Entity'
|
||||
alias: App\Core\Entity
|
||||
App\Entity:
|
||||
is_bundle: false
|
||||
type: annotation
|
||||
type: attribute
|
||||
dir: '%kernel.project_dir%/src/Entity'
|
||||
prefix: 'App\Entity'
|
||||
alias: App\Entity
|
||||
|
@ -28,3 +28,29 @@ doctrine:
|
|||
dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Tree/Entity"
|
||||
alias: GedmoTree # (optional) it will default to the name set for the mapping
|
||||
is_bundle: false
|
||||
|
||||
when@test:
|
||||
doctrine:
|
||||
dbal:
|
||||
# "TEST_TOKEN" is typically set by ParaTest
|
||||
dbname_suffix: '_test%env(default::TEST_TOKEN)%'
|
||||
|
||||
when@prod:
|
||||
doctrine:
|
||||
orm:
|
||||
auto_generate_proxy_classes: false
|
||||
proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies'
|
||||
query_cache_driver:
|
||||
type: pool
|
||||
pool: doctrine.system_cache_pool
|
||||
result_cache_driver:
|
||||
type: pool
|
||||
pool: doctrine.result_cache_pool
|
||||
|
||||
framework:
|
||||
cache:
|
||||
pools:
|
||||
doctrine.result_cache_pool:
|
||||
adapter: cache.app
|
||||
doctrine.system_cache_pool:
|
||||
adapter: cache.system
|
||||
|
|
|
@ -3,3 +3,4 @@ doctrine_migrations:
|
|||
# namespace is arbitrary but should be different from App\Migrations
|
||||
# as migrations classes should NOT be autoloaded
|
||||
'DoctrineMigrations': '%kernel.project_dir%/migrations'
|
||||
enable_profiler: false
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
framework:
|
||||
secret: '%env(APP_SECRET)%'
|
||||
#csrf_protection: true
|
||||
#http_method_override: true
|
||||
http_method_override: false
|
||||
|
||||
# Enables session support. Note that the session will ONLY be started if you read or write from it.
|
||||
# Remove or comment this section to explicitly disable session support.
|
||||
|
@ -10,8 +10,15 @@ framework:
|
|||
handler_id: null
|
||||
cookie_secure: auto
|
||||
cookie_samesite: lax
|
||||
storage_factory_id: session.storage.factory.native
|
||||
|
||||
#esi: true
|
||||
#fragments: true
|
||||
php_errors:
|
||||
log: true
|
||||
|
||||
when@test:
|
||||
framework:
|
||||
test: true
|
||||
session:
|
||||
storage_factory_id: session.storage.factory.mock_file
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# See dos how to configure the bundle: https://symfony.com/doc/current/bundles/LiipImagineBundle/basic-usage.html
|
||||
# Documentation on how to configure the bundle can be found at: https://symfony.com/doc/current/bundles/LiipImagineBundle/basic-usage.html
|
||||
liip_imagine:
|
||||
# valid drivers options include "gd" or "gmagick" or "imagick"
|
||||
driver: "imagick"
|
||||
|
|
61
config/packages/monolog.yaml
Normal file
61
config/packages/monolog.yaml
Normal file
|
@ -0,0 +1,61 @@
|
|||
monolog:
|
||||
channels:
|
||||
- deprecation # Deprecations are logged in the dedicated "deprecation" channel when it exists
|
||||
|
||||
when@dev:
|
||||
monolog:
|
||||
handlers:
|
||||
main:
|
||||
type: stream
|
||||
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||
level: debug
|
||||
channels: ["!event"]
|
||||
# uncomment to get logging in your browser
|
||||
# you may have to allow bigger header sizes in your Web server configuration
|
||||
#firephp:
|
||||
# type: firephp
|
||||
# level: info
|
||||
#chromephp:
|
||||
# type: chromephp
|
||||
# level: info
|
||||
console:
|
||||
type: console
|
||||
process_psr_3_messages: false
|
||||
channels: ["!event", "!doctrine", "!console"]
|
||||
|
||||
when@test:
|
||||
monolog:
|
||||
handlers:
|
||||
main:
|
||||
type: fingers_crossed
|
||||
action_level: error
|
||||
handler: nested
|
||||
excluded_http_codes: [404, 405]
|
||||
channels: ["!event"]
|
||||
nested:
|
||||
type: stream
|
||||
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||
level: debug
|
||||
|
||||
when@prod:
|
||||
monolog:
|
||||
handlers:
|
||||
main:
|
||||
type: fingers_crossed
|
||||
action_level: error
|
||||
handler: nested
|
||||
excluded_http_codes: [404, 405]
|
||||
buffer_size: 50 # How many messages should be saved? Prevent memory leaks
|
||||
nested:
|
||||
type: stream
|
||||
path: php://stderr
|
||||
level: debug
|
||||
formatter: monolog.formatter.json
|
||||
console:
|
||||
type: console
|
||||
process_psr_3_messages: false
|
||||
channels: ["!event", "!doctrine"]
|
||||
deprecation:
|
||||
type: stream
|
||||
channels: [deprecation]
|
||||
path: php://stderr
|
|
@ -1,8 +0,0 @@
|
|||
# As of Symfony 5.1, deprecations are logged in the dedicated "deprecation" channel when it exists
|
||||
#monolog:
|
||||
# channels: [deprecation]
|
||||
# handlers:
|
||||
# deprecation:
|
||||
# type: stream
|
||||
# channels: [deprecation]
|
||||
# path: "%kernel.logs_dir%/%kernel.environment%.deprecations.log"
|
|
@ -1,20 +0,0 @@
|
|||
doctrine:
|
||||
orm:
|
||||
auto_generate_proxy_classes: false
|
||||
metadata_cache_driver:
|
||||
type: pool
|
||||
pool: doctrine.system_cache_pool
|
||||
query_cache_driver:
|
||||
type: pool
|
||||
pool: doctrine.system_cache_pool
|
||||
result_cache_driver:
|
||||
type: pool
|
||||
pool: doctrine.result_cache_pool
|
||||
|
||||
framework:
|
||||
cache:
|
||||
pools:
|
||||
doctrine.result_cache_pool:
|
||||
adapter: cache.app
|
||||
doctrine.system_cache_pool:
|
||||
adapter: cache.system
|
|
@ -1,16 +0,0 @@
|
|||
monolog:
|
||||
handlers:
|
||||
main:
|
||||
type: fingers_crossed
|
||||
action_level: error
|
||||
handler: nested
|
||||
excluded_http_codes: [404, 405]
|
||||
buffer_size: 50 # How many messages should be saved? Prevent memory leaks
|
||||
nested:
|
||||
type: stream
|
||||
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||
level: debug
|
||||
console:
|
||||
type: console
|
||||
process_psr_3_messages: false
|
||||
channels: ["!event", "!doctrine"]
|
|
@ -1,3 +0,0 @@
|
|||
framework:
|
||||
router:
|
||||
strict_requirements: null
|
|
@ -1,4 +0,0 @@
|
|||
#webpack_encore:
|
||||
# Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
|
||||
# Available in version 1.2
|
||||
#cache: true
|
|
@ -5,3 +5,8 @@ framework:
|
|||
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
|
||||
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
|
||||
#default_uri: http://localhost
|
||||
|
||||
when@prod:
|
||||
framework:
|
||||
router:
|
||||
strict_requirements: null
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
scheb_two_factor:
|
||||
google:
|
||||
enabled: true
|
||||
issuer: "Muprh"
|
||||
issuer: "Murph"
|
||||
server_name:
|
||||
digits: 6
|
||||
window: 1
|
||||
|
|
|
@ -3,7 +3,16 @@ security:
|
|||
App\Entity\User:
|
||||
algorithm: auto
|
||||
|
||||
access_decision_manager:
|
||||
strategy: consensus
|
||||
allow_if_all_abstain: false
|
||||
|
||||
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
|
||||
enable_authenticator_manager: true
|
||||
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
|
||||
password_hashers:
|
||||
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
|
||||
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
|
||||
providers:
|
||||
# used to reload user from session & other features (e.g. switch_user)
|
||||
app_user_provider:
|
||||
|
@ -20,7 +29,6 @@ security:
|
|||
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
||||
security: false
|
||||
main:
|
||||
anonymous: ~
|
||||
two_factor:
|
||||
auth_form_path: 2fa_login # The route name you have used in the routes.yaml
|
||||
check_path: 2fa_login_check # The route name you have used in the routes.yaml
|
||||
|
@ -30,7 +38,7 @@ security:
|
|||
form_login:
|
||||
login_path: auth_login
|
||||
check_path: auth_login
|
||||
csrf_token_generator: security.csrf.token_manager
|
||||
enable_csrf: true
|
||||
logout:
|
||||
path: auth_logout
|
||||
target: /
|
||||
|
@ -39,6 +47,8 @@ security:
|
|||
lifetime: 604800
|
||||
path: /
|
||||
|
||||
entry_point: form_login
|
||||
|
||||
# Easy way to control access for large sections of your site
|
||||
# Note: Only the *first* access control that matches will be used
|
||||
access_control:
|
||||
|
@ -52,3 +62,16 @@ security:
|
|||
- { path: ^/admin/file_manager, roles: ROLE_WRITER }
|
||||
- { path: ^/admin, roles: ROLE_USER }
|
||||
- { path: ^/_internal, roles: IS_AUTHENTICATED_ANONYMOUSLY }
|
||||
|
||||
when@test:
|
||||
security:
|
||||
password_hashers:
|
||||
# By default, password hashers are resource intensive and take time. This is
|
||||
# important to generate secure password hashes. In tests however, secure hashes
|
||||
# are not important, waste resources and increase test times. The following
|
||||
# reduces the work factor to the lowest possible values.
|
||||
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
|
||||
algorithm: auto
|
||||
cost: 4 # Lowest possible value for bcrypt
|
||||
time_cost: 3 # Lowest possible value for argon
|
||||
memory_cost: 10 # Lowest possible value for argon
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Read the documentation: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html
|
||||
# See the official DoctrineExtensions documentation for more details: https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc/
|
||||
# See the official DoctrineExtensions documentation for more details: https://github.com/doctrine-extensions/DoctrineExtensions/tree/main/doc
|
||||
stof_doctrine_extensions:
|
||||
default_locale: en_US
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
framework:
|
||||
test: true
|
||||
session:
|
||||
storage_id: session.storage.mock_file
|
|
@ -1,12 +0,0 @@
|
|||
monolog:
|
||||
handlers:
|
||||
main:
|
||||
type: fingers_crossed
|
||||
action_level: error
|
||||
handler: nested
|
||||
excluded_http_codes: [404, 405]
|
||||
channels: ["!event"]
|
||||
nested:
|
||||
type: stream
|
||||
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||
level: debug
|
|
@ -1,2 +0,0 @@
|
|||
twig:
|
||||
strict_variables: true
|
|
@ -1,3 +0,0 @@
|
|||
framework:
|
||||
validation:
|
||||
not_compromised_password: false
|
|
@ -1,6 +0,0 @@
|
|||
web_profiler:
|
||||
toolbar: false
|
||||
intercept_redirects: false
|
||||
|
||||
framework:
|
||||
profiler: { collect: false }
|
|
@ -1,2 +0,0 @@
|
|||
#webpack_encore:
|
||||
# strict_mode: false
|
|
@ -3,6 +3,13 @@ framework:
|
|||
translator:
|
||||
default_path: '%kernel.project_dir%/translations'
|
||||
paths:
|
||||
- '%kernel.project_dir%/core/Resources/translations'
|
||||
- '%kernel.project_dir%/vendor/murph/murph-core/src/core/Resources/translations'
|
||||
fallbacks:
|
||||
- en
|
||||
# providers:
|
||||
# crowdin:
|
||||
# dsn: '%env(CROWDIN_DSN)%'
|
||||
# loco:
|
||||
# dsn: '%env(LOCO_DSN)%'
|
||||
# lokalise:
|
||||
# dsn: '%env(LOKALISE_DSN)%'
|
||||
|
|
|
@ -3,4 +3,8 @@ twig:
|
|||
form_themes: ['@Core/form/bootstrap_4_form_theme.html.twig']
|
||||
paths:
|
||||
'%kernel.project_dir%/templates/core/': Core
|
||||
'%kernel.project_dir%/core/Resources/views/': Core
|
||||
'%kernel.project_dir%/vendor/murph/murph-core/src/core/Resources/views/': Core
|
||||
|
||||
when@test:
|
||||
twig:
|
||||
strict_variables: true
|
||||
|
|
|
@ -6,3 +6,8 @@ framework:
|
|||
# For instance, basic validation constraints will be inferred from Doctrine's metadata.
|
||||
#auto_mapping:
|
||||
# App\Entity\: []
|
||||
|
||||
when@test:
|
||||
framework:
|
||||
validation:
|
||||
not_compromised_password: false
|
||||
|
|
15
config/packages/web_profiler.yaml
Normal file
15
config/packages/web_profiler.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
when@dev:
|
||||
web_profiler:
|
||||
toolbar: true
|
||||
intercept_redirects: false
|
||||
|
||||
framework:
|
||||
profiler: { only_exceptions: false }
|
||||
|
||||
when@test:
|
||||
web_profiler:
|
||||
toolbar: false
|
||||
intercept_redirects: false
|
||||
|
||||
framework:
|
||||
profiler: { collect: false }
|
|
@ -7,7 +7,12 @@ webpack_encore:
|
|||
# Set attributes that will be rendered on all script and link tags
|
||||
script_attributes:
|
||||
defer: true
|
||||
# Uncomment (also under link_attributes) if using Turbo Drive
|
||||
# https://turbo.hotwired.dev/handbook/drive#reloading-when-assets-change
|
||||
# 'data-turbo-track': reload
|
||||
# link_attributes:
|
||||
# Uncomment if using Turbo Drive
|
||||
# 'data-turbo-track': reload
|
||||
|
||||
# If using Encore.enableIntegrityHashes() and need the crossorigin attribute (default: false, or use 'anonymous' or 'use-credentials')
|
||||
# crossorigin: 'anonymous'
|
||||
|
@ -20,11 +25,21 @@ webpack_encore:
|
|||
|
||||
# If you have multiple builds:
|
||||
# builds:
|
||||
# pass "frontend" as the 3rg arg to the Twig functions
|
||||
# {{ encore_entry_script_tags('entry1', null, 'frontend') }}
|
||||
|
||||
# frontend: '%kernel.project_dir%/public/frontend/build'
|
||||
|
||||
# Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
|
||||
# Put in config/packages/prod/webpack_encore.yaml
|
||||
# cache: true
|
||||
# pass the build name as the 3rd argument to the Twig functions
|
||||
# {{ encore_entry_script_tags('entry1', null, 'frontend') }}
|
||||
|
||||
framework:
|
||||
assets:
|
||||
json_manifest_path: '%kernel.project_dir%/public/build/manifest.json'
|
||||
|
||||
#when@prod:
|
||||
# webpack_encore:
|
||||
# # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
|
||||
# # Available in version 1.2
|
||||
# cache: true
|
||||
|
||||
#when@test:
|
||||
# webpack_encore:
|
||||
# strict_mode: false
|
||||
|
|
|
@ -3,7 +3,7 @@ controllers:
|
|||
type: annotation
|
||||
|
||||
core_controllers:
|
||||
resource: ../../core/Controller/
|
||||
resource: ../../vendor/murph/murph-core/src/core/Controller/
|
||||
type: annotation
|
||||
|
||||
kernel:
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
_errors:
|
||||
resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
|
||||
prefix: /_error
|
|
@ -1,7 +0,0 @@
|
|||
web_profiler_wdt:
|
||||
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
|
||||
prefix: /_wdt
|
||||
|
||||
web_profiler_profiler:
|
||||
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
|
||||
prefix: /_profiler
|
4
config/routes/framework.yaml
Normal file
4
config/routes/framework.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
when@dev:
|
||||
_errors:
|
||||
resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
|
||||
prefix: /_error
|
|
@ -1,7 +1,7 @@
|
|||
2fa_login:
|
||||
path: /2fa
|
||||
defaults:
|
||||
_controller: "scheb_two_factor.form_controller:form"
|
||||
_controller: "scheb_two_factor.form_controller::form"
|
||||
|
||||
2fa_login_check:
|
||||
path: /2fa_check
|
||||
|
|
8
config/routes/web_profiler.yaml
Normal file
8
config/routes/web_profiler.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
when@dev:
|
||||
web_profiler_wdt:
|
||||
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
|
||||
prefix: /_wdt
|
||||
|
||||
web_profiler_profiler:
|
||||
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
|
||||
prefix: /_profiler
|
|
@ -2,7 +2,7 @@
|
|||
# Files in the packages/ subdirectory configure your dependencies.
|
||||
|
||||
# Put parameters here that don't need to change on each machine where the app is deployed
|
||||
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
|
||||
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
|
||||
parameters:
|
||||
|
||||
services:
|
||||
|
@ -11,22 +11,29 @@ services:
|
|||
autowire: true # Automatically injects dependencies in your services.
|
||||
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
|
||||
|
||||
# makes classes in src/ available to be used as services
|
||||
# this creates a service per class whose id is the fully-qualified class name
|
||||
# Murph services
|
||||
App\Core\:
|
||||
resource: '../core/'
|
||||
resource: '../vendor/murph/murph-core/src/core/'
|
||||
exclude:
|
||||
- '../core/DependencyInjection/'
|
||||
- '../core/Entity/'
|
||||
- '../vendor/murph/murph-core/src/core/DependencyInjection/'
|
||||
- '../vendor/murph/murph-core/src/core/Entity/'
|
||||
|
||||
# Redirections
|
||||
App\Core\EventListener\RedirectListener:
|
||||
tags:
|
||||
- { name: kernel.event_listener, event: kernel.exception }
|
||||
|
||||
# Analytics
|
||||
App\Core\EventListener\AnalyticListener:
|
||||
tags:
|
||||
- { name: kernel.event_listener, event: kernel.request }
|
||||
|
||||
# A/B Testing
|
||||
App\Core\EventListener\AbListener:
|
||||
tags:
|
||||
- { name: kernel.event_listener, event: kernel.request }
|
||||
- { name: kernel.event_listener, event: kernel.response }
|
||||
|
||||
App\:
|
||||
resource: '../src/'
|
||||
exclude:
|
||||
|
@ -36,11 +43,11 @@ services:
|
|||
- '../src/Tests/'
|
||||
|
||||
App\Core\Maker\:
|
||||
resource: '../core/Maker/'
|
||||
resource: '../vendor/murph/murph-core/src/core/Maker/'
|
||||
tags: ['maker.command']
|
||||
|
||||
App\Core\Controller\:
|
||||
resource: '../core/Controller/'
|
||||
resource: '../vendor/murph/murph-core/src/core/Controller/'
|
||||
tags: ['controller.service_arguments']
|
||||
|
||||
App\Controller\:
|
||||
|
@ -58,8 +65,8 @@ services:
|
|||
calls:
|
||||
- [ setAnnotationReader, [ "@annotation_reader" ] ]
|
||||
|
||||
App\UrlGenerator\FooUrlGenerator:
|
||||
public: true
|
||||
# App\UrlGenerator\FooUrlGenerator:
|
||||
# public: true
|
||||
|
||||
# add more service definitions when explicit configuration is needed
|
||||
# please note that last definitions always *replace* previous ones
|
||||
|
|
|
@ -1,204 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Analytic;
|
||||
|
||||
use App\Core\Entity\Site\Node;
|
||||
use App\Core\Repository\Analytic\RefererRepositoryQuery;
|
||||
use App\Core\Repository\Analytic\ViewRepositoryQuery;
|
||||
|
||||
/**
|
||||
* class DateRangeAnalytic.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class DateRangeAnalytic
|
||||
{
|
||||
protected ViewRepositoryQuery $viewQuery;
|
||||
protected RefererRepositoryQuery $refererQuery;
|
||||
protected ?Node $node;
|
||||
protected ?\DateTime $from;
|
||||
protected ?\DateTime $to;
|
||||
protected bool $reload = true;
|
||||
protected array $cache = [];
|
||||
|
||||
public function __construct(ViewRepositoryQuery $viewQuery, RefererRepositoryQuery $refererQuery)
|
||||
{
|
||||
$this->viewQuery = $viewQuery;
|
||||
$this->refererQuery = $refererQuery;
|
||||
}
|
||||
|
||||
public function getViews(): array
|
||||
{
|
||||
$entities = $this->getEntities('view');
|
||||
$this->reload = false;
|
||||
|
||||
if ($entities) {
|
||||
$first = $entities[0];
|
||||
$last = $entities[count($entities) - 1];
|
||||
|
||||
$diff = $first->getDate()->diff($last->getDate());
|
||||
|
||||
if ($diff->days >= 90) {
|
||||
$format = 'Y-m';
|
||||
} else {
|
||||
$format = 'Y-m-d';
|
||||
}
|
||||
}
|
||||
|
||||
$datas = [];
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
$index = $entity->getDate()->format($format);
|
||||
|
||||
if (!isset($datas[$index])) {
|
||||
$datas[$index] = 0;
|
||||
}
|
||||
|
||||
$datas[$index] += $entity->getViews();
|
||||
}
|
||||
|
||||
return $datas;
|
||||
}
|
||||
|
||||
public function getPathViews(): array
|
||||
{
|
||||
$entities = $this->getEntities('view');
|
||||
$this->reload = false;
|
||||
|
||||
$datas = [];
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
$index = $entity->getPath();
|
||||
|
||||
if (!isset($datas[$index])) {
|
||||
$datas[$index] = [
|
||||
'views' => 0,
|
||||
'desktopViews' => 0,
|
||||
'mobileViews' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$datas[$index]['views'] += $entity->getViews();
|
||||
$datas[$index]['desktopViews'] += $entity->getDesktopViews();
|
||||
$datas[$index]['mobileViews'] += $entity->getMobileViews();
|
||||
}
|
||||
|
||||
uasort($datas, function($a, $b) {
|
||||
if ($a['views'] > $b['views']) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ($a['views'] < $b['views']) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
return $datas;
|
||||
}
|
||||
|
||||
public function getReferers(): array
|
||||
{
|
||||
$entities = $this->getEntities('referer');
|
||||
$this->reload = false;
|
||||
|
||||
$datas = [];
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
$index = parse_url($entity->getUri(), PHP_URL_HOST);
|
||||
|
||||
if (!isset($datas[$index])) {
|
||||
$datas[$index] = [
|
||||
'views' => 0,
|
||||
'uris' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$datas[$index]['views'] += $entity->getViews();
|
||||
|
||||
$path = parse_url($entity->getUri(), PHP_URL_PATH);
|
||||
|
||||
if (empty($path)) {
|
||||
$path = '/';
|
||||
}
|
||||
|
||||
if (!isset($datas[$index]['uris'][$path])) {
|
||||
$datas[$index]['uris'][$path] = 0;
|
||||
}
|
||||
|
||||
$datas[$index]['uris'][$path] += $entity->getViews();
|
||||
}
|
||||
|
||||
uasort($datas, function($a, $b) {
|
||||
if ($a['views'] > $b['views']) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ($a['views'] < $b['views']) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
return $datas;
|
||||
}
|
||||
|
||||
public function setDateRange(?\DateTime $from, ?\DateTime $to): self
|
||||
{
|
||||
$this->from = $from;
|
||||
$this->to = $to;
|
||||
$this->reload = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setNode(?Node $node): self
|
||||
{
|
||||
$this->node = $node;
|
||||
$this->reload = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getEntities(string $type): array
|
||||
{
|
||||
if ('view' === $type) {
|
||||
$query = $this->viewQuery->create();
|
||||
} elseif ('referer' === $type) {
|
||||
$query = $this->refererQuery->create();
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Invalid type');
|
||||
}
|
||||
|
||||
if (!$this->reload && isset($this->cache[$type])) {
|
||||
return $this->cache[$type];
|
||||
}
|
||||
|
||||
if (null !== $this->from) {
|
||||
$query
|
||||
->andWhere('.date >= :from')
|
||||
->setParameter(':from', $this->from)
|
||||
;
|
||||
}
|
||||
|
||||
if (null !== $this->to) {
|
||||
$query
|
||||
->andWhere('.date <= :to')
|
||||
->setParameter(':to', $this->to)
|
||||
;
|
||||
}
|
||||
|
||||
if (null !== $this->node) {
|
||||
$query
|
||||
->andWhere('.node = :node')
|
||||
->setParameter(':node', $this->node->getId())
|
||||
;
|
||||
}
|
||||
|
||||
$this->cache[$type] = $query->orderBy('.date')->find();
|
||||
|
||||
return $this->cache[$type];
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* class UrlGenerator.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
* @Annotation
|
||||
*/
|
||||
class UrlGenerator
|
||||
{
|
||||
public string $service;
|
||||
|
||||
public string $method;
|
||||
|
||||
public array $options = [];
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Authenticator;
|
||||
|
||||
use App\Entity\User;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
|
||||
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
|
||||
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||
use Symfony\Component\Security\Csrf\CsrfToken;
|
||||
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
|
||||
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
|
||||
use Symfony\Component\Security\Http\Util\TargetPathTrait;
|
||||
|
||||
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
|
||||
{
|
||||
use TargetPathTrait;
|
||||
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
private UrlGeneratorInterface $urlGenerator;
|
||||
|
||||
private CsrfTokenManagerInterface $csrfTokenManager;
|
||||
|
||||
private UserPasswordEncoderInterface $passwordEncoder;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->csrfTokenManager = $csrfTokenManager;
|
||||
$this->passwordEncoder = $passwordEncoder;
|
||||
}
|
||||
|
||||
public function supports(Request $request)
|
||||
{
|
||||
return 'auth_login' === $request->attributes->get('_route') && $request->isMethod('POST');
|
||||
}
|
||||
|
||||
public function getCredentials(Request $request)
|
||||
{
|
||||
$credentials = [
|
||||
'email' => $request->request->get('_username'),
|
||||
'password' => $request->request->get('_password'),
|
||||
'csrf_token' => $request->request->get('_csrf_token'),
|
||||
];
|
||||
|
||||
$request->getSession()->set(Security::LAST_USERNAME, $credentials['email']);
|
||||
|
||||
return $credentials;
|
||||
}
|
||||
|
||||
public function getUser($credentials, UserProviderInterface $userProvider)
|
||||
{
|
||||
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
|
||||
|
||||
if (!$this->csrfTokenManager->isTokenValid($token)) {
|
||||
throw new InvalidCsrfTokenException();
|
||||
}
|
||||
|
||||
$user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);
|
||||
|
||||
if (!$user) {
|
||||
// fail authentication with a custom error
|
||||
throw new CustomUserMessageAuthenticationException('Email could not be found.');
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function checkCredentials($credentials, UserInterface $user)
|
||||
{
|
||||
return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
|
||||
}
|
||||
|
||||
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
|
||||
{
|
||||
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
|
||||
return new RedirectResponse($targetPath);
|
||||
}
|
||||
|
||||
return new RedirectResponse($this->urlGenerator->generate('admin_dashboard_index'));
|
||||
}
|
||||
|
||||
protected function getLoginUrl()
|
||||
{
|
||||
return $this->urlGenerator->generate('auth_login');
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Core\Bundle;
|
||||
|
||||
use App\Core\DependencyInjection\CoreExtension;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
|
||||
|
||||
class CoreBundle extends Bundle
|
||||
{
|
||||
public function getContainerExtension(): ?ExtensionInterface
|
||||
{
|
||||
return new CoreExtension();
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Cache;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\BufferedOutput;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\HttpClient\Exception\ClientException;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\HttpClient\Exception\TransportException;
|
||||
|
||||
/**
|
||||
* class SymfonyCacheManager.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class SymfonyCacheManager
|
||||
{
|
||||
protected KernelInterface $kernel;
|
||||
protected HttpClientInterface $httpClient;
|
||||
protected UrlGeneratorInterface $urlGenerator;
|
||||
|
||||
public function __construct(KernelInterface $kernel, HttpClientInterface $httpClient, UrlGeneratorInterface $urlGenerator)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
$this->httpClient = $httpClient;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
}
|
||||
|
||||
public function cleanRouting()
|
||||
{
|
||||
$finder = new Finder();
|
||||
$finder
|
||||
->in($this->kernel->getCacheDir())
|
||||
->depth('== 0')
|
||||
->name('url_*.php*')
|
||||
;
|
||||
|
||||
$pingUrl = $this->urlGenerator->generate('_ping', [], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
|
||||
foreach ($finder as $file) {
|
||||
unlink((string) $file->getPathname());
|
||||
}
|
||||
|
||||
try {
|
||||
// Hack: used to regenerate cache of url generator
|
||||
$this->httpClient->request('POST', $pingUrl);
|
||||
} catch (ClientException $e) {
|
||||
} catch (TransportException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
public function cleanAll(OutputInterface $output = null)
|
||||
{
|
||||
$application = new Application($this->kernel);
|
||||
$application->setAutoExit(false);
|
||||
|
||||
if (null === $output) {
|
||||
$output = new BufferedOutput();
|
||||
}
|
||||
|
||||
$input = new ArrayInput([
|
||||
'command' => 'cache:warmup',
|
||||
'-e' => $this->kernel->getEnvironment(),
|
||||
]);
|
||||
|
||||
$application->run($input, $output);
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Command;
|
||||
|
||||
use App\Core\Factory\UserFactory;
|
||||
use App\Core\Manager\EntityManager;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface;
|
||||
|
||||
class UserCreateCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'murph:user:create';
|
||||
protected static $defaultDescription = 'Creates a user';
|
||||
protected UserFactory $userFactory;
|
||||
protected EntityManager $entityManager;
|
||||
protected TokenGeneratorInterface $tokenGenerator;
|
||||
|
||||
public function __construct(
|
||||
UserFactory $userFactory,
|
||||
EntityManager $entityManager,
|
||||
TokenGeneratorInterface $tokenGenerator
|
||||
) {
|
||||
$this->userFactory = $userFactory;
|
||||
$this->entityManager = $entityManager;
|
||||
$this->tokenGenerator = $tokenGenerator;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setDescription(self::$defaultDescription)
|
||||
->addArgument('email', InputArgument::OPTIONAL, 'E-mail')
|
||||
->addOption('is-admin', null, InputOption::VALUE_NONE, 'Add the admin role')
|
||||
->addOption('is-writer', null, InputOption::VALUE_NONE, 'Add the write role')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$helper = $this->getHelper('question');
|
||||
|
||||
$emailQuestion = new Question('E-mail: ');
|
||||
$emailQuestion->setValidator(function ($value) {
|
||||
if (empty($value)) {
|
||||
throw new \RuntimeException('The email must not be empty.');
|
||||
}
|
||||
|
||||
return $value;
|
||||
});
|
||||
|
||||
$passwordQuestion = new Question('Password (leave empty to generate a random password): ');
|
||||
$passwordQuestion->setHidden(true);
|
||||
|
||||
$isAdminDefault = $input->getOption('is-admin');
|
||||
$isWriterDefault = $input->getOption('is-writer');
|
||||
|
||||
$isAdminQuestionLabel = sprintf('Administrator [%s] ', $isAdminDefault ? 'Y/n' : 'y/N');
|
||||
$isWriterQuestionLabel = sprintf('Writer [%s] ', $isWriterDefault ? 'Y/n' : 'y/N');
|
||||
|
||||
$isAdminQuestion = new ConfirmationQuestion($isAdminQuestionLabel, $isAdminDefault);
|
||||
$isWriterQuestion = new ConfirmationQuestion($isWriterQuestionLabel, $isWriterDefault);
|
||||
|
||||
$io->section('Authentication');
|
||||
|
||||
$email = $input->getArgument('email');
|
||||
|
||||
if (empty($email)) {
|
||||
$email = $helper->ask($input, $output, $emailQuestion);
|
||||
}
|
||||
|
||||
$password = $helper->ask($input, $output, $passwordQuestion);
|
||||
|
||||
$showPassword = empty($password);
|
||||
|
||||
if ($showPassword) {
|
||||
$password = mb_substr($this->tokenGenerator->generateToken(), 0, 18);
|
||||
$io->info(sprintf('Password: %s', $password));
|
||||
} else {
|
||||
$io->newLine();
|
||||
}
|
||||
|
||||
$io->section('Roles');
|
||||
|
||||
$isAdmin = $helper->ask($input, $output, $isAdminQuestion);
|
||||
$isWriter = $helper->ask($input, $output, $isWriterQuestion);
|
||||
|
||||
$user = $this->userFactory->create($email, $password);
|
||||
$user->setIsAdmin($isAdmin);
|
||||
$user->setIsWriter($isWriter);
|
||||
|
||||
$this->entityManager->create($user);
|
||||
|
||||
$io->newLine();
|
||||
$io->success('User created!');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Account;
|
||||
|
||||
use App\Core\Controller\Admin\AdminController;
|
||||
use App\Core\Manager\EntityManager;
|
||||
use App\Repository\UserRepository;
|
||||
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticatorInterface;
|
||||
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticatorInterface as TotpAuthenticatorInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
|
||||
use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface;
|
||||
use ZxcvbnPhp\Zxcvbn;
|
||||
|
||||
/**
|
||||
* @Route("/admin/account")
|
||||
*/
|
||||
class AccountAdminController extends AdminController
|
||||
{
|
||||
/**
|
||||
* @Route("/", name="admin_account")
|
||||
*/
|
||||
public function account(Request $request, TotpAuthenticatorInterface $totpAuthenticatorService): Response
|
||||
{
|
||||
$account = $this->getUser();
|
||||
|
||||
return $this->render('@Core/account/admin/edit.html.twig', [
|
||||
'account' => $account,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/2fa", name="admin_account_2fa")
|
||||
*/
|
||||
public function twoFactorAuthentication(
|
||||
Request $request,
|
||||
GoogleAuthenticatorInterface $totpAuthenticatorService,
|
||||
EntityManager $entityManager
|
||||
): Response {
|
||||
if ($request->isMethod('GET')) {
|
||||
return $this->redirectToRoute('admin_account');
|
||||
}
|
||||
|
||||
$account = $this->getUser();
|
||||
$csrfToken = $request->request->get('_csrf_token');
|
||||
$enable = (bool) $request->request->get('enable');
|
||||
$code = $request->request->get('code', '');
|
||||
$secret = $request->request->get('secret', '');
|
||||
$qrCodeContent = null;
|
||||
|
||||
if ($this->isCsrfTokenValid('2fa', $csrfToken)) {
|
||||
if ($enable && !$account->isTotpAuthenticationEnabled()) {
|
||||
if (empty($secret)) {
|
||||
$secret = $totpAuthenticatorService->generateSecret();
|
||||
|
||||
$account->setTotpSecret($secret);
|
||||
|
||||
$qrCodeContent = $totpAuthenticatorService->getQRContent($account);
|
||||
} else {
|
||||
$account->setTotpSecret($secret);
|
||||
|
||||
$qrCodeContent = $totpAuthenticatorService->getQRContent($account);
|
||||
|
||||
if (!$totpAuthenticatorService->checkCode($account, $code)) {
|
||||
$this->addFlash('error', 'The code is not valid.');
|
||||
} else {
|
||||
$this->addFlash('success', 'Double authentication enabled.');
|
||||
|
||||
$entityManager->update($account);
|
||||
|
||||
return $this->redirectToRoute('admin_account');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$enable && $account->isTotpAuthenticationEnabled()) {
|
||||
$account->setTotpSecret(null);
|
||||
|
||||
$entityManager->update($account);
|
||||
|
||||
$this->addFlash('success', 'Double authentication disabled.');
|
||||
|
||||
return $this->redirectToRoute('admin_account');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('@Core/account/admin/edit.html.twig', [
|
||||
'account' => $account,
|
||||
'twoFaKey' => $secret,
|
||||
'twoFaQrCodeContent' => $qrCodeContent,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/password", name="admin_account_password", methods={"POST"})
|
||||
*/
|
||||
public function password(
|
||||
Request $request,
|
||||
UserRepository $repository,
|
||||
TokenGeneratorInterface $tokenGenerator,
|
||||
UserPasswordEncoderInterface $encoder,
|
||||
EntityManager $entityManager
|
||||
): Response {
|
||||
$account = $this->getUser();
|
||||
$csrfToken = $request->request->get('_csrf_token');
|
||||
|
||||
if ($this->isCsrfTokenValid('password', $csrfToken)) {
|
||||
$password = $request->request->get('password');
|
||||
|
||||
if (!$encoder->isPasswordValid($account, $password)) {
|
||||
$this->addFlash('error', 'The form is not valid.');
|
||||
|
||||
return $this->redirectToRoute('admin_account');
|
||||
}
|
||||
|
||||
$password1 = $request->request->get('password1');
|
||||
$password2 = $request->request->get('password2');
|
||||
|
||||
$zxcvbn = new Zxcvbn();
|
||||
$strength = $zxcvbn->passwordStrength($password1, []);
|
||||
|
||||
if (4 === $strength['score'] && $password1 === $password2) {
|
||||
$account
|
||||
->setPassword($encoder->encodePassword($account, $password1))
|
||||
->setConfirmationToken($tokenGenerator->generateToken())
|
||||
;
|
||||
|
||||
$entityManager->update($account);
|
||||
|
||||
$this->addFlash('success', 'Password updated.');
|
||||
|
||||
return $this->redirectToRoute('admin_account');
|
||||
}
|
||||
}
|
||||
|
||||
$this->addFlash('error', 'The form is not valid.');
|
||||
|
||||
return $this->redirectToRoute('admin_account');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSection(): string
|
||||
{
|
||||
return 'account';
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Admin;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
abstract class AdminController extends AbstractController
|
||||
{
|
||||
protected array $coreParameters;
|
||||
|
||||
public function __construct(ParameterBagInterface $parameters)
|
||||
{
|
||||
$this->coreParameters = $parameters->get('core');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/_ping", name="_ping")
|
||||
*/
|
||||
public function ping()
|
||||
{
|
||||
return $this->json(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function render(string $view, array $parameters = [], Response $response = null): Response
|
||||
{
|
||||
$parameters['section'] = $this->getSection();
|
||||
$parameters['site_name'] = $this->coreParameters['site']['name'];
|
||||
$parameters['site_logo'] = $this->coreParameters['site']['logo'];
|
||||
|
||||
return parent::render($view, $parameters, $response);
|
||||
}
|
||||
|
||||
abstract protected function getSection(): string;
|
||||
}
|
|
@ -1,342 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Admin\Crud;
|
||||
|
||||
use App\Core\Controller\Admin\AdminController;
|
||||
use App\Core\Crud\CrudConfiguration;
|
||||
use App\Core\Entity\EntityInterface;
|
||||
use App\Core\Manager\EntityManager;
|
||||
use App\Core\Repository\RepositoryQuery;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
|
||||
/**
|
||||
* class CrudController.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
abstract class CrudController extends AdminController
|
||||
{
|
||||
protected array $filters = [];
|
||||
protected array $sort = [
|
||||
'label' => null,
|
||||
'direction' => null,
|
||||
];
|
||||
|
||||
abstract protected function getConfiguration(): CrudConfiguration;
|
||||
|
||||
protected function doIndex(int $page = 1, RepositoryQuery $query, Request $request, Session $session): Response
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
|
||||
$this->applySort('index', $query, $request);
|
||||
$this->updateFilters($request, $session);
|
||||
|
||||
$pager = $query
|
||||
->usefilters($this->filters)
|
||||
->paginate($page, $configuration->getmaxperpage('index'))
|
||||
;
|
||||
|
||||
return $this->render($this->getConfiguration()->getView('index'), [
|
||||
'configuration' => $configuration,
|
||||
'pager' => $pager,
|
||||
'sort' => $this->sort,
|
||||
'filters' => [
|
||||
'show' => null !== $configuration->getForm('filter'),
|
||||
'isEmpty' => empty($this->filters),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
protected function doNew(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeCreate = null): Response
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
|
||||
$this->prepareEntity($entity);
|
||||
|
||||
$form = $this->createForm($configuration->getForm('new'), $entity, $configuration->getFormOptions('new'));
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
if (null !== $beforeCreate) {
|
||||
call_user_func_array($beforeCreate, [$entity, $form, $request]);
|
||||
}
|
||||
|
||||
$entityManager->create($entity);
|
||||
$this->addFlash('success', 'The data has been saved.');
|
||||
|
||||
return $this->redirectToRoute($configuration->getPageRoute('edit'), array_merge(
|
||||
['entity' => $entity->getId()],
|
||||
$configuration->getPageRouteParams('edit')
|
||||
));
|
||||
}
|
||||
$this->addFlash('warning', 'The form is not valid.');
|
||||
}
|
||||
|
||||
return $this->render($configuration->getView('new'), [
|
||||
'form' => $form->createView(),
|
||||
'configuration' => $configuration,
|
||||
'entity' => $entity,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function doShow(EntityInterface $entity): Response
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
|
||||
return $this->render($configuration->getView('show'), [
|
||||
'entity' => $entity,
|
||||
'configuration' => $configuration,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function doEdit(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeUpdate = null): Response
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
|
||||
$this->prepareEntity($entity);
|
||||
|
||||
$form = $this->createForm($configuration->getForm('edit'), $entity, $configuration->getFormOptions('edit'));
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
if (null !== $beforeUpdate) {
|
||||
call_user_func_array($beforeUpdate, [$entity, $form, $request]);
|
||||
}
|
||||
|
||||
$entityManager->update($entity);
|
||||
$this->addFlash('success', 'The data has been saved.');
|
||||
|
||||
return $this->redirectToRoute($configuration->getPageRoute('edit'), array_merge(
|
||||
['entity' => $entity->getId()],
|
||||
$configuration->getPageRouteParams('edit')
|
||||
));
|
||||
}
|
||||
$this->addFlash('warning', 'The form is not valid.');
|
||||
}
|
||||
|
||||
return $this->render($configuration->getView('edit'), [
|
||||
'form' => $form->createView(),
|
||||
'configuration' => $configuration,
|
||||
'entity' => $entity,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function doSort(int $page = 1, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
$context = $request->query->get('context', 'index');
|
||||
|
||||
if (!$configuration->getIsSortableCollection($context)) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
$this->applySort($context, $query, $request);
|
||||
$this->updateFilters($request, $session);
|
||||
|
||||
$pager = $query
|
||||
->useFilters($this->filters)
|
||||
->paginate($page, $configuration->getMaxPerPage($context))
|
||||
;
|
||||
|
||||
if ($this->isCsrfTokenValid('sort', $request->query->get('_token'))) {
|
||||
$items = $request->request->get('items', []);
|
||||
$setter = 'set'.$configuration->getSortableCollectionProperty();
|
||||
$orderStart = ($page - 1) * $configuration->getMaxPerPage($context);
|
||||
|
||||
foreach ($pager as $key => $entity) {
|
||||
if (isset($items[$key + 1])) {
|
||||
$entity->{$setter}($items[$key + 1] + $orderStart);
|
||||
|
||||
$entityManager->update($entity);
|
||||
}
|
||||
}
|
||||
|
||||
$this->addFlash('success', 'The data has been saved.');
|
||||
} else {
|
||||
$this->addFlash('warning', 'The form is not valid.');
|
||||
}
|
||||
|
||||
return $this->json([]);
|
||||
}
|
||||
|
||||
protected function doBatch(int $page = 1, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
$datas = $request->request->get('batch', []);
|
||||
|
||||
$context = $datas['context'] ?? 'index';
|
||||
$target = $datas['target'] ?? null;
|
||||
$action = $datas['action'] ?? null;
|
||||
$token = $datas['_token'] ?? null;
|
||||
$items = $datas['items'] ?? [];
|
||||
$batchAction = $configuration->getBatchAction($context, $action);
|
||||
|
||||
if (empty($context) || empty($action) || empty($target)) {
|
||||
return $this->json([]);
|
||||
}
|
||||
|
||||
if (!$this->isCsrfTokenValid('batch', $token) || empty($batchAction)) {
|
||||
$this->addFlash('warning', 'The form is not valid.');
|
||||
|
||||
return $this->json([]);
|
||||
}
|
||||
|
||||
$callback = $batchAction['callback'];
|
||||
|
||||
$this->applySort($context, $query, $request);
|
||||
$this->updateFilters($request, $session);
|
||||
|
||||
$query->useFilters($this->filters);
|
||||
|
||||
if ('selection' === $target) {
|
||||
$isSelection = true;
|
||||
$pager = $query->paginate($page, $configuration->getMaxPerPage($context));
|
||||
} else {
|
||||
$isSelection = false;
|
||||
$pager = $query->find();
|
||||
}
|
||||
|
||||
foreach ($pager as $key => $entity) {
|
||||
if (($isSelection && isset($items[$key + 1])) || !$isSelection) {
|
||||
$callback($entity, $entityManager);
|
||||
}
|
||||
}
|
||||
|
||||
$this->addFlash('success', 'Batch action done.');
|
||||
|
||||
return $this->json([]);
|
||||
}
|
||||
|
||||
protected function doDelete(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeDelete = null): Response
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
|
||||
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
||||
if (null !== $beforeDelete) {
|
||||
call_user_func($beforeDelete, $entity);
|
||||
}
|
||||
|
||||
$entityManager->delete($entity);
|
||||
|
||||
$this->addFlash('success', 'The data has been removed.');
|
||||
}
|
||||
|
||||
return $this->redirectToRoute($configuration->getPageRoute('index'));
|
||||
}
|
||||
|
||||
protected function doFilter(Session $session): Response
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
$type = $configuration->getForm('filter');
|
||||
|
||||
if (null === $type) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
$form = $this->createForm($type);
|
||||
$form->submit($session->get($form->getName(), []));
|
||||
|
||||
return $this->render($configuration->getView('filter'), [
|
||||
'form' => $form->createView(),
|
||||
'configuration' => $configuration,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function updateFilters(Request $request, Session $session)
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
$type = $configuration->getForm('filter');
|
||||
|
||||
if (null === $type) {
|
||||
return;
|
||||
}
|
||||
|
||||
$form = $this->createForm($type);
|
||||
|
||||
if ($request->query->has($form->getName())) {
|
||||
$filters = $request->query->get($form->getName());
|
||||
|
||||
if ('0' === $filters) {
|
||||
$filters = [];
|
||||
}
|
||||
} elseif ($session->has($form->getName())) {
|
||||
$filters = $session->get($form->getName());
|
||||
} else {
|
||||
$filters = [];
|
||||
}
|
||||
|
||||
$form->submit($filters);
|
||||
|
||||
if (empty($filters)) {
|
||||
$this->filters = $filters;
|
||||
$session->set($form->getName(), $filters);
|
||||
} elseif ($form->isValid()) {
|
||||
$this->filters = $form->getData();
|
||||
$session->set($form->getName(), $filters);
|
||||
}
|
||||
}
|
||||
|
||||
protected function prepareEntity(EntityInterface $entity)
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
|
||||
if ($configuration->isI18n()) {
|
||||
foreach ($configuration->getLocales() as $locale) {
|
||||
$entity->addTranslation($entity->translate($locale, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function applySort(string $context, RepositoryQuery $query, Request $request)
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
|
||||
if ($configuration->getIsSortableCollection($context)) {
|
||||
$query->orderBy(sprintf('.%s', $configuration->getSortableCollectionProperty()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$defaultSort = $configuration->getDefaultSort($context);
|
||||
|
||||
$name = $request->query->get('_sort', $defaultSort['label'] ?? null);
|
||||
$direction = strtolower($request->query->get('_sort_direction', $defaultSort['direction'] ?? 'asc'));
|
||||
|
||||
if (!in_array($direction, ['asc', 'desc'])) {
|
||||
$direction = 'asc';
|
||||
}
|
||||
|
||||
foreach ($configuration->getFields($context) as $label => $field) {
|
||||
$sortOption = $field['options']['sort'] ?? null;
|
||||
|
||||
if (null === $sortOption) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($sortOption[0] !== $name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sorter = $sortOption[1];
|
||||
|
||||
if (is_string($sorter)) {
|
||||
$query->orderBy($sorter, $direction);
|
||||
} else {
|
||||
call_user_func_array($sorter, [$query, $direction]);
|
||||
}
|
||||
|
||||
$this->sort = [
|
||||
'label' => $label,
|
||||
'direction' => $direction,
|
||||
];
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Analytic;
|
||||
|
||||
use App\Core\Analytic\DateRangeAnalytic;
|
||||
use App\Core\Entity\Site\Node;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* @Route("/admin/analytic")
|
||||
*/
|
||||
class AnalyticController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @Route("/stats/{node}/{range}", name="admin_analytic_stats")
|
||||
*/
|
||||
public function stats(Node $node, DateRangeAnalytic $analytic, string $range = '7days'): Response
|
||||
{
|
||||
if (!in_array($range, ['7days', '30days', '90days', '1year'])) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
$analytic
|
||||
->setDateRange(new \DateTime('now - '.$range), new \DateTime())
|
||||
->setNode($node)
|
||||
;
|
||||
|
||||
return $this->render('@Core/analytic/stats.html.twig', [
|
||||
'range' => $range,
|
||||
'views' => $analytic->getViews(),
|
||||
'pathViews' => $analytic->getPathViews(),
|
||||
'referers' => $analytic->getReferers(),
|
||||
'node' => $node,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,155 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Auth;
|
||||
|
||||
use App\Core\Event\Account\PasswordRequestEvent;
|
||||
use App\Core\Manager\EntityManager;
|
||||
use App\Repository\UserRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
|
||||
use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
use ZxcvbnPhp\Zxcvbn;
|
||||
|
||||
class AuthController extends AbstractController
|
||||
{
|
||||
protected array $coreParameters;
|
||||
|
||||
public function __construct(ParameterBagInterface $parameters)
|
||||
{
|
||||
$this->coreParameters = $parameters->get('core');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/login", name="auth_login")
|
||||
*/
|
||||
public function login(AuthenticationUtils $authenticationUtils): Response
|
||||
{
|
||||
if ($this->getUser()) {
|
||||
return $this->redirectToRoute('admin_dashboard_index');
|
||||
}
|
||||
|
||||
$error = $authenticationUtils->getLastAuthenticationError();
|
||||
$lastUsername = $authenticationUtils->getLastUsername();
|
||||
|
||||
return $this->render('@Core/auth/login.html.twig', [
|
||||
'last_username' => $lastUsername,
|
||||
'error' => $error,
|
||||
'site_name' => $this->coreParameters['site']['name'],
|
||||
'site_logo' => $this->coreParameters['site']['logo'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/resetting/request", name="auth_resetting_request")
|
||||
*/
|
||||
public function requestResetting(Request $request, UserRepository $repository, EventDispatcherInterface $eventDispatcher): Response
|
||||
{
|
||||
if ($this->getUser()) {
|
||||
return $this->redirectToRoute('admin_dashboard_index');
|
||||
}
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
$csrfToken = $request->request->get('_csrf_token');
|
||||
|
||||
if (!$this->isCsrfTokenValid('resetting_request', $csrfToken)) {
|
||||
throw $this->createAccessDeniedException();
|
||||
}
|
||||
|
||||
$username = trim((string) $request->request->get('username'));
|
||||
|
||||
if (!$username) {
|
||||
throw $this->createAccessDeniedException();
|
||||
}
|
||||
|
||||
$account = $repository->findOneByEmail($username);
|
||||
|
||||
if ($account) {
|
||||
$requestedAt = $account->getPasswordRequestedAt();
|
||||
|
||||
if (null === $requestedAt || $requestedAt->getTimestamp() < (time() - 3600 / 2)) {
|
||||
$eventDispatcher->dispatch(new PasswordRequestEvent($account), PasswordRequestEvent::EVENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('@Core/auth/resetting_request.html.twig', [
|
||||
'email_sent' => $request->isMethod('POST'),
|
||||
'site_name' => $this->coreParameters['site']['name'],
|
||||
'site_logo' => $this->coreParameters['site']['logo'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/resetting/update/{token}", name="auth_resetting_update")
|
||||
*/
|
||||
public function requestUpdate(
|
||||
string $token,
|
||||
Request $request,
|
||||
UserRepository $repository,
|
||||
TokenGeneratorInterface $tokenGenerator,
|
||||
UserPasswordEncoderInterface $encoder,
|
||||
EntityManager $entityManager
|
||||
): Response {
|
||||
if ($this->getUser()) {
|
||||
return $this->redirectToRoute('admin_dashboard_index');
|
||||
}
|
||||
|
||||
$account = $repository->findOneByConfirmationToken($token);
|
||||
$passwordUpdated = false;
|
||||
$expired = true;
|
||||
|
||||
if ($account) {
|
||||
$requestedAt = $account->getPasswordRequestedAt();
|
||||
$expired = (null === $requestedAt || ($requestedAt->getTimestamp() < (time() - 3600 * 2)));
|
||||
}
|
||||
|
||||
if ($request->isMethod('POST') && !$expired) {
|
||||
$csrfToken = $request->request->get('_csrf_token');
|
||||
|
||||
if ($this->isCsrfTokenValid('resetting_update', $csrfToken)) {
|
||||
$password = $request->request->get('password');
|
||||
$password2 = $request->request->get('password2');
|
||||
|
||||
$zxcvbn = new Zxcvbn();
|
||||
$strength = $zxcvbn->passwordStrength($password, []);
|
||||
|
||||
if (4 === $strength['score'] && $password === $password2) {
|
||||
$account
|
||||
->setPassword($encoder->encodePassword(
|
||||
$account,
|
||||
$password
|
||||
))
|
||||
->setConfirmationToken($tokenGenerator->generateToken())
|
||||
->setPasswordRequestedAt(new \DateTime('now'))
|
||||
;
|
||||
|
||||
$entityManager->update($account);
|
||||
|
||||
$passwordUpdated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('@Core/auth/resetting_update.html.twig', [
|
||||
'password_updated' => $passwordUpdated,
|
||||
'token' => $token,
|
||||
'expired' => $expired,
|
||||
'site_name' => $this->coreParameters['site']['name'],
|
||||
'site_logo' => $this->coreParameters['site']['logo'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/logout", name="auth_logout")
|
||||
*/
|
||||
public function logout()
|
||||
{
|
||||
throw new \Exception('This method can be blank - it will be intercepted by the logout key on your firewall');
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Dashboard;
|
||||
|
||||
use App\Core\Controller\Admin\AdminController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* @Route("/admin")
|
||||
*/
|
||||
class DashboardAdminController extends AdminController
|
||||
{
|
||||
/**
|
||||
* @Route("/", name="admin_dashboard_index")
|
||||
*/
|
||||
public function index(): Response
|
||||
{
|
||||
return $this->render('@Core/dashboard/index.html.twig', [
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getSection(): string
|
||||
{
|
||||
return 'dashboard';
|
||||
}
|
||||
}
|
|
@ -1,363 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\FileManager;
|
||||
|
||||
use App\Core\Controller\Admin\AdminController;
|
||||
use App\Core\FileManager\FsFileManager;
|
||||
use App\Core\Form\FileManager\DirectoryCreateType;
|
||||
use App\Core\Form\FileManager\DirectoryRenameType;
|
||||
use App\Core\Form\FileManager\FileInformationType;
|
||||
use App\Core\Form\FileManager\FileUploadType;
|
||||
use App\Core\Manager\EntityManager;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @Route("/admin/file_manager")
|
||||
*/
|
||||
class FileManagerAdminController extends AdminController
|
||||
{
|
||||
/**
|
||||
* @Route("/", name="admin_file_manager_index")
|
||||
*/
|
||||
public function index(): Response
|
||||
{
|
||||
return $this->render('@Core/file_manager/index.html.twig');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/api/directory", name="admin_file_manager_api_directory", options={"expose"=true})
|
||||
*/
|
||||
public function directory(FsFileManager $manager, Request $request): Response
|
||||
{
|
||||
$options = [
|
||||
'sort' => $request->query->get('_sort', 'name'),
|
||||
'sort_direction' => $request->query->get('_sort_direction', 'asc'),
|
||||
];
|
||||
|
||||
$files = $manager->list($request->query->get('directory', '/'), $options);
|
||||
|
||||
return $this->json($files);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/info/{tab}/{context}/{ajax}", name="admin_file_manager_info", options={"expose"=true})
|
||||
*/
|
||||
public function info(
|
||||
FsFileManager $manager,
|
||||
Request $request,
|
||||
EntityManager $entityManager,
|
||||
TranslatorInterface $translator,
|
||||
string $context = 'crud',
|
||||
string $tab = 'information',
|
||||
bool $ajax = false
|
||||
): Response {
|
||||
$splInfo = $manager->getSplInfo($request->query->get('file'));
|
||||
|
||||
if (!$splInfo) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
$fileInfo = $manager->getFileInformation($request->query->get('file'));
|
||||
$path = $manager->getPathUri().'/'.$splInfo->getRelativePathname();
|
||||
|
||||
$form = $this->createForm(FileInformationType::class, $fileInfo);
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
$entityManager->update($fileInfo);
|
||||
|
||||
if (!$request->isXmlHttpRequest()) {
|
||||
$this->addFlash('success', 'The data has been saved.');
|
||||
} else {
|
||||
return $this->json([
|
||||
'_error' => 0,
|
||||
'_message' => $translator->trans('The data has been saved.'),
|
||||
'_level' => 'success',
|
||||
'_dispatch' => 'file_manager.info.update.success',
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
if (!$request->isXmlHttpRequest()) {
|
||||
$this->addFlash('warning', 'The form is not valid.');
|
||||
} else {
|
||||
return $this->json([
|
||||
'_error' => 1,
|
||||
'_message' => $translator->trans('The form is not valid.'),
|
||||
'_level' => 'warning',
|
||||
'_dispatch' => 'file_manager.info.update.error',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('admin_file_manager_index', [
|
||||
'data-modal' => $this->generateUrl('admin_file_manager_info', [
|
||||
'file' => $request->query->get('file'),
|
||||
'tab' => 'attributes',
|
||||
]),
|
||||
'path' => $splInfo->getRelativePath(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->render('@Core/file_manager/info.html.twig', [
|
||||
'splInfo' => $splInfo,
|
||||
'path' => $path,
|
||||
'isLocked' => $manager->isLocked($splInfo->getRelativePathname()),
|
||||
'tab' => $tab,
|
||||
'form' => $form->createView(),
|
||||
'context' => $context,
|
||||
'ajax' => $ajax,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/directory/new/{ajax}", name="admin_file_manager_directory_new", options={"expose"=true}, methods={"GET", "POST"})
|
||||
*/
|
||||
public function directoryNew(FsFileManager $manager, Request $request, TranslatorInterface $translator, bool $ajax = false): Response
|
||||
{
|
||||
$splInfo = $manager->getSplInfo($request->query->get('file'));
|
||||
|
||||
if (!$splInfo) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
if (!$splInfo->isDir()) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
if ($manager->isLocked($request->query->get('file'))) {
|
||||
return $this->render('@Core/file_manager/directory_new.html.twig', [
|
||||
'locked' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
$form = $this->createForm(DirectoryCreateType::class);
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
$status = $manager->createDirectory($form->get('name')->getData(), $request->query->get('file'));
|
||||
|
||||
if (true === $status) {
|
||||
if (!$request->isXmlHttpRequest()) {
|
||||
$this->addFlash('success', 'Directory created.');
|
||||
} else {
|
||||
return $this->json([
|
||||
'_error' => 0,
|
||||
'_message' => $translator->trans('Directory created.'),
|
||||
'_level' => 'success',
|
||||
'_dispatch' => 'file_manager.directory.new.success',
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
if (!$request->isXmlHttpRequest()) {
|
||||
$this->addFlash('warning', 'Directory not created.');
|
||||
} else {
|
||||
return $this->json([
|
||||
'_error' => 1,
|
||||
'_message' => $translator->trans('Directory not created.'),
|
||||
'_level' => 'warning',
|
||||
'_dispatch' => 'file_manager.directory.new.error',
|
||||
]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->addFlash('warning', 'Unauthorized char(s).');
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('admin_file_manager_index', [
|
||||
'path' => $splInfo->getRelativePathname(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->render('@Core/file_manager/directory_new.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'file' => $request->query->get('file'),
|
||||
'ajax' => $ajax,
|
||||
'locked' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/directory/rename/{ajax}", name="admin_file_manager_directory_rename", methods={"GET", "POST"})
|
||||
*/
|
||||
public function directoryRename(FsFileManager $manager, Request $request, TranslatorInterface $translator, bool $ajax = false): Response
|
||||
{
|
||||
$splInfo = $manager->getSplInfo($request->query->get('file'));
|
||||
|
||||
if (!$splInfo) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
if (!$splInfo->isDir()) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
if ($manager->isLocked($request->query->get('file'))) {
|
||||
return $this->render('@Core/file_manager/directory_rename.html.twig', [
|
||||
'locked' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
$form = $this->createForm(DirectoryRenameType::class);
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
$status = $manager->renameDirectory($form->get('name')->getData(), $request->query->get('file'));
|
||||
|
||||
if (true === $status) {
|
||||
if (!$request->isXmlHttpRequest()) {
|
||||
$this->addFlash('success', 'Directory renamed.');
|
||||
} else {
|
||||
return $this->json([
|
||||
'_error' => 0,
|
||||
'_message' => $translator->trans('Directory renamed.'),
|
||||
'_level' => 'success',
|
||||
'_dispatch' => 'file_manager.directory.rename.success',
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
if (!$request->isXmlHttpRequest()) {
|
||||
$this->addFlash('warning', 'Directory not renamed.');
|
||||
} else {
|
||||
return $this->json([
|
||||
'_error' => 1,
|
||||
'_message' => $translator->trans('Directory not renamed.'),
|
||||
'_level' => 'warning',
|
||||
'_dispatch' => 'file_manager.directory.rename.error',
|
||||
]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->addFlash('warning', 'Unauthorized char(s).');
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('admin_file_manager_index', [
|
||||
'path' => $splInfo->getRelativePath(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->render('@Core/file_manager/directory_rename.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'file' => $request->query->get('file'),
|
||||
'locked' => false,
|
||||
'ajax' => $ajax,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/upload/{ajax}", name="admin_file_manager_upload", options={"expose"=true}, methods={"GET", "POST"})
|
||||
*/
|
||||
public function upload(FsFileManager $manager, Request $request, TranslatorInterface $translator, bool $ajax = false): Response
|
||||
{
|
||||
$splInfo = $manager->getSplInfo($request->query->get('file'));
|
||||
|
||||
if (!$splInfo) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
if (!$splInfo->isDir()) {
|
||||
throw $this->createAccessDeniedException();
|
||||
}
|
||||
|
||||
if ($manager->isLocked($request->query->get('file'))) {
|
||||
return $this->render('@Core/file_manager/upload.html.twig', [
|
||||
'locked' => true,
|
||||
]);
|
||||
}
|
||||
$form = $this->createForm(FileUploadType::class, null, [
|
||||
'mimes' => $manager->getMimes(),
|
||||
]);
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
if ($form->get('files')->getData()) {
|
||||
$manager->upload(
|
||||
$form->get('files')->getData(),
|
||||
$request->query->get('file')
|
||||
);
|
||||
}
|
||||
|
||||
if ($form->get('directory')->getData()) {
|
||||
$manager->upload(
|
||||
$form->get('directory')->getData(),
|
||||
$request->query->get('file'),
|
||||
$_FILES['file_upload']['full_path']['directory'] ?? []
|
||||
);
|
||||
}
|
||||
|
||||
if (!$request->isXmlHttpRequest()) {
|
||||
$this->addFlash('success', 'Files uploaded.');
|
||||
} else {
|
||||
return $this->json([
|
||||
'_error' => 0,
|
||||
'_message' => $translator->trans('Files uploaded.'),
|
||||
'_level' => 'success',
|
||||
'_dispatch' => 'file_manager.file.new.success',
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
if (!$request->isXmlHttpRequest()) {
|
||||
$this->addFlash('warning', 'Unauthorized file type(s).');
|
||||
} else {
|
||||
return $this->json([
|
||||
'_error' => 1,
|
||||
'_message' => $translator->trans('Unauthorized file type(s).'),
|
||||
'_level' => 'warning',
|
||||
'_dispatch' => 'file_manager.file.new.error',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('admin_file_manager_index', [
|
||||
'path' => $splInfo->getRelativePathname(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->render('@Core/file_manager/upload.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'file' => $request->query->get('file'),
|
||||
'locked' => false,
|
||||
'ajax' => $ajax,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/delete", name="admin_file_manager_delete", methods={"DELETE"})
|
||||
*/
|
||||
public function delete(FsFileManager $manager, Request $request): Response
|
||||
{
|
||||
$path = $request->request->get('file');
|
||||
$splInfo = $manager->getSplInfo($request->request->get('file'));
|
||||
|
||||
if (!$splInfo) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
if ($this->isCsrfTokenValid('delete', $request->request->get('_token'))) {
|
||||
if ($manager->delete($path)) {
|
||||
$this->addFlash('success', 'The data has been removed.');
|
||||
} else {
|
||||
$this->addFlash('warning', 'The data has not been removed.');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('admin_file_manager_index', [
|
||||
'path' => $splInfo->getRelativePath(),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getSection(): string
|
||||
{
|
||||
return 'file_manager';
|
||||
}
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Redirect;
|
||||
|
||||
use App\Core\Controller\Admin\Crud\CrudController;
|
||||
use App\Core\Crud\CrudConfiguration;
|
||||
use App\Core\Crud\Field;
|
||||
use App\Core\Entity\EntityInterface;
|
||||
use App\Core\Entity\Redirect as Entity;
|
||||
use App\Core\Factory\RedirectFactory as Factory;
|
||||
use App\Core\Form\Filter\RedirectFilterType as FilterType;
|
||||
use App\Core\Form\RedirectType as Type;
|
||||
use App\Core\Manager\EntityManager;
|
||||
use App\Core\Repository\RedirectRepositoryQuery as RepositoryQuery;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class RedirectAdminController extends CrudController
|
||||
{
|
||||
/**
|
||||
* @Route("/admin/redirect/{page}", name="admin_redirect_index", methods={"GET"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response
|
||||
{
|
||||
return $this->doIndex($page, $query, $request, $session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/redirect/new", name="admin_redirect_new", methods={"GET", "POST"})
|
||||
*/
|
||||
public function new(Factory $factory, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doNew($factory->create(), $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/redirect/show/{entity}", name="admin_redirect_show", methods={"GET"})
|
||||
*/
|
||||
public function show(Entity $entity): Response
|
||||
{
|
||||
return $this->doShow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/redirect/filter", name="admin_redirect_filter", methods={"GET"})
|
||||
*/
|
||||
public function filter(Session $session): Response
|
||||
{
|
||||
return $this->doFilter($session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/redirect/edit/{entity}", name="admin_redirect_edit", methods={"GET", "POST"})
|
||||
*/
|
||||
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doEdit($entity, $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/redirect/sort/{page}", name="admin_redirect_sort", methods={"POST"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
public function sort(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
|
||||
{
|
||||
return $this->doSort($page, $query, $entityManager, $request, $session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/redirect/batch/{page}", name="admin_redirect_batch", methods={"POST"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
public function batch(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
|
||||
{
|
||||
return $this->doBatch($page, $query, $entityManager, $request, $session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/redirect/delete/{entity}", name="admin_redirect_delete", methods={"DELETE"})
|
||||
*/
|
||||
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doDelete($entity, $entityManager, $request);
|
||||
}
|
||||
|
||||
protected function getConfiguration(): CrudConfiguration
|
||||
{
|
||||
return CrudConfiguration::create()
|
||||
->setPageTitle('index', 'Redirects')
|
||||
->setPageTitle('edit', '{label}')
|
||||
->setPageTitle('new', 'New redirect')
|
||||
|
||||
->setPageRoute('index', 'admin_redirect_index')
|
||||
->setPageRoute('new', 'admin_redirect_new')
|
||||
->setPageRoute('edit', 'admin_redirect_edit')
|
||||
->setPageRoute('sort', 'admin_redirect_sort')
|
||||
->setPageRoute('batch', 'admin_redirect_batch')
|
||||
->setPageRoute('delete', 'admin_redirect_delete')
|
||||
->setPageRoute('filter', 'admin_redirect_filter')
|
||||
|
||||
->setForm('edit', Type::class, [])
|
||||
->setForm('new', Type::class)
|
||||
->setForm('filter', FilterType::class)
|
||||
|
||||
->setView('form', '@Core/redirect/redirect_admin/_form.html.twig')
|
||||
->setMaxPerPage('index', 100)
|
||||
->setIsSortableCollection('index', true)
|
||||
|
||||
->setAction('index', 'show', false)
|
||||
->setAction('edit', 'show', false)
|
||||
|
||||
->setField('index', 'Label', Field\TextField::class, [
|
||||
'property' => 'label',
|
||||
'attr' => ['class' => 'col-4'],
|
||||
])
|
||||
->setField('index', 'Rule', Field\TextField::class, [
|
||||
'view' => '@Core/redirect/redirect_admin/field/rule.html.twig',
|
||||
'attr' => ['class' => 'col-6'],
|
||||
])
|
||||
->setField('index', 'Enabled', Field\ButtonField::class, [
|
||||
'property_builder' => function(EntityInterface $entity) {
|
||||
return $entity->getIsEnabled() ? 'Yes' : 'No';
|
||||
},
|
||||
'attr' => ['class' => 'col-1'],
|
||||
'button_attr_builder' => function(EntityInterface $entity) {
|
||||
return ['class' => 'btn btn-sm btn-'.($entity->getIsEnabled() ? 'success' : 'primary')];
|
||||
},
|
||||
])
|
||||
->setField('index', 'Type', Field\ButtonField::class, [
|
||||
'property' => 'redirectCode',
|
||||
'attr' => ['class' => 'col-1'],
|
||||
'button_attr' => ['class' => 'btn btn-sm btn-light border-secondary font-weight-bold'],
|
||||
])
|
||||
->setBatchAction('index', 'enable', 'Enable', function (EntityInterface $entity, EntityManager $manager) {
|
||||
$entity->setIsEnabled(true);
|
||||
|
||||
$manager->update($entity);
|
||||
})
|
||||
->setBatchAction('index', 'disable', 'Disable', function (EntityInterface $entity, EntityManager $manager) {
|
||||
$entity->setIsEnabled(false);
|
||||
|
||||
$manager->update($entity);
|
||||
})
|
||||
->setBatchAction('index', 'delete', 'Delete', function (EntityInterface $entity, EntityManager $manager) {
|
||||
$manager->delete($entity);
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
protected function getSection(): string
|
||||
{
|
||||
return 'site_navigation';
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Setting;
|
||||
|
||||
use App\Core\Controller\Admin\AdminController;
|
||||
use App\Core\Entity\NavigationSetting as Entity;
|
||||
use App\Core\Event\Setting\NavigationSettingEvent;
|
||||
use App\Core\Manager\EntityManager;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* @Route("/admin/navigation_setting")
|
||||
*/
|
||||
class NavigationSettingAdminController extends AdminController
|
||||
{
|
||||
/**
|
||||
* @Route("/edit/{entity}", name="admin_navigation_setting_edit")
|
||||
*/
|
||||
public function edit(
|
||||
Entity $entity,
|
||||
EntityManager $entityManager,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
Request $request
|
||||
): Response {
|
||||
$builder = $this->createFormBuilder($entity);
|
||||
$event = new NavigationSettingEvent([
|
||||
'builder' => $builder,
|
||||
'entity' => $entity,
|
||||
'options' => [],
|
||||
]);
|
||||
|
||||
$eventDispatcher->dispatch($event, NavigationSettingEvent::FORM_INIT_EVENT);
|
||||
|
||||
$form = $builder->getForm();
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
$entityManager->update($entity);
|
||||
$this->addFlash('success', 'The data has been saved.');
|
||||
|
||||
return $this->redirectToRoute('admin_site_navigation_show', [
|
||||
'entity' => $entity->getNavigation()->getId(),
|
||||
]);
|
||||
}
|
||||
|
||||
$this->addFlash('warning', 'The form is not valid.');
|
||||
}
|
||||
|
||||
return $this->render('@Core/setting/navigation_setting_admin/edit.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'entity' => $entity,
|
||||
'options' => $event->getData()['options'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/delete/{entity}", name="admin_navigation_setting_delete", methods={"DELETE"})
|
||||
*/
|
||||
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
||||
$entityManager->delete($entity);
|
||||
|
||||
$this->addFlash('success', 'The data has been removed.');
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('admin_site_navigation_show', [
|
||||
'entity' => $entity->getNavigation()->getId(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getSection(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Setting;
|
||||
|
||||
use App\Core\Controller\Admin\AdminController;
|
||||
use App\Core\Entity\Setting as Entity;
|
||||
use App\Core\Event\Setting\SettingEvent;
|
||||
use App\Core\Manager\EntityManager;
|
||||
use App\Core\Repository\SettingRepositoryQuery as RepositoryQuery;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* @Route("/admin/setting")
|
||||
*/
|
||||
class SettingAdminController extends AdminController
|
||||
{
|
||||
/**
|
||||
* @Route("/{page}", name="admin_setting_index", requirements={"page": "\d+"})
|
||||
*/
|
||||
public function index(
|
||||
RepositoryQuery $query,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
Request $request,
|
||||
int $page = 1
|
||||
): Response {
|
||||
$eventDispatcher->dispatch(new SettingEvent(), SettingEvent::INIT_EVENT);
|
||||
|
||||
$pager = $query
|
||||
->orderBy('.section, .label')
|
||||
->paginate($page)
|
||||
;
|
||||
|
||||
return $this->render('@Core/setting/setting_admin/index.html.twig', [
|
||||
'pager' => $pager,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/edit/{entity}", name="admin_setting_edit")
|
||||
*/
|
||||
public function edit(
|
||||
Entity $entity,
|
||||
EntityManager $entityManager,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
Request $request
|
||||
): Response {
|
||||
$builder = $this->createFormBuilder($entity);
|
||||
$event = new SettingEvent([
|
||||
'builder' => $builder,
|
||||
'entity' => $entity,
|
||||
'options' => [],
|
||||
]);
|
||||
|
||||
$eventDispatcher->dispatch($event, SettingEvent::FORM_INIT_EVENT);
|
||||
|
||||
$form = $builder->getForm();
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
$entityManager->update($entity);
|
||||
$this->addFlash('success', 'The data has been saved.');
|
||||
|
||||
return $this->redirectToRoute('admin_setting_index');
|
||||
}
|
||||
|
||||
$this->addFlash('warning', 'The form is not valid.');
|
||||
}
|
||||
|
||||
return $this->render('@Core/setting/setting_admin/edit.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'entity' => $entity,
|
||||
'options' => $event->getData()['options'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/delete/{entity}", name="admin_setting_delete", methods={"DELETE"})
|
||||
*/
|
||||
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
||||
$entityManager->delete($entity);
|
||||
|
||||
$this->addFlash('success', 'The data has been removed.');
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('admin_setting_index');
|
||||
}
|
||||
|
||||
public function getSection(): string
|
||||
{
|
||||
return 'setting';
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Site;
|
||||
|
||||
use App\Core\Controller\Admin\AdminController;
|
||||
use App\Core\Entity\Site\Menu as Entity;
|
||||
use App\Core\Entity\Site\Navigation;
|
||||
use App\Core\Factory\Site\MenuFactory as EntityFactory;
|
||||
use App\Core\Form\Site\MenuType as EntityType;
|
||||
use App\Core\Manager\EntityManager;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/menu")
|
||||
*/
|
||||
class MenuAdminController extends AdminController
|
||||
{
|
||||
/**
|
||||
* @Route("/new/{navigation}", name="admin_site_menu_new", methods={"POST"})
|
||||
*/
|
||||
public function new(Navigation $navigation, EntityFactory $factory, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
$entity = $factory->create($navigation);
|
||||
$form = $this->createForm(EntityType::class, $entity);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
$entityManager->create($entity);
|
||||
|
||||
$this->addFlash('success', 'The data has been saved.');
|
||||
} else {
|
||||
$this->addFlash('warning', 'The form is not valid.');
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('admin_site_tree_navigation', [
|
||||
'navigation' => $navigation->getId(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/edit/{entity}", name="admin_site_menu_edit", methods={"POST"})
|
||||
*/
|
||||
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
$form = $this->createForm(EntityType::class, $entity);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
$entityManager->update($entity);
|
||||
$this->addFlash('success', 'The data has been saved.');
|
||||
} else {
|
||||
$this->addFlash('warning', 'The form is not valid.');
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('admin_site_tree_navigation', [
|
||||
'navigation' => $entity->getNavigation()->getId(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/delete/{entity}", name="admin_site_menu_delete", methods={"DELETE"})
|
||||
*/
|
||||
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
||||
$entityManager->delete($entity);
|
||||
|
||||
$this->addFlash('success', 'The data has been removed.');
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('admin_site_tree_navigation', [
|
||||
'navigation' => $entity->getNavigation()->getId(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getSection(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Site;
|
||||
|
||||
use App\Core\Controller\Admin\Crud\CrudController;
|
||||
use App\Core\Crud\CrudConfiguration;
|
||||
use App\Core\Crud\Field;
|
||||
use App\Core\Entity\Site\Navigation as Entity;
|
||||
use App\Core\Event\Setting\NavigationSettingEvent;
|
||||
use App\Core\Factory\Site\NavigationFactory as Factory;
|
||||
use App\Core\Form\Site\NavigationType as Type;
|
||||
use App\Core\Manager\EntityManager;
|
||||
use App\Core\Repository\NavigationSettingRepositoryQuery;
|
||||
use App\Core\Repository\Site\NavigationRepositoryQuery as RepositoryQuery;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class NavigationAdminController extends CrudController
|
||||
{
|
||||
/**
|
||||
* @Route("/admin/site/navigation/{page}", name="admin_site_navigation_index", methods={"GET"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response
|
||||
{
|
||||
return $this->doIndex($page, $query, $request, $session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/navigation/new", name="admin_site_navigation_new", methods={"GET", "POST"})
|
||||
*/
|
||||
public function new(Factory $factory, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doNew($factory->create(), $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/navigation/show/{entity}", name="admin_site_navigation_show", methods={"GET"})
|
||||
*/
|
||||
public function show(
|
||||
Entity $entity,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
NavigationSettingRepositoryQuery $settingQuery
|
||||
): Response {
|
||||
$eventDispatcher->dispatch(new NavigationSettingEvent([
|
||||
'navigation' => $entity,
|
||||
]), NavigationSettingEvent::INIT_EVENT);
|
||||
|
||||
$settings = $settingQuery
|
||||
->where('.navigation = :navigation')
|
||||
->orderBy('.section, .label')
|
||||
->setParameter(':navigation', $entity->getId())
|
||||
->paginate(1, 1000)
|
||||
;
|
||||
|
||||
$this->getConfiguration()->addViewData('show', 'settings', $settings);
|
||||
|
||||
return $this->doShow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/navigation/filter", name="admin_site_navigation_filter", methods={"GET"})
|
||||
*/
|
||||
public function filter(Session $session): Response
|
||||
{
|
||||
return $this->doFilter($session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/navigation/edit/{entity}", name="admin_site_navigation_edit", methods={"GET", "POST"})
|
||||
*/
|
||||
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doEdit($entity, $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/navigation/sort/{page}", name="admin_site_navigation_sort", methods={"POST"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
public function sort(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1, ): Response
|
||||
{
|
||||
return $this->doSort($page, $query, $entityManager, $request, $session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/navigation/delete/{entity}", name="admin_site_navigation_delete", methods={"DELETE"})
|
||||
*/
|
||||
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doDelete($entity, $entityManager, $request);
|
||||
}
|
||||
|
||||
protected function getConfiguration(): CrudConfiguration
|
||||
{
|
||||
return CrudConfiguration::create()
|
||||
->setPageTitle('index', 'Navigations')
|
||||
->setPageTitle('edit', '{label}')
|
||||
->setPageTitle('new', 'New navigation')
|
||||
->setPageTitle('show', '{label}')
|
||||
|
||||
->setPageRoute('index', 'admin_site_navigation_index')
|
||||
->setPageRoute('new', 'admin_site_navigation_new')
|
||||
->setPageRoute('edit', 'admin_site_navigation_edit')
|
||||
->setPageRoute('show', 'admin_site_navigation_show')
|
||||
->setPageRoute('sort', 'admin_site_navigation_sort')
|
||||
->setPageRoute('delete', 'admin_site_navigation_delete')
|
||||
->setPageRoute('filter', 'admin_site_navigation_filter')
|
||||
->setPageRoute('redirects', 'admin_redirect_index')
|
||||
|
||||
->setForm('edit', Type::class, [])
|
||||
->setForm('new', Type::class)
|
||||
|
||||
->setView('index', '@Core/site/navigation_admin/index.html.twig')
|
||||
->setView('show', '@Core/site/navigation_admin/show.html.twig')
|
||||
->setView('show_entity', '@Core/site/navigation_admin/_show.html.twig')
|
||||
->setView('form', '@Core/site/navigation_admin/_form.html.twig')
|
||||
|
||||
->setIsSortableCollection('index', true)
|
||||
|
||||
->setField('index', 'Label', Field\TextField::class, [
|
||||
'property' => 'label',
|
||||
'attr' => ['class' => 'miw-200'],
|
||||
])
|
||||
->setField('index', 'Domain', Field\ButtonField::class, [
|
||||
'property' => 'domain',
|
||||
'button_attr' => ['class' => 'btn btn-light'],
|
||||
'attr' => ['class' => 'miw-200'],
|
||||
])
|
||||
->setField('index', 'Locale', Field\ButtonField::class, [
|
||||
'property' => 'locale',
|
||||
'button_attr' => ['class' => 'btn btn-light'],
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
protected function getSection(): string
|
||||
{
|
||||
return 'site_navigation';
|
||||
}
|
||||
}
|
|
@ -1,305 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Site;
|
||||
|
||||
use App\Core\Controller\Admin\AdminController;
|
||||
use App\Core\Entity\Site\Node;
|
||||
use App\Core\Entity\Site\Node as Entity;
|
||||
use App\Core\Entity\Site\Page\Page;
|
||||
use App\Core\Event\EntityManager\EntityManagerEvent;
|
||||
use App\Core\Factory\Site\NodeFactory as EntityFactory;
|
||||
use App\Core\Factory\Site\Page\PageFactory;
|
||||
use App\Core\Form\Site\NodeMoveType;
|
||||
use App\Core\Form\Site\NodeType as EntityType;
|
||||
use App\Core\Manager\EntityManager;
|
||||
use App\Core\Repository\Site\NodeRepository;
|
||||
use App\Core\Site\ControllerLocator;
|
||||
use App\Core\Site\PageLocator;
|
||||
use App\Core\Sitemap\SitemapBuilder;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/node")
|
||||
*/
|
||||
class NodeAdminController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @Route("/new/{node}", name="admin_site_node_new")
|
||||
*/
|
||||
public function new(
|
||||
Node $node,
|
||||
EntityFactory $factory,
|
||||
PageFactory $pageFactory,
|
||||
EntityManager $entityManager,
|
||||
NodeRepository $nodeRepository,
|
||||
PageLocator $pageLocator,
|
||||
ControllerLocator $controllerLocator,
|
||||
Request $request
|
||||
): Response {
|
||||
$entity = $factory->create($node->getMenu());
|
||||
$form = $this->createForm(EntityType::class, $entity, [
|
||||
'pages' => $pageLocator->getPages(),
|
||||
'controllers' => $controllerLocator->getControllers(),
|
||||
'navigation' => $node->getMenu()->getNavigation(),
|
||||
]);
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
$position = $form->get('position')->getData();
|
||||
|
||||
$parent = 'above' === $position ? $node : $node->getParent();
|
||||
$entity->setParent($parent);
|
||||
|
||||
if ('above' === $position) {
|
||||
$nodeRepository->persistAsLastChild($entity, $node);
|
||||
} else {
|
||||
if ('after' === $position) {
|
||||
$nodeRepository->persistAsNextSiblingOf($entity, $node);
|
||||
} elseif ('before' === $position) {
|
||||
$nodeRepository->persistAsPrevSiblingOf($entity, $node);
|
||||
}
|
||||
}
|
||||
|
||||
$this->handlePageAssociation(
|
||||
$form->get('pageAction')->getData(),
|
||||
$form->get('pageEntity')->getData(),
|
||||
$form->get('pageType')->getData(),
|
||||
$entity,
|
||||
$pageFactory,
|
||||
$pageLocator
|
||||
);
|
||||
|
||||
$entityManager->create($entity);
|
||||
|
||||
$this->addFlash('success', 'The data has been saved.');
|
||||
|
||||
return $this->redirect($this->generateUrl('admin_site_tree_navigation', [
|
||||
'navigation' => $node->getMenu()->getNavigation()->getId(),
|
||||
'data-modal' => $this->generateUrl('admin_site_node_edit', ['entity' => $entity->getId()]),
|
||||
]).sprintf('#node-%d', $entity->getId()));
|
||||
}
|
||||
$this->addFlash('warning', 'The form is not valid.');
|
||||
|
||||
return $this->redirect($this->generateUrl('admin_site_tree_navigation', [
|
||||
'navigation' => $entity->getMenu()->getNavigation()->getId(),
|
||||
]).sprintf('#node-%d', $entity->getId()));
|
||||
}
|
||||
|
||||
return $this->render('@Core/site/node_admin/new.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'node' => $node,
|
||||
'entity' => $entity,
|
||||
'tab' => 'content',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/edit/{entity}/{tab}", name="admin_site_node_edit")
|
||||
*/
|
||||
public function edit(
|
||||
Entity $entity,
|
||||
EntityManager $entityManager,
|
||||
PageFactory $pageFactory,
|
||||
PageLocator $pageLocator,
|
||||
ControllerLocator $controllerLocator,
|
||||
Request $request,
|
||||
string $tab = 'content'
|
||||
): Response {
|
||||
$form = $this->createForm(EntityType::class, $entity, [
|
||||
'pages' => $pageLocator->getPages(),
|
||||
'controllers' => $controllerLocator->getControllers(),
|
||||
'navigation' => $entity->getMenu()->getNavigation(),
|
||||
]);
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
$this->handlePageAssociation(
|
||||
$form->get('pageAction')->getData(),
|
||||
$form->get('pageEntity')->getData(),
|
||||
$form->get('pageType')->getData(),
|
||||
$entity,
|
||||
$pageFactory,
|
||||
$pageLocator
|
||||
);
|
||||
|
||||
$entityManager->update($entity);
|
||||
|
||||
$this->addFlash('success', 'The data has been saved.');
|
||||
} else {
|
||||
$this->addFlash('warning', 'The form is not valid.');
|
||||
}
|
||||
|
||||
return $this->redirect($this->generateUrl('admin_site_tree_navigation', [
|
||||
'navigation' => $entity->getMenu()->getNavigation()->getId(),
|
||||
'data-modal' => $this->generateUrl('admin_site_node_edit', ['entity' => $entity->getId()]),
|
||||
]).sprintf('#node-%d', $entity->getId()));
|
||||
}
|
||||
|
||||
$page = $entity->getPage();
|
||||
|
||||
if ($page !== null) {
|
||||
$pageConfiguration = $pageLocator->getPages()[get_class($page)] ?? null;
|
||||
} else {
|
||||
$pageConfiguration = null;
|
||||
}
|
||||
|
||||
return $this->render('@Core/site/node_admin/edit.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'entity' => $entity,
|
||||
'page' => $page,
|
||||
'pageConfiguration' => $pageConfiguration,
|
||||
'tab' => $tab,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/urls/{entity}", name="admin_site_node_urls")
|
||||
*/
|
||||
public function urls(Entity $entity, SitemapBuilder $builder): Response
|
||||
{
|
||||
return $this->render('@Core/site/node_admin/urls.html.twig', [
|
||||
'entity' => $entity,
|
||||
'urls' => $builder->getNodeUrls($entity),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/move/{entity}", name="admin_site_node_move")
|
||||
*/
|
||||
public function move(
|
||||
Entity $entity,
|
||||
EntityManager $entityManager,
|
||||
NodeRepository $nodeRepository,
|
||||
Request $request
|
||||
): Response {
|
||||
$form = $this->createForm(NodeMoveType::class, null, [
|
||||
'menu' => $entity->getMenu(),
|
||||
]);
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->get('node')->getData()->getId() === $entity->getId()) {
|
||||
$form->get('node')->addError(new FormError('Élement de référence invalide.'));
|
||||
}
|
||||
|
||||
if ($form->isValid()) {
|
||||
$position = $form->get('position')->getData();
|
||||
$node = $form->get('node')->getData();
|
||||
|
||||
$parent = 'above' === $position ? $node : $node->getParent();
|
||||
$entity->setParent($parent);
|
||||
|
||||
if ('above' === $position) {
|
||||
$nodeRepository->persistAsLastChild($entity, $node);
|
||||
$entityManager->flush();
|
||||
} else {
|
||||
if ('after' === $position) {
|
||||
$nodeRepository->persistAsNextSiblingOf($entity, $node);
|
||||
} elseif ('before' === $position) {
|
||||
$nodeRepository->persistAsPrevSiblingOf($entity, $node);
|
||||
}
|
||||
|
||||
$entityManager->flush();
|
||||
}
|
||||
|
||||
$this->addFlash('success', 'The data has been saved.');
|
||||
} else {
|
||||
$this->addFlash('warning', 'The form is not valid.');
|
||||
}
|
||||
|
||||
return $this->redirect($this->generateUrl('admin_site_tree_navigation', [
|
||||
'navigation' => $entity->getMenu()->getNavigation()->getId(),
|
||||
]).sprintf('#node-%d', $entity->getId()));
|
||||
}
|
||||
|
||||
return $this->render('@Core/site/node_admin/move.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'entity' => $entity,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/toggle/visibility/{entity}", name="admin_site_node_toggle_visibility", methods={"POST"})
|
||||
*/
|
||||
public function toggleVisibility(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
if ($this->isCsrfTokenValid('toggle_visibility'.$entity->getId(), $request->request->get('_token'))) {
|
||||
$entity->setIsVisible(!$entity->getIsVisible());
|
||||
|
||||
$entityManager->update($entity);
|
||||
|
||||
$this->addFlash('success', 'The data has been saved.');
|
||||
}
|
||||
|
||||
return $this->redirect($this->generateUrl('admin_site_tree_navigation', [
|
||||
'navigation' => $entity->getMenu()->getNavigation()->getId(),
|
||||
]).sprintf('#node-%d', $entity->getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/delete/{entity}", name="admin_site_node_delete", methods={"DELETE"})
|
||||
*/
|
||||
public function delete(
|
||||
Entity $entity,
|
||||
NodeRepository $nodeRepository,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
Request $request
|
||||
): Response {
|
||||
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
||||
$eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::PRE_DELETE_EVENT);
|
||||
$nodeRepository->removeFromTree($entity);
|
||||
$nodeRepository->reorder($entity->getMenu()->getRootNode());
|
||||
$eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::DELETE_EVENT);
|
||||
|
||||
$this->addFlash('success', 'Donnée supprimée.');
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('admin_site_tree_navigation', [
|
||||
'navigation' => $entity->getMenu()->getNavigation()->getId(),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function handlePageAssociation(
|
||||
string $pageAction,
|
||||
?Page $pageEntity,
|
||||
string $pageType,
|
||||
Entity $entity,
|
||||
PageFactory $pageFactory,
|
||||
PageLocator $pageLocator
|
||||
) {
|
||||
if ('new' === $pageAction) {
|
||||
$pageConfiguration = $pageLocator->getPage($pageType);
|
||||
$page = $pageFactory->create($pageType, $entity->getLabel());
|
||||
$page->setTemplate($pageConfiguration->getTemplates()[0]['file']);
|
||||
|
||||
$entity
|
||||
->setPage($page)
|
||||
->setAliasNode(null)
|
||||
;
|
||||
} elseif ('existing' === $pageAction) {
|
||||
if ($pageEntity) {
|
||||
$entity->setPage($pageEntity);
|
||||
} else {
|
||||
$this->addFlash('info', 'Aucun changement de page effectué.');
|
||||
}
|
||||
$entity->setAliasNode(null);
|
||||
} elseif ('alias' === $pageAction) {
|
||||
$entity->setPage(null);
|
||||
} elseif ('none' === $pageAction) {
|
||||
$entity
|
||||
->setPage(null)
|
||||
->setAliasNode(null)
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Site;
|
||||
|
||||
use App\Core\Controller\Admin\Crud\CrudController;
|
||||
use App\Core\Crud\CrudConfiguration;
|
||||
use App\Core\Crud\Field;
|
||||
use App\Core\Entity\Site\Page\Page as Entity;
|
||||
use App\Core\Form\Site\Page\Filter\PageFilterType as FilterType;
|
||||
use App\Core\Form\Site\Page\PageType as Type;
|
||||
use App\Core\Manager\EntityManager;
|
||||
use App\Core\Repository\Site\Page\PageRepositoryQuery as RepositoryQuery;
|
||||
use App\Core\Site\PageLocator;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use App\Core\Event\Page\PageEditEvent;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use App\Core\Entity\EntityInterface;
|
||||
|
||||
class PageAdminController extends CrudController
|
||||
{
|
||||
/**
|
||||
* @Route("/admin/site/page/{page}", name="admin_site_page_index", methods={"GET"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response
|
||||
{
|
||||
return $this->doIndex($page, $query, $request, $session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/page/show/{entity}", name="admin_site_page_show", methods={"GET"})
|
||||
*/
|
||||
public function show(Entity $entity): Response
|
||||
{
|
||||
return $this->doShow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/page/filter", name="admin_site_page_filter", methods={"GET"})
|
||||
*/
|
||||
public function filter(Session $session): Response
|
||||
{
|
||||
return $this->doFilter($session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/page/edit/{entity}", name="admin_site_page_edit", methods={"GET", "POST"})
|
||||
*/
|
||||
public function edit(
|
||||
int $entity,
|
||||
EntityManager $entityManager,
|
||||
RepositoryQuery $repositoryQuery,
|
||||
PageLocator $pageLocator,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
Request $request
|
||||
): Response {
|
||||
$entity = $repositoryQuery->filterById($entity)->findOne();
|
||||
|
||||
$event = new PageEditEvent($entity);
|
||||
$eventDispatcher->dispatch($event, PageEditEvent::FORM_INIT_EVENT);
|
||||
|
||||
$this->getConfiguration()->setFormOptions('edit', [
|
||||
'page_configuration' => $pageLocator->getPage(get_class($entity)),
|
||||
'page_builder_options' => $event->getPageBuilderOptions(),
|
||||
]);
|
||||
|
||||
return $this->doEdit($entity, $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/page/delete/{entity}", name="admin_site_page_delete", methods={"DELETE"})
|
||||
*/
|
||||
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doDelete($entity, $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/page/batch/{page}", name="admin_site_page_batch", methods={"POST"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
public function batch(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
|
||||
{
|
||||
return $this->doBatch($page, $query, $entityManager, $request, $session);
|
||||
}
|
||||
|
||||
protected function getConfiguration(): CrudConfiguration
|
||||
{
|
||||
return CrudConfiguration::create()
|
||||
->setPageTitle('index', 'Pages')
|
||||
->setPageTitle('edit', '{name}')
|
||||
->setPageTitle('show', '{name}')
|
||||
|
||||
->setPageRoute('index', 'admin_site_page_index')
|
||||
->setPageRoute('edit', 'admin_site_page_edit')
|
||||
->setPageRoute('delete', 'admin_site_page_delete')
|
||||
->setPageRoute('filter', 'admin_site_page_filter')
|
||||
->setPageRoute('batch', 'admin_site_page_batch')
|
||||
|
||||
->setForm('edit', Type::class, [])
|
||||
->setForm('filter', FilterType::class)
|
||||
->setView('form', '@Core/site/page_admin/_form.html.twig')
|
||||
|
||||
->setAction('index', 'new', false)
|
||||
->setAction('index', 'show', false)
|
||||
->setAction('edit', 'show', false)
|
||||
|
||||
->setField('index', 'Name', Field\TextField::class, [
|
||||
'property' => 'name',
|
||||
'sort' => ['name', '.name'],
|
||||
'attr' => ['class' => 'col-4'],
|
||||
])
|
||||
->setField('index', 'Elements', Field\TextField::class, [
|
||||
'view' => '@Core/site/page_admin/fields/nodes.html.twig',
|
||||
'sort' => ['navigation', function (RepositoryQuery $query, $direction) {
|
||||
$query
|
||||
->leftJoin('.nodes', 'node')
|
||||
->leftJoin('node.menu', 'menu')
|
||||
->leftJoin('menu.navigation', 'navigation')
|
||||
->orderBy('navigation.label', $direction)
|
||||
;
|
||||
}],
|
||||
'attr' => ['class' => 'col-6'],
|
||||
])
|
||||
->setBatchAction('index', 'delete', 'Delete', function(EntityInterface $entity, EntityManager $manager) {
|
||||
$manager->delete($entity);
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
protected function getSection(): string
|
||||
{
|
||||
return 'site_page';
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Site;
|
||||
|
||||
use App\Core\Site\SiteRequest;
|
||||
use App\Core\Site\SiteStore;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class PageController extends AbstractController
|
||||
{
|
||||
protected SiteRequest $siteRequest;
|
||||
protected SiteStore $siteStore;
|
||||
|
||||
public function __construct(SiteRequest $siteRequest, SiteStore $siteStore)
|
||||
{
|
||||
$this->siteRequest = $siteRequest;
|
||||
$this->siteStore = $siteStore;
|
||||
}
|
||||
|
||||
public function show(): Response
|
||||
{
|
||||
if (!$this->siteRequest->getPage()) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
return $this->defaultRender($this->siteRequest->getPage()->getTemplate());
|
||||
}
|
||||
|
||||
protected function defaultRender(string $view, array $parameters = [], Response $response = null): Response
|
||||
{
|
||||
$parameters = array_merge($this->getDefaultRenderParameters(), $parameters);
|
||||
|
||||
if (null === $response) {
|
||||
$contentType = $this->siteRequest->getNode()->getContentType();
|
||||
|
||||
$response = new Response(null, 200, [
|
||||
'Content-Type' => $contentType ?? 'text/html',
|
||||
]);
|
||||
}
|
||||
|
||||
return parent::render($view, $parameters, $response);
|
||||
}
|
||||
|
||||
protected function getDefaultRenderParameters(): array
|
||||
{
|
||||
return [
|
||||
'_node' => $this->siteRequest->getNode(),
|
||||
'_page' => $this->siteRequest->getPage(),
|
||||
'_menu' => $this->siteRequest->getMenu(),
|
||||
'_navigation' => $this->siteRequest->getNavigation(),
|
||||
'_domain' => $this->siteRequest->getDomain(),
|
||||
'_locale' => $this->siteRequest->getNavigation()->getLocale(),
|
||||
'_store' => $this->siteStore,
|
||||
];
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Site;
|
||||
|
||||
use App\Core\Repository\Site\NavigationRepositoryQuery;
|
||||
use App\Core\Sitemap\SitemapBuilder;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class SitemapController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @Route("/sitemap.xml", name="sitemap")
|
||||
*/
|
||||
public function sitemap(Request $request, NavigationRepositoryQuery $navigationRepositoryQuery, SitemapBuilder $builder): Response
|
||||
{
|
||||
$navigations = $navigationRepositoryQuery
|
||||
->whereDomain($request->getHost())
|
||||
->find()
|
||||
;
|
||||
|
||||
$items = [];
|
||||
|
||||
foreach ($navigations as $navigation) {
|
||||
$items = array_merge(
|
||||
$items,
|
||||
$builder->build($navigation)
|
||||
);
|
||||
}
|
||||
|
||||
$response = new Response();
|
||||
$response->headers->set('Content-Type', 'text/xml');
|
||||
|
||||
return $this->render('@Core/site/sitemap/sitemap.xml.twig', [
|
||||
'items' => $items,
|
||||
], $response);
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Site;
|
||||
|
||||
use App\Core\Controller\Admin\AdminController;
|
||||
use App\Core\Entity\Site\Navigation;
|
||||
use App\Core\Factory\Site\MenuFactory;
|
||||
use App\Core\Form\Site\MenuType;
|
||||
use App\Core\Repository\Site\NavigationRepositoryQuery;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/tree")
|
||||
*/
|
||||
class TreeAdminController extends AdminController
|
||||
{
|
||||
/**
|
||||
* @Route("/", name="admin_site_tree_index")
|
||||
*/
|
||||
public function index(NavigationRepositoryQuery $navigationQuery, Session $session): Response
|
||||
{
|
||||
$navigation = null;
|
||||
|
||||
if ($session->has('site_tree_last_navigation')) {
|
||||
$navigation = $navigationQuery->create()
|
||||
->filterById((int) $session->get('site_tree_last_navigation'))
|
||||
->findOne()
|
||||
;
|
||||
}
|
||||
|
||||
if (null === $navigation) {
|
||||
$navigation = $navigationQuery->create()
|
||||
->orderBy('.sortOrder')
|
||||
->findOne()
|
||||
;
|
||||
}
|
||||
|
||||
if (null === $navigation) {
|
||||
$this->addFlash('warning', 'You must add a navigation.');
|
||||
|
||||
return $this->redirectToRoute('admin_site_navigation_new');
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('admin_site_tree_navigation', [
|
||||
'navigation' => $navigation->getId(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/navigation/{navigation}", name="admin_site_tree_navigation")
|
||||
*/
|
||||
public function navigation(
|
||||
Navigation $navigation,
|
||||
NavigationRepositoryQuery $navigationQuery,
|
||||
MenuFactory $menuFactory,
|
||||
Session $session
|
||||
): Response {
|
||||
$navigations = $navigationQuery->create()
|
||||
->orderBy('.sortOrder')
|
||||
->find()
|
||||
;
|
||||
|
||||
$session->set('site_tree_last_navigation', $navigation->getId());
|
||||
|
||||
$forms = [
|
||||
'menu' => $this->createForm(MenuType::class, $menuFactory->create())->createView(),
|
||||
'menus' => [],
|
||||
];
|
||||
|
||||
foreach ($navigation->getMenus() as $menu) {
|
||||
$forms['menus'][$menu->getId()] = $this->createForm(MenuType::class, $menu)->createView();
|
||||
}
|
||||
|
||||
return $this->render('@Core/site/tree_admin/navigation.html.twig', [
|
||||
'navigation' => $navigation,
|
||||
'navigations' => $navigations,
|
||||
'forms' => $forms,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getSection(): string
|
||||
{
|
||||
return 'site_tree';
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Task;
|
||||
|
||||
use App\Core\Controller\Admin\AdminController;
|
||||
use App\Core\Event\Task\TaskInitEvent;
|
||||
use App\Core\Event\Task\TaskRunRequestedEvent;
|
||||
use SensioLabs\AnsiConverter\AnsiToHtmlConverter;
|
||||
use SensioLabs\AnsiConverter\Theme\SolarizedTheme;
|
||||
use Symfony\Component\Console\Output\BufferedOutput;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* @Route("/admin/task")
|
||||
*/
|
||||
class TaskAdminController extends AdminController
|
||||
{
|
||||
/**
|
||||
* @Route("/", name="admin_task_index")
|
||||
*/
|
||||
public function index(EventDispatcherInterface $eventDispatcher): Response
|
||||
{
|
||||
$event = new TaskInitEvent();
|
||||
$eventDispatcher->dispatch($event, TaskInitEvent::INIT_EVENT);
|
||||
|
||||
return $this->render('@Core/task/task_admin/index.html.twig', [
|
||||
'pager' => $event->getTasks(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/run/{task}", name="admin_task_run", methods={"GET"})
|
||||
*/
|
||||
public function run(
|
||||
string $task,
|
||||
Request $request,
|
||||
EventDispatcherInterface $eventDispatcher
|
||||
): Response {
|
||||
if (!$this->isCsrfTokenValid('task_run', $request->query->get('_token'))) {
|
||||
throw $this->createAccessDeniedException();
|
||||
}
|
||||
|
||||
$output = new BufferedOutput();
|
||||
$event = new TaskRunRequestedEvent($task, $request->query, $output);
|
||||
$eventDispatcher->dispatch($event, TaskRunRequestedEvent::RUN_REQUEST_EVENT);
|
||||
|
||||
$converter = new AnsiToHtmlConverter(new SolarizedTheme());
|
||||
$content = $converter->convert($output->fetch());
|
||||
|
||||
return $this->render('@Core/task/task_admin/run.html.twig', [
|
||||
'output' => $content,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getSection(): string
|
||||
{
|
||||
return 'task';
|
||||
}
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\User;
|
||||
|
||||
use App\Core\Controller\Admin\Crud\CrudController;
|
||||
use App\Core\Crud\CrudConfiguration;
|
||||
use App\Core\Crud\Field;
|
||||
use App\Core\Event\Account\PasswordRequestEvent;
|
||||
use App\Core\Factory\UserFactory as Factory;
|
||||
use App\Core\Manager\EntityManager;
|
||||
use App\Entity\User as Entity;
|
||||
use App\Form\UserType as Type;
|
||||
use App\Repository\UserRepositoryQuery as RepositoryQuery;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class UserAdminController extends CrudController
|
||||
{
|
||||
/**
|
||||
* @Route("/admin/user/{page}", name="admin_user_index", methods={"GET"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response
|
||||
{
|
||||
return $this->doIndex($page, $query, $request, $session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/user/new", name="admin_user_new", methods={"GET", "POST"})
|
||||
*/
|
||||
public function new(Factory $factory, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
$entity = $factory->create($this->getUser());
|
||||
|
||||
return $this->doNew($factory->create(), $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/user/show/{entity}", name="admin_user_show", methods={"GET"})
|
||||
*/
|
||||
public function show(Entity $entity): Response
|
||||
{
|
||||
return $this->doShow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/user/filter", name="admin_user_filter", methods={"GET"})
|
||||
*/
|
||||
public function filter(Session $session): Response
|
||||
{
|
||||
return $this->doFilter($session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/user/edit/{entity}", name="admin_user_edit", methods={"GET", "POST"})
|
||||
*/
|
||||
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doEdit($entity, $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/user/delete/{entity}", name="admin_user_delete", methods={"DELETE"})
|
||||
*/
|
||||
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doDelete($entity, $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/user/resetting_request/{entity}", name="admin_user_resetting_request", methods={"POST"})
|
||||
*/
|
||||
public function requestResetting(Entity $entity, EventDispatcherInterface $eventDispatcher, Request $request): Response
|
||||
{
|
||||
if ($this->isCsrfTokenValid('resetting_request'.$entity->getId(), $request->request->get('_token'))) {
|
||||
$eventDispatcher->dispatch(new PasswordRequestEvent($entity), PasswordRequestEvent::EVENT);
|
||||
|
||||
$this->addFlash('success', 'E-mail sent.');
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('admin_user_edit', [
|
||||
'entity' => $entity->getId(),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getConfiguration(): CrudConfiguration
|
||||
{
|
||||
return CrudConfiguration::create()
|
||||
->setPageTitle('index', 'Users')
|
||||
->setPageTitle('edit', '{username}')
|
||||
->setPageTitle('new', 'New user')
|
||||
->setPageTitle('show', '{username}')
|
||||
|
||||
->setPageRoute('index', 'admin_user_index')
|
||||
->setPageRoute('new', 'admin_user_new')
|
||||
->setPageRoute('edit', 'admin_user_edit')
|
||||
->setPageRoute('show', 'admin_user_show')
|
||||
->setPageRoute('delete', 'admin_user_delete')
|
||||
->setPageRoute('filter', 'admin_user_filter')
|
||||
|
||||
->setForm('edit', Type::class, [])
|
||||
->setForm('new', Type::class)
|
||||
|
||||
->setView('form', '@Core/user/user_admin/_form.html.twig')
|
||||
->setView('index', '@Core/user/user_admin/index.html.twig')
|
||||
->setView('new', '@Core/user/user_admin/new.html.twig')
|
||||
->setView('show', '@Core/user/user_admin/show.html.twig')
|
||||
->setView('show_entity', '@Core/user/user_admin/_show.html.twig')
|
||||
->setView('edit', '@Core/user/user_admin/edit.html.twig')
|
||||
|
||||
->setDefaultSort('index', 'username')
|
||||
|
||||
->setField('index', 'E-mail', Field\TextField::class, [
|
||||
'property' => 'email',
|
||||
'sort' => ['email', '.email'],
|
||||
'attr' => ['class' => 'miw-200'],
|
||||
])
|
||||
->setField('index', 'Display name', Field\TextField::class, [
|
||||
'property' => 'displayName',
|
||||
'sort' => ['displayName', '.displayName'],
|
||||
'attr' => ['class' => 'miw-200'],
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
protected function getSection(): string
|
||||
{
|
||||
return 'user';
|
||||
}
|
||||
}
|
|
@ -1,330 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Crud;
|
||||
|
||||
use App\Core\Crud\Exception\CrudConfigurationException;
|
||||
|
||||
/**
|
||||
* class CrudConfiguration.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class CrudConfiguration
|
||||
{
|
||||
protected array $pageTitles = [];
|
||||
protected array $pageRoutes = [];
|
||||
protected array $pageRouteParams = [];
|
||||
protected array $actions = [];
|
||||
protected array $batchActions = [];
|
||||
protected array $actionTitles = [];
|
||||
protected array $forms = [];
|
||||
protected array $formOptions = [];
|
||||
protected array $views = [];
|
||||
protected array $viewDatas = [];
|
||||
protected array $fields = [];
|
||||
protected array $maxPerPage = [];
|
||||
protected array $locales = [];
|
||||
protected array $defaultSort = [];
|
||||
protected array $isSortableCollection = [];
|
||||
protected string $sortableCollectionProperty = 'sortOrder';
|
||||
protected ?string $defaultLocale = null;
|
||||
protected bool $showActions = true;
|
||||
|
||||
protected static $self;
|
||||
|
||||
public static function create()
|
||||
{
|
||||
if (null === self::$self) {
|
||||
self::$self = new self();
|
||||
}
|
||||
|
||||
return self::$self;
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setPageTitle(string $page, string $title): self
|
||||
{
|
||||
$this->pageTitles[$page] = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPageTitle(string $page, ?string $default = null): ?string
|
||||
{
|
||||
return $this->pageTitles[$page] ?? $default;
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setPageRoute(string $page, string $route): self
|
||||
{
|
||||
$this->pageRoutes[$page] = $route;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPageRoute(string $page): ?string
|
||||
{
|
||||
return $this->pageRoutes[$page];
|
||||
}
|
||||
|
||||
public function setPageRouteParams(string $page, array $params): self
|
||||
{
|
||||
$this->pageRouteParams[$page] = $params;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPageRouteParams(string $page): array
|
||||
{
|
||||
return $this->pageRouteParams[$page] ?? [];
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setForm(string $context, string $form, array $options = []): self
|
||||
{
|
||||
$this->forms[$context] = $form;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getForm(string $context): ?string
|
||||
{
|
||||
return $this->forms[$context] ?? null;
|
||||
}
|
||||
|
||||
public function setFormOptions(string $context, array $options = []): self
|
||||
{
|
||||
$this->formOptions[$context] = $options;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFormOptions(string $context): array
|
||||
{
|
||||
return $this->formOptions[$context] ?? [];
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setAction(string $page, string $action, bool $enabled): self
|
||||
{
|
||||
if (!isset($this->actions[$page])) {
|
||||
$this->actions[$page] = [];
|
||||
}
|
||||
|
||||
$this->actions[$page][$action] = $enabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAction(string $page, string $action, bool $default = true)
|
||||
{
|
||||
return $this->actions[$page][$action] ?? $default;
|
||||
}
|
||||
|
||||
public function setBatchAction(string $page, string $action, string $label, callable $callback): self
|
||||
{
|
||||
if (!isset($this->batchActions[$page])) {
|
||||
$this->batchActions[$page] = [];
|
||||
}
|
||||
|
||||
$this->batchActions[$page][$action] = [
|
||||
'label' => $label,
|
||||
'callback' => $callback,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBatchActions(string $page)
|
||||
{
|
||||
return $this->batchActions[$page] ?? [];
|
||||
}
|
||||
|
||||
public function getBatchAction(string $page, string $action)
|
||||
{
|
||||
return $this->batchActions[$page][$action] ?? null;
|
||||
}
|
||||
|
||||
public function hasBatchAction(string $page)
|
||||
{
|
||||
return !empty($this->batchActions[$page]);
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setActionTitle(string $page, string $action, string $title): self
|
||||
{
|
||||
if (!isset($this->actionTitles[$page])) {
|
||||
$this->actionTitles[$page] = [];
|
||||
}
|
||||
|
||||
$this->actions[$page][$action] = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getActionTitle(string $page, string $action, ?string $default = null): ?string
|
||||
{
|
||||
return $this->actionTitles[$page][$action] ?? $default;
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setView(string $context, string $view): self
|
||||
{
|
||||
$this->views[$context] = $view;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getView(string $context, ?string $default = null)
|
||||
{
|
||||
if (null === $default) {
|
||||
$default = sprintf('@Core/admin/crud/%s.html.twig', $context);
|
||||
}
|
||||
|
||||
return $this->views[$context] ?? $default;
|
||||
}
|
||||
|
||||
public function addViewData(string $context, string $name, $value): self
|
||||
{
|
||||
if (!isset($this->viewDatas[$context])) {
|
||||
$this->viewDatas[$context] = [];
|
||||
}
|
||||
|
||||
$this->viewDatas[$context][$name] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setViewDatas(string $context, array $datas): self
|
||||
{
|
||||
foreach ($datas as $name => $value) {
|
||||
$this->addViewData($name, $value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getViewDatas(string $context): array
|
||||
{
|
||||
return $this->viewDatas[$context] ?? [];
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setField(string $context, string $label, string $field, array $options): self
|
||||
{
|
||||
if (!isset($this->fields[$context])) {
|
||||
$this->fields[$context] = [];
|
||||
}
|
||||
|
||||
$this->fields[$context][$label] = [
|
||||
'field' => $field,
|
||||
'options' => $options,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFields(string $context): array
|
||||
{
|
||||
return $this->fields[$context] ?? [];
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setMaxPerPage(string $page, int $max)
|
||||
{
|
||||
$this->maxPerPage[$page] = $max;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMaxPerPage(string $page, int $default = 20)
|
||||
{
|
||||
return $this->maxPerPage[$page] ?? $default;
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setI18n(array $locales, string $defaultLocale): self
|
||||
{
|
||||
$this->locales = $locales;
|
||||
$this->defaultLocale = $defaultLocale;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLocales(): array
|
||||
{
|
||||
return $this->locales;
|
||||
}
|
||||
|
||||
public function getDefaultLocale(): ?string
|
||||
{
|
||||
return $this->defaultLocale;
|
||||
}
|
||||
|
||||
public function isI18n(): bool
|
||||
{
|
||||
return !empty($this->locales);
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setDefaultSort(string $context, string $label, string $direction = 'asc'): self
|
||||
{
|
||||
$this->defaultSort[$context] = [
|
||||
'label' => $label,
|
||||
'direction' => $direction,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDefaultSort(string $context)
|
||||
{
|
||||
return $this->defaultSort[$context] ?? null;
|
||||
}
|
||||
|
||||
public function setIsSortableCollection(string $page, bool $isSortableCollection): self
|
||||
{
|
||||
$this->isSortableCollection[$page] = $isSortableCollection;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIsSortableCollection(string $page): bool
|
||||
{
|
||||
return $this->isSortableCollection[$page] ?? false;
|
||||
}
|
||||
|
||||
public function setSortableCollectionProperty(string $sortableCollectionProperty): self
|
||||
{
|
||||
$this->sortableCollectionProperty = $sortableCollectionProperty;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSortableCollectionProperty(): string
|
||||
{
|
||||
return $this->sortableCollectionProperty;
|
||||
}
|
||||
|
||||
public function setShowActions(bool $showActions): self
|
||||
{
|
||||
$this->showActions = $showActions;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getShowActions(): bool
|
||||
{
|
||||
return $this->showActions;
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Crud\Exception;
|
||||
|
||||
/**
|
||||
* class CrudConfigurationException.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class CrudConfigurationException extends \Exception
|
||||
{
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Crud\Field;
|
||||
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Twig\Environment;
|
||||
|
||||
/**
|
||||
* class ButtonField.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class ButtonField extends Field
|
||||
{
|
||||
public function buildView(Environment $twig, $entity, array $options, ?string $locale = null)
|
||||
{
|
||||
if (isset($options['button_attr_builder']) && is_callable($options['button_attr_builder'])) {
|
||||
$options['button_attr'] = (array) call_user_func($options['button_attr_builder'], $entity, $options);
|
||||
}
|
||||
|
||||
return parent::buildView($twig, $entity, $options, $locale);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): OptionsResolver
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
|
||||
$resolver->setDefaults([
|
||||
'view' => '@Core/admin/crud/field/button.html.twig',
|
||||
'button_attr' => [],
|
||||
'button_attr_builder' => null,
|
||||
'button_tag' => 'button',
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('button_attr', ['array']);
|
||||
$resolver->setAllowedTypes('button_tag', ['string']);
|
||||
$resolver->setAllowedTypes('button_attr_builder', ['null', 'callable']);
|
||||
|
||||
return $resolver;
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Crud\Field;
|
||||
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* class DateField.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class DateField extends Field
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): OptionsResolver
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
|
||||
$resolver->setDefaults([
|
||||
'view' => '@Core/admin/crud/field/date.html.twig',
|
||||
'format' => 'Y-m-d',
|
||||
]);
|
||||
|
||||
return $resolver;
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Crud\Field;
|
||||
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* class DatetimeField.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class DatetimeField extends Field
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): OptionsResolver
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
|
||||
$resolver->setDefaults([
|
||||
'view' => '@Core/admin/crud/field/date.html.twig',
|
||||
'format' => 'Y-m-d H:i:s',
|
||||
]);
|
||||
|
||||
return $resolver;
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Crud\Field;
|
||||
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Twig\Environment;
|
||||
|
||||
/**
|
||||
* class Field.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
abstract class Field
|
||||
{
|
||||
public function buildView(Environment $twig, $entity, array $options, ?string $locale = null)
|
||||
{
|
||||
return $twig->render($this->getView($options), [
|
||||
'entity' => $entity,
|
||||
'value' => $this->getValue($entity, $options, $locale),
|
||||
'options' => $options,
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): OptionsResolver
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'property' => null,
|
||||
'property_builder' => null,
|
||||
'view' => null,
|
||||
'raw' => false,
|
||||
'sort' => null,
|
||||
'href' => null,
|
||||
'href_attr' => [],
|
||||
'attr' => [],
|
||||
]);
|
||||
|
||||
$resolver->setRequired('view');
|
||||
$resolver->setAllowedTypes('property', ['null', 'string']);
|
||||
$resolver->setAllowedTypes('view', 'string');
|
||||
$resolver->setAllowedTypes('attr', 'array');
|
||||
$resolver->setAllowedTypes('href', ['null', 'string', 'callable']);
|
||||
$resolver->setAllowedTypes('href_attr', 'array', 'callable');
|
||||
$resolver->setAllowedTypes('raw', 'boolean');
|
||||
$resolver->setAllowedTypes('property_builder', ['null', 'callable']);
|
||||
$resolver->setAllowedValues('sort', function($value) {
|
||||
if ($value === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!is_array($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$isValidParam1 = !empty($value[0]) && is_string($value[0]);
|
||||
$isValidParam2 = !empty($value[1]) && (is_string($value[1]) || is_callable($value[1]));
|
||||
|
||||
return $isValidParam1 && $isValidParam2;
|
||||
});
|
||||
|
||||
return $resolver;
|
||||
}
|
||||
|
||||
protected function getValue($entity, array $options, ?string $locale = null)
|
||||
{
|
||||
if (null !== $options['property']) {
|
||||
$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()->getPropertyAccessor();
|
||||
|
||||
try {
|
||||
$value = $propertyAccessor->getValue($entity, $options['property']);
|
||||
} catch (NoSuchPropertyException $e) {
|
||||
if (null !== $locale) {
|
||||
$value = $propertyAccessor->getValue($entity->translate($locale), $options['property']);
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
} elseif (null !== $options['property_builder']) {
|
||||
$value = call_user_func($options['property_builder'], $entity, $options);
|
||||
} else {
|
||||
$value = null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function getView(array $options)
|
||||
{
|
||||
return $options['view'];
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Crud\Field;
|
||||
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* class ImageField.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class ImageField extends Field
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): OptionsResolver
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
|
||||
$resolver->setDefaults([
|
||||
'view' => '@Core/admin/crud/field/image.html.twig',
|
||||
'image_attr' => [],
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('image_attr', ['array']);
|
||||
|
||||
return $resolver;
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Crud\Field;
|
||||
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* class TextField.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class TextField extends Field
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): OptionsResolver
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
|
||||
$resolver->setDefaults([
|
||||
'view' => '@Core/admin/crud/field/text.html.twig',
|
||||
]);
|
||||
|
||||
return $resolver;
|
||||
}
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
|
||||
class Configuration implements ConfigurationInterface
|
||||
{
|
||||
public function getConfigTreeBuilder(): TreeBuilder
|
||||
{
|
||||
$defaultMimetypes = [
|
||||
'image/png',
|
||||
'image/jpg',
|
||||
'image/jpeg',
|
||||
'image/gif',
|
||||
'image/svg+xml',
|
||||
'video/mp4',
|
||||
'audio/mpeg3',
|
||||
'audio/x-mpeg-3',
|
||||
'multipart/x-zip',
|
||||
'multipart/x-gzip',
|
||||
'application/pdf',
|
||||
'application/ogg',
|
||||
'application/zip',
|
||||
'application/rar',
|
||||
'application/x-rar-compressed',
|
||||
'application/x-zip-compressed',
|
||||
'application/tar',
|
||||
'application/x-tar',
|
||||
'application/x-bzip',
|
||||
'application/x-bzip2',
|
||||
'application/x-gzip',
|
||||
'application/octet-stream',
|
||||
'application/msword',
|
||||
'text/plain',
|
||||
'text/css',
|
||||
];
|
||||
|
||||
$defaultLocked = [
|
||||
'%kernel.project_dir%/public/uploads',
|
||||
];
|
||||
|
||||
$treeBuilder = new TreeBuilder('core');
|
||||
|
||||
$treeBuilder->getRootNode()
|
||||
->children()
|
||||
->arrayNode('site')
|
||||
->children()
|
||||
->scalarNode('name')
|
||||
->defaultValue('Murph')
|
||||
->isRequired()
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->scalarNode('logo')
|
||||
->defaultValue('build/images/core/logo.svg')
|
||||
->isRequired()
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->arrayNode('controllers')
|
||||
->prototype('array')
|
||||
->children()
|
||||
->scalarNode('name')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->scalarNode('action')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('pages')
|
||||
->prototype('array')
|
||||
->children()
|
||||
->scalarNode('name')
|
||||
->isRequired()
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->arrayNode('templates')
|
||||
->prototype('array')
|
||||
->children()
|
||||
->scalarNode('name')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->scalarNode('file')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('file_manager')
|
||||
->children()
|
||||
->arrayNode('mimes')
|
||||
->scalarPrototype()
|
||||
->end()
|
||||
->defaultValue($defaultMimetypes)
|
||||
->end()
|
||||
->scalarNode('path')
|
||||
->defaultValue('%kernel.project_dir%/public/uploads')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->scalarNode('path_uri')
|
||||
->defaultValue('/uploads')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->arrayNode('path_locked')
|
||||
->scalarPrototype()
|
||||
->end()
|
||||
->defaultValue($defaultLocked)
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end();
|
||||
|
||||
return $treeBuilder;
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\Extension;
|
||||
|
||||
class CoreExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
$configuration = $this->getConfiguration($configs, $container);
|
||||
$config = $this->processConfiguration($configuration, $configs);
|
||||
|
||||
$container->setParameter('core', $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfiguration(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
return new Configuration();
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Doctrine;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
trait Timestampable
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(name="created_at", type="datetime")
|
||||
*/
|
||||
protected $createdAt;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="updated_at", type="datetime")
|
||||
*/
|
||||
protected $updatedAt;
|
||||
|
||||
/**
|
||||
* @ORM\PrePersist
|
||||
*/
|
||||
public function onPrePersist(): void
|
||||
{
|
||||
$this->createdAt = new \DateTime();
|
||||
$this->updatedAt = new \DateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\PreUpdate
|
||||
*/
|
||||
public function onPreUpdate(): void
|
||||
{
|
||||
$this->updatedAt = new \DateTime();
|
||||
}
|
||||
|
||||
public function setCreatedAt(?\DateTime $createdAt): self
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?\DateTime
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setUpdatedAt(?\DateTime $updatedAt): self
|
||||
{
|
||||
$this->updatedAt = $updatedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUpdatedAt(): ?\DateTime
|
||||
{
|
||||
return $this->updatedAt;
|
||||
}
|
||||
}
|
0
core/Entity/.gitignore
vendored
0
core/Entity/.gitignore
vendored
|
@ -1,103 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Entity\Analytic;
|
||||
|
||||
use App\Core\Entity\Site\Node;
|
||||
use App\Repository\Entity\Analytic\NodeViewRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use App\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=ViewRepository::class)
|
||||
* @ORM\Table(name="analytic_referer")
|
||||
*/
|
||||
class Referer implements EntityInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Node::class, inversedBy="analyticReferers")
|
||||
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
|
||||
*/
|
||||
protected $node;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $uri;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer", options={"default"=0})
|
||||
*/
|
||||
protected $views = 0;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="date")
|
||||
*/
|
||||
protected $date;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getNode(): ?Node
|
||||
{
|
||||
return $this->node;
|
||||
}
|
||||
|
||||
public function setNode(?Node $node): self
|
||||
{
|
||||
$this->node = $node;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUri(): ?string
|
||||
{
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
public function setUri(string $uri): self
|
||||
{
|
||||
$this->uri = $uri;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getViews(): ?int
|
||||
{
|
||||
return $this->views;
|
||||
}
|
||||
|
||||
public function setViews(int $views): self
|
||||
{
|
||||
$this->views = $views;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addView(): self
|
||||
{
|
||||
++$this->views;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDate(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
public function setDate(\DateTimeInterface $date): self
|
||||
{
|
||||
$this->date = $date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -1,151 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Entity\Analytic;
|
||||
|
||||
use App\Core\Entity\Site\Node;
|
||||
use App\Repository\Entity\Analytic\NodeViewRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use App\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=ViewRepository::class)
|
||||
* @ORM\Table(name="analytic_view")
|
||||
*/
|
||||
class View implements EntityInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Node::class, inversedBy="analyticViews")
|
||||
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
|
||||
*/
|
||||
protected $node;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer", options={"default"=0})
|
||||
*/
|
||||
protected $views = 0;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer", options={"default"=0})
|
||||
*/
|
||||
protected $desktopViews = 0;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer", options={"default"=0})
|
||||
*/
|
||||
protected $mobileViews = 0;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="date")
|
||||
*/
|
||||
protected $date;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getNode(): ?Node
|
||||
{
|
||||
return $this->node;
|
||||
}
|
||||
|
||||
public function setNode(?Node $node): self
|
||||
{
|
||||
$this->node = $node;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPath(): ?string
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function setPath(string $path): self
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getViews(): ?int
|
||||
{
|
||||
return $this->views;
|
||||
}
|
||||
|
||||
public function setViews(int $views): self
|
||||
{
|
||||
$this->views = $views;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addView(): self
|
||||
{
|
||||
++$this->views;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDesktopViews(): ?int
|
||||
{
|
||||
return $this->desktopViews;
|
||||
}
|
||||
|
||||
public function setDesktopViews(int $desktopViews): self
|
||||
{
|
||||
$this->desktopViews = $desktopViews;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addDesktopView(): self
|
||||
{
|
||||
++$this->desktopViews;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMobileViews(): ?int
|
||||
{
|
||||
return $this->mobileViews;
|
||||
}
|
||||
|
||||
public function setMobileViews(int $mobileViews): self
|
||||
{
|
||||
$this->mobileViews = $mobileViews;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addMobileView(): self
|
||||
{
|
||||
++$this->mobileViews;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDate(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
public function setDate(\DateTimeInterface $date): self
|
||||
{
|
||||
$this->date = $date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Entity;
|
||||
|
||||
interface EntityInterface
|
||||
{
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Entity;
|
||||
|
||||
use App\Repository\Entity\FileInformationRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=FileInformationRepository::class)
|
||||
*/
|
||||
class FileInformation implements EntityInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue(strategy="NONE")
|
||||
* @ORM\Column(type="string", length=96, unique=true)
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text", nullable=true)
|
||||
*/
|
||||
protected $attributes;
|
||||
|
||||
public function getId(): ?string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId(string $id): self
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAttributes()
|
||||
{
|
||||
return (array) json_decode($this->attributes, true);
|
||||
}
|
||||
|
||||
public function setAttributes($attributes): self
|
||||
{
|
||||
$this->attributes = json_encode($attributes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Entity;
|
||||
|
||||
use App\Core\Entity\Site\Navigation;
|
||||
use App\Core\Repository\NavigationSettingRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=NavigationSettingRepository::class)
|
||||
*/
|
||||
class NavigationSetting implements EntityInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $section;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $code;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text", nullable=true)
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Navigation::class, inversedBy="navigationSettings")
|
||||
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
|
||||
*/
|
||||
protected $navigation;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getSection(): ?string
|
||||
{
|
||||
return $this->section;
|
||||
}
|
||||
|
||||
public function setSection(string $section): self
|
||||
{
|
||||
$this->section = $section;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setLabel(string $label): self
|
||||
{
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCode(): ?string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function setCode(string $code): self
|
||||
{
|
||||
$this->code = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
return json_decode($this->value, true);
|
||||
}
|
||||
|
||||
public function setValue($value): self
|
||||
{
|
||||
$this->value = json_encode($value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNavigation(): ?Navigation
|
||||
{
|
||||
return $this->navigation;
|
||||
}
|
||||
|
||||
public function setNavigation(?Navigation $navigation): self
|
||||
{
|
||||
$this->navigation = $navigation;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -1,211 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Entity;
|
||||
|
||||
use App\Core\Repository\RedirectRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=RedirectRepository::class)
|
||||
*/
|
||||
class Redirect implements EntityInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=5)
|
||||
*/
|
||||
protected $scheme;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $domain;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=6)
|
||||
*/
|
||||
protected $domainType;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $rule;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=6)
|
||||
*/
|
||||
protected $ruleType;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $location;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
protected $redirectCode;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer", nullable=true)
|
||||
*/
|
||||
protected $sortOrder;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean")
|
||||
*/
|
||||
protected $isEnabled;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean")
|
||||
*/
|
||||
protected $reuseQueryString;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getScheme(): ?string
|
||||
{
|
||||
return $this->scheme;
|
||||
}
|
||||
|
||||
public function setScheme(string $scheme): self
|
||||
{
|
||||
$this->scheme = $scheme;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDomain(): ?string
|
||||
{
|
||||
return $this->domain;
|
||||
}
|
||||
|
||||
public function setDomain(string $domain): self
|
||||
{
|
||||
$this->domain = $domain;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDomainType(): ?string
|
||||
{
|
||||
return $this->domainType;
|
||||
}
|
||||
|
||||
public function setDomainType(string $domainType): self
|
||||
{
|
||||
$this->domainType = $domainType;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRule(): ?string
|
||||
{
|
||||
return $this->rule;
|
||||
}
|
||||
|
||||
public function setRule(string $rule): self
|
||||
{
|
||||
$this->rule = $rule;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRuleType(): ?string
|
||||
{
|
||||
return $this->ruleType;
|
||||
}
|
||||
|
||||
public function setRuleType(string $ruleType): self
|
||||
{
|
||||
$this->ruleType = $ruleType;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLocation(): ?string
|
||||
{
|
||||
return $this->location;
|
||||
}
|
||||
|
||||
public function setLocation(string $location): self
|
||||
{
|
||||
$this->location = $location;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRedirectCode(): ?int
|
||||
{
|
||||
return $this->redirectCode;
|
||||
}
|
||||
|
||||
public function setRedirectCode(int $redirectCode): self
|
||||
{
|
||||
$this->redirectCode = $redirectCode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setLabel(string $label): self
|
||||
{
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSortOrder(): ?int
|
||||
{
|
||||
return $this->sortOrder;
|
||||
}
|
||||
|
||||
public function setSortOrder(?int $sortOrder): self
|
||||
{
|
||||
$this->sortOrder = $sortOrder;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIsEnabled(): ?bool
|
||||
{
|
||||
return $this->isEnabled;
|
||||
}
|
||||
|
||||
public function setIsEnabled(bool $isEnabled): self
|
||||
{
|
||||
$this->isEnabled = $isEnabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getReuseQueryString(): ?bool
|
||||
{
|
||||
return $this->reuseQueryString;
|
||||
}
|
||||
|
||||
public function setReuseQueryString(bool $reuseQueryString): self
|
||||
{
|
||||
$this->reuseQueryString = $reuseQueryString;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Entity;
|
||||
|
||||
use App\Core\Repository\SettingRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=SettingRepository::class)
|
||||
*/
|
||||
class Setting implements EntityInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $section;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $code;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text", nullable=true)
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getSection(): ?string
|
||||
{
|
||||
return $this->section;
|
||||
}
|
||||
|
||||
public function setSection(string $section): self
|
||||
{
|
||||
$this->section = $section;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setLabel(string $label): self
|
||||
{
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCode(): ?string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function setCode(string $code): self
|
||||
{
|
||||
$this->code = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
return json_decode($this->value, true);
|
||||
}
|
||||
|
||||
public function setValue($value): self
|
||||
{
|
||||
$this->value = json_encode($value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Entity\Site;
|
||||
|
||||
use App\Core\Doctrine\Timestampable;
|
||||
use App\Core\Entity\EntityInterface;
|
||||
use App\Core\Repository\Site\MenuRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=MenuRepository::class)
|
||||
* @ORM\HasLifecycleCallbacks
|
||||
*/
|
||||
class Menu implements EntityInterface
|
||||
{
|
||||
use Timestampable;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $code;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Navigation::class, inversedBy="menus")
|
||||
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
|
||||
*/
|
||||
protected $navigation;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity=Node::class, mappedBy="menu", orphanRemoval=true, cascade={"remove", "persist"})
|
||||
*/
|
||||
protected $nodes;
|
||||
|
||||
/**
|
||||
* @ORM\OneToOne(targetEntity=Node::class, cascade={"persist"})
|
||||
* @ORM\JoinColumn(onDelete="CASCADE")
|
||||
*/
|
||||
protected $rootNode;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->nodes = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setLabel(string $label): self
|
||||
{
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCode(): ?string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function setCode(string $code): self
|
||||
{
|
||||
$this->code = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNavigation(): ?Navigation
|
||||
{
|
||||
return $this->navigation;
|
||||
}
|
||||
|
||||
public function setNavigation(?Navigation $navigation): self
|
||||
{
|
||||
$this->navigation = $navigation;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|Node[]
|
||||
*/
|
||||
public function getNodes(): Collection
|
||||
{
|
||||
return $this->nodes;
|
||||
}
|
||||
|
||||
public function addNode(Node $node): self
|
||||
{
|
||||
if (!$this->nodes->contains($node)) {
|
||||
$this->nodes[] = $node;
|
||||
$node->setMenu($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeNode(Node $node): self
|
||||
{
|
||||
if ($this->nodes->removeElement($node)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($node->getMenu() === $this) {
|
||||
$node->setMenu(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRootNode(): ?Node
|
||||
{
|
||||
return $this->rootNode;
|
||||
}
|
||||
|
||||
public function setRootNode(?Node $rootNode): self
|
||||
{
|
||||
$this->rootNode = $rootNode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRouteName(): string
|
||||
{
|
||||
return $this->getNavigation()->getRouteName().'_'.($this->getCode() ? $this->getCode() : $this->getId());
|
||||
}
|
||||
}
|
|
@ -1,243 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Entity\Site;
|
||||
|
||||
use App\Core\Doctrine\Timestampable;
|
||||
use App\Core\Entity\EntityInterface;
|
||||
use App\Core\Entity\NavigationSetting;
|
||||
use App\Core\Repository\Site\NavigationRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=NavigationRepository::class)
|
||||
* @ORM\HasLifecycleCallbacks
|
||||
*/
|
||||
class Navigation implements EntityInterface
|
||||
{
|
||||
use Timestampable;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $code;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
protected $domain;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean", options={"default"=0})
|
||||
*/
|
||||
protected $forceDomain = false;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text", nullable=true)
|
||||
*/
|
||||
protected $additionalDomains = '[]';
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity=Menu::class, mappedBy="navigation")
|
||||
*/
|
||||
protected $menus;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=10)
|
||||
*/
|
||||
protected $locale = 'en';
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer", nullable=true)
|
||||
*/
|
||||
protected $sortOrder;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity=NavigationSetting::class, mappedBy="navigation", orphanRemoval=true)
|
||||
*/
|
||||
protected $navigationSettings;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->menus = new ArrayCollection();
|
||||
$this->navigationSettings = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setLabel(string $label): self
|
||||
{
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCode(): ?string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function setCode(string $code): self
|
||||
{
|
||||
$this->code = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDomain(): ?string
|
||||
{
|
||||
return $this->domain;
|
||||
}
|
||||
|
||||
public function setDomain(string $domain): self
|
||||
{
|
||||
$this->domain = $domain;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getForceDomain(): ?bool
|
||||
{
|
||||
return $this->forceDomain;
|
||||
}
|
||||
|
||||
public function setForceDomain(bool $forceDomain): self
|
||||
{
|
||||
$this->forceDomain = $forceDomain;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAdditionalDomains(): array
|
||||
{
|
||||
return (array) json_decode($this->additionalDomains, true);
|
||||
}
|
||||
|
||||
public function setAdditionalDomains(array $additionalDomains): self
|
||||
{
|
||||
$this->additionalDomains = json_encode($additionalDomains);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|Menu[]
|
||||
*/
|
||||
public function getMenus(): Collection
|
||||
{
|
||||
return $this->menus;
|
||||
}
|
||||
|
||||
public function addMenu(Menu $menu): self
|
||||
{
|
||||
if (!$this->menus->contains($menu)) {
|
||||
$this->menus[] = $menu;
|
||||
$menu->setNavigation($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeMenu(Menu $menu): self
|
||||
{
|
||||
if ($this->menus->removeElement($menu)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($menu->getNavigation() === $this) {
|
||||
$menu->setNavigation(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMenu(string $code): ?Menu
|
||||
{
|
||||
foreach ($this->menus as $menu) {
|
||||
if ($menu->getCode() === $code) {
|
||||
return $menu;
|
||||
}
|
||||
}
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
public function getRouteName(): string
|
||||
{
|
||||
return $this->getCode() ? $this->getCode() : 'navigation_'.$this->getId();
|
||||
}
|
||||
|
||||
public function getLocale(): ?string
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
public function setLocale(string $locale): self
|
||||
{
|
||||
$this->locale = $locale;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSortOrder(): ?int
|
||||
{
|
||||
return $this->sortOrder;
|
||||
}
|
||||
|
||||
public function setSortOrder(?int $sortOrder): self
|
||||
{
|
||||
$this->sortOrder = $sortOrder;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|NavigationSetting[]
|
||||
*/
|
||||
public function getNavigationSettings(): Collection
|
||||
{
|
||||
return $this->navigationSettings;
|
||||
}
|
||||
|
||||
public function addNavigationSetting(NavigationSetting $navigationSetting): self
|
||||
{
|
||||
if (!$this->navigationSettings->contains($navigationSetting)) {
|
||||
$this->navigationSettings[] = $navigationSetting;
|
||||
$navigationSetting->setNavigation($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeNavigationSetting(NavigationSetting $navigationSetting): self
|
||||
{
|
||||
if ($this->navigationSettings->removeElement($navigationSetting)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($navigationSetting->getNavigation() === $this) {
|
||||
$navigationSetting->setNavigation(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -1,643 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Entity\Site;
|
||||
|
||||
use App\Core\Doctrine\Timestampable;
|
||||
use App\Core\Entity\EntityInterface;
|
||||
use App\Core\Entity\NodeView;
|
||||
use App\Core\Entity\Site\Page\Page;
|
||||
use App\Core\Repository\Site\NodeRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use function Symfony\Component\String\u;
|
||||
use App\Core\Entity\Analytic\View;
|
||||
use App\Core\Entity\Analytic\Referer;
|
||||
|
||||
/**
|
||||
* @Gedmo\Tree(type="nested")
|
||||
* @ORM\HasLifecycleCallbacks
|
||||
* @ORM\Entity(repositoryClass=NodeRepository::class)
|
||||
*/
|
||||
class Node implements EntityInterface
|
||||
{
|
||||
use Timestampable;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Menu::class, inversedBy="nodes", cascade={"persist", "remove"})
|
||||
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
|
||||
*/
|
||||
protected $menu;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean", options={"default"=0})
|
||||
*/
|
||||
protected $disableUrl = false;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean", options={"default"=0})
|
||||
*/
|
||||
protected $isVisible = false;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeLeft
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
protected $treeLeft;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeLevel
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
protected $treeLevel;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeRight
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
protected $treeRight;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeRoot
|
||||
* @ORM\ManyToOne(targetEntity="Node")
|
||||
* @ORM\JoinColumn(referencedColumnName="id", onDelete="CASCADE")
|
||||
*/
|
||||
protected $treeRoot;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeParent
|
||||
* @ORM\ManyToOne(targetEntity="Node", inversedBy="children")
|
||||
* @ORM\JoinColumn(referencedColumnName="id", onDelete="CASCADE")
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="Node", mappedBy="parent")
|
||||
* @ORM\OrderBy({"treeLeft"="ASC"})
|
||||
*/
|
||||
protected $children;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Page::class, inversedBy="nodes", cascade={"persist"})
|
||||
* @ORM\JoinColumn(nullable=true, onDelete="SET NULL")
|
||||
*/
|
||||
protected $page;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
protected $code;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="array", nullable=true)
|
||||
*/
|
||||
protected $parameters = [];
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="array", nullable=true)
|
||||
*/
|
||||
protected $attributes = [];
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
protected $controller;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="array", nullable=true)
|
||||
*/
|
||||
protected $sitemapParameters = [];
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Node::class, inversedBy="aliasNodes")
|
||||
* @ORM\JoinColumn(nullable=true, onDelete="SET NULL")
|
||||
*/
|
||||
protected $aliasNode;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity=Node::class, mappedBy="aliasNode")
|
||||
*/
|
||||
protected $aliasNodes;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
protected $contentType;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean", options={"default"=0})
|
||||
*/
|
||||
protected $enableAnalytics = false;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity=View::class, mappedBy="node")
|
||||
*/
|
||||
protected $analyticViews;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity=Referer::class, mappedBy="node")
|
||||
*/
|
||||
protected $analyticReferers;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->children = new ArrayCollection();
|
||||
$this->aliasNodes = new ArrayCollection();
|
||||
$this->analyticViews = new ArrayCollection();
|
||||
$this->analyticReferers = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getMenu(): ?Menu
|
||||
{
|
||||
return $this->menu;
|
||||
}
|
||||
|
||||
public function setMenu(?Menu $menu): self
|
||||
{
|
||||
$this->menu = $menu;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTreeLeft(): ?int
|
||||
{
|
||||
return $this->treeLeft;
|
||||
}
|
||||
|
||||
public function setTreeLeft(int $treeLeft): self
|
||||
{
|
||||
$this->treeLeft = $treeLeft;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTreeLevel(): ?int
|
||||
{
|
||||
return $this->treeLevel;
|
||||
}
|
||||
|
||||
public function setTreeLevel(int $treeLevel): self
|
||||
{
|
||||
$this->treeLevel = $treeLevel;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTreeRight(): ?int
|
||||
{
|
||||
return $this->treeRight;
|
||||
}
|
||||
|
||||
public function setTreeRight(int $treeRight): self
|
||||
{
|
||||
$this->treeRight = $treeRight;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTreeRoot(): ?self
|
||||
{
|
||||
return $this->treeRoot;
|
||||
}
|
||||
|
||||
public function setTreeRoot(?self $treeRoot): self
|
||||
{
|
||||
$this->treeRoot = $treeRoot;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getParent(): ?self
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
public function setParent(?self $parent): self
|
||||
{
|
||||
$this->parent = $parent;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|Node[]
|
||||
*/
|
||||
public function getChildren(array $criteria = []): Collection
|
||||
{
|
||||
if (null === $this->children) {
|
||||
$this->children = new ArrayCollection();
|
||||
}
|
||||
|
||||
if (!empty($criteria)) {
|
||||
$children = new ArrayCollection();
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$add = true;
|
||||
|
||||
if (isset($criteria['visible']) && $child->getIsVisible() !== $criteria['visible']) {
|
||||
$add = false;
|
||||
}
|
||||
|
||||
if ($add) {
|
||||
$children->add($child);
|
||||
}
|
||||
}
|
||||
|
||||
return $children;
|
||||
}
|
||||
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
public function addChild(Node $child): self
|
||||
{
|
||||
if (!$this->children->contains($child)) {
|
||||
$this->children[] = $child;
|
||||
$child->setParent($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeChild(Node $child): self
|
||||
{
|
||||
if ($this->children->removeElement($child)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($child->getParent() === $this) {
|
||||
$child->setParent(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAllChildren(array $criteria = []): ArrayCollection
|
||||
{
|
||||
$children = [];
|
||||
|
||||
$getChildren = function (Node $node) use (&$children, &$getChildren) {
|
||||
foreach ($node->getChildren() as $nodeChildren) {
|
||||
$children[] = $nodeChildren;
|
||||
|
||||
$getChildren($nodeChildren);
|
||||
}
|
||||
};
|
||||
|
||||
$getChildren($this);
|
||||
|
||||
usort($children, function ($a, $b) {
|
||||
return $a->getTreeLeft() < $b->getTreeLeft() ? -1 : 1;
|
||||
});
|
||||
|
||||
if (!empty($criteria)) {
|
||||
foreach ($children as $key => $child) {
|
||||
if (isset($criteria['visible']) && $child->getIsVisible() !== $criteria['visible']) {
|
||||
unset($children[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayCollection($children);
|
||||
}
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setLabel(?string $label): self
|
||||
{
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUrl(): ?string
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function setUrl(?string $url): self
|
||||
{
|
||||
$this->url = $url;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasExternalUrl(): bool
|
||||
{
|
||||
$string = u($this->getUrl());
|
||||
|
||||
return $string->startsWith('http://') || $string->startsWith('https://');
|
||||
}
|
||||
|
||||
public function hasAppUrl(): bool
|
||||
{
|
||||
$string = u($this->getUrl());
|
||||
|
||||
foreach (['tel:', 'fax:', 'mailto:'] as $prefix) {
|
||||
if ($string->startsWith($prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getIsVisible(): ?bool
|
||||
{
|
||||
return $this->isVisible;
|
||||
}
|
||||
|
||||
public function setIsVisible(bool $isVisible): self
|
||||
{
|
||||
$this->isVisible = $isVisible;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDisableUrl(): ?bool
|
||||
{
|
||||
return $this->disableUrl;
|
||||
}
|
||||
|
||||
public function setDisableUrl(bool $disableUrl): self
|
||||
{
|
||||
$this->disableUrl = $disableUrl;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTreeLabel()
|
||||
{
|
||||
$prefix = str_repeat('-', ($this->getTreeLevel() - 1) * 5);
|
||||
|
||||
return trim($prefix.' '.$this->getLabel());
|
||||
}
|
||||
|
||||
public function getPage(): ?Page
|
||||
{
|
||||
if ($this->getAliasNode()) {
|
||||
return $this->getAliasNode()->getPage();
|
||||
}
|
||||
|
||||
return $this->page;
|
||||
}
|
||||
|
||||
public function setPage(?Page $page): self
|
||||
{
|
||||
$this->page = $page;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRouteName(): string
|
||||
{
|
||||
if ($this->getAliasNode()) {
|
||||
return $this->getAliasNode()->getRouteName();
|
||||
}
|
||||
|
||||
return $this->getMenu()->getRouteName().'_'.($this->getCode() ? $this->getCode() : $this->getId());
|
||||
}
|
||||
|
||||
public function getCode(): ?string
|
||||
{
|
||||
if ($this->getAliasNode()) {
|
||||
return $this->getAliasNode()->getCode();
|
||||
}
|
||||
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function setCode(?string $code): self
|
||||
{
|
||||
$this->code = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getParameters(): ?array
|
||||
{
|
||||
if ($this->getAliasNode()) {
|
||||
return $this->getAliasNode()->getParameters();
|
||||
}
|
||||
|
||||
if (!is_array($this->parameters)) {
|
||||
$this->parameters = [];
|
||||
}
|
||||
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
public function setParameters(array $parameters): self
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAttributes(): ?array
|
||||
{
|
||||
if (!is_array($this->attributes)) {
|
||||
$this->attributes = [];
|
||||
}
|
||||
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
public function setAttributes(array $attributes): self
|
||||
{
|
||||
$this->attributes = $attributes;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getController(): ?string
|
||||
{
|
||||
if ($this->getAliasNode()) {
|
||||
return $this->getAliasNode()->getController();
|
||||
}
|
||||
|
||||
return $this->controller;
|
||||
}
|
||||
|
||||
public function setController(?string $controller): self
|
||||
{
|
||||
$this->controller = $controller;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSitemapParameters(): ?array
|
||||
{
|
||||
if ($this->getAliasNode()) {
|
||||
return $this->getAliasNode()->getSitemapParameters();
|
||||
}
|
||||
|
||||
if (!is_array($this->sitemapParameters)) {
|
||||
$this->sitemapParameters = [
|
||||
'isVisible' => false,
|
||||
'priority' => 0,
|
||||
'changeFrequency' => 'daily',
|
||||
];
|
||||
}
|
||||
|
||||
return $this->sitemapParameters;
|
||||
}
|
||||
|
||||
public function setSitemapParameters(?array $sitemapParameters): self
|
||||
{
|
||||
$this->sitemapParameters = $sitemapParameters;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAliasNode(): ?self
|
||||
{
|
||||
return $this->aliasNode;
|
||||
}
|
||||
|
||||
public function setAliasNode(?self $aliasNode): self
|
||||
{
|
||||
$this->aliasNode = $aliasNode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|self[]
|
||||
*/
|
||||
public function getAliasNodes(): Collection
|
||||
{
|
||||
return $this->aliasNodes;
|
||||
}
|
||||
|
||||
public function addAliasNode(self $aliasNode): self
|
||||
{
|
||||
if (!$this->aliasNodes->contains($aliasNode)) {
|
||||
$this->aliasNodes[] = $aliasNode;
|
||||
$aliasNode->setAliasNode($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeAliasNode(self $aliasNode): self
|
||||
{
|
||||
if ($this->aliasNodes->removeElement($aliasNode)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($aliasNode->getAliasNode() === $this) {
|
||||
$aliasNode->setAliasNode(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContentType(): ?string
|
||||
{
|
||||
return $this->contentType;
|
||||
}
|
||||
|
||||
public function setContentType(?string $contentType): self
|
||||
{
|
||||
$this->contentType = $contentType;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEnableAnalytics(): ?bool
|
||||
{
|
||||
return $this->enableAnalytics;
|
||||
}
|
||||
|
||||
public function setEnableAnalytics(bool $enableAnalytics): self
|
||||
{
|
||||
$this->enableAnalytics = $enableAnalytics;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|View[]
|
||||
*/
|
||||
public function getAnalyticViews(): Collection
|
||||
{
|
||||
return $this->analyticViews;
|
||||
}
|
||||
|
||||
public function addAnalyticView(View $view): self
|
||||
{
|
||||
if (!$this->analyticViews->contains($view)) {
|
||||
$this->analyticViews[] = $view;
|
||||
$view->setNode($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeAnalyticView(View $view): self
|
||||
{
|
||||
if ($this->analyticViews->removeElement($view)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($view->getNode() === $this) {
|
||||
$view->setNode(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|Referer[]
|
||||
*/
|
||||
public function getAnalyticReferers(): Collection
|
||||
{
|
||||
return $this->analyticReferers;
|
||||
}
|
||||
|
||||
public function addAnalyticReferer(Referer $referer): self
|
||||
{
|
||||
if (!$this->analyticReferers->contains($referer)) {
|
||||
$this->analyticReferers[] = $referer;
|
||||
$referer->setNode($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeAnalyticReferer(Referer $referer): self
|
||||
{
|
||||
if ($this->analyticReferers->removeElement($referer)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($referer->getNode() === $this) {
|
||||
$referer->setNode(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue