Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
30de93b6c8 | |||
67767310d3 | |||
d7a36aca4c | |||
7547e11d39 | |||
a53f4be3e5 | |||
4e2971c942 | |||
81de4c1a81 | |||
cf4ec48113 | |||
ecf999aa76 | |||
52984edc3c | |||
1633c17c7b | |||
6afe38d089 | |||
f467b867da |
@ -31,6 +31,9 @@ services:
|
|||||||
- ./misc:/app/misc:delegated
|
- ./misc:/app/misc:delegated
|
||||||
- ./public/lib:/app/public/lib:delegated
|
- ./public/lib:/app/public/lib:delegated
|
||||||
- ./.env.local:/app/.env.local
|
- ./.env.local:/app/.env.local
|
||||||
|
- ./vendor:/app/vendor:delegated
|
||||||
|
- ./public/bundles:/app/public/bundles:delegated
|
||||||
|
|
||||||
|
|
||||||
adminer:
|
adminer:
|
||||||
image: adminer
|
image: adminer
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
"ext-ctype": "*",
|
"ext-ctype": "*",
|
||||||
"ext-iconv": "*",
|
"ext-iconv": "*",
|
||||||
"apereo/phpcas": "^1.6",
|
"apereo/phpcas": "^1.6",
|
||||||
|
"bnine/filesbundle": "^1.0",
|
||||||
|
"bnine/mdeditorbundle": "^1.1",
|
||||||
"doctrine/dbal": "^3",
|
"doctrine/dbal": "^3",
|
||||||
"doctrine/doctrine-bundle": "^2.13",
|
"doctrine/doctrine-bundle": "^2.13",
|
||||||
"doctrine/doctrine-migrations-bundle": "^3.3",
|
"doctrine/doctrine-migrations-bundle": "^3.3",
|
||||||
|
396
composer.lock
generated
396
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "236661a47d3b0278e1198c4fb0940e3a",
|
"content-hash": "2d59dc750783494ae1dbf58d9a3025c3",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "apereo/phpcas",
|
"name": "apereo/phpcas",
|
||||||
@ -77,6 +77,126 @@
|
|||||||
},
|
},
|
||||||
"time": "2023-02-19T19:52:35+00:00"
|
"time": "2023-02-19T19:52:35+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "bnine/filesbundle",
|
||||||
|
"version": "v1.0.6",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/afornerot/bNine-FilesBundle.git",
|
||||||
|
"reference": "add22ab4bd7c7342968e901294a2990b7efc8895"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/afornerot/bNine-FilesBundle/zipball/add22ab4bd7c7342968e901294a2990b7efc8895",
|
||||||
|
"reference": "add22ab4bd7c7342968e901294a2990b7efc8895",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^8.1",
|
||||||
|
"symfony/framework-bundle": "^7.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.85"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"oneup/uploader-bundle": "^4.0"
|
||||||
|
},
|
||||||
|
"type": "symfony-bundle",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "1.0-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Bnine\\FilesBundle\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "afornerot",
|
||||||
|
"email": "arno.nanor@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony bundle for entity-based file browser and management.",
|
||||||
|
"keywords": [
|
||||||
|
"bundle",
|
||||||
|
"directory",
|
||||||
|
"entity",
|
||||||
|
"file-browser",
|
||||||
|
"files",
|
||||||
|
"symfony",
|
||||||
|
"upload"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/afornerot/bNine-FilesBundle/issues",
|
||||||
|
"source": "https://github.com/afornerot/bNine-FilesBundle/tree/v1.0.6"
|
||||||
|
},
|
||||||
|
"time": "2025-08-02T11:06:27+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bnine/mdeditorbundle",
|
||||||
|
"version": "v1.1.4",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/afornerot/bNine-MdEditorBundle.git",
|
||||||
|
"reference": "9c0e29e989f68d64664c060e3b1d284abdebc9c5"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/afornerot/bNine-MdEditorBundle/zipball/9c0e29e989f68d64664c060e3b1d284abdebc9c5",
|
||||||
|
"reference": "9c0e29e989f68d64664c060e3b1d284abdebc9c5",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^8.1",
|
||||||
|
"symfony/framework-bundle": "^7.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.85"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"oneup/uploader-bundle": "^4.0"
|
||||||
|
},
|
||||||
|
"type": "symfony-bundle",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "1.0-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Bnine\\MdEditorBundle\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "afornerot",
|
||||||
|
"email": "arno.nanor@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony bundle for entity-based file browser and management.",
|
||||||
|
"keywords": [
|
||||||
|
"bundle",
|
||||||
|
"editor",
|
||||||
|
"entity",
|
||||||
|
"markdown",
|
||||||
|
"symfony"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/afornerot/bNine-MdEditorBundle/issues",
|
||||||
|
"source": "https://github.com/afornerot/bNine-MdEditorBundle/tree/v1.1.4"
|
||||||
|
},
|
||||||
|
"time": "2025-08-03T20:42:31+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
"version": "0.13.1",
|
"version": "0.13.1",
|
||||||
@ -212,99 +332,6 @@
|
|||||||
},
|
},
|
||||||
"time": "2024-07-08T12:26:09+00:00"
|
"time": "2024-07-08T12:26:09+00:00"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "doctrine/cache",
|
|
||||||
"version": "2.2.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/doctrine/cache.git",
|
|
||||||
"reference": "1ca8f21980e770095a31456042471a57bc4c68fb"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb",
|
|
||||||
"reference": "1ca8f21980e770095a31456042471a57bc4c68fb",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": "~7.1 || ^8.0"
|
|
||||||
},
|
|
||||||
"conflict": {
|
|
||||||
"doctrine/common": ">2.2,<2.4"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"cache/integration-tests": "dev-master",
|
|
||||||
"doctrine/coding-standard": "^9",
|
|
||||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
|
|
||||||
"psr/cache": "^1.0 || ^2.0 || ^3.0",
|
|
||||||
"symfony/cache": "^4.4 || ^5.4 || ^6",
|
|
||||||
"symfony/var-exporter": "^4.4 || ^5.4 || ^6"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Guilherme Blanco",
|
|
||||||
"email": "guilhermeblanco@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Roman Borschel",
|
|
||||||
"email": "roman@code-factory.org"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Benjamin Eberlei",
|
|
||||||
"email": "kontakt@beberlei.de"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Jonathan Wage",
|
|
||||||
"email": "jonwage@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Johannes Schmitt",
|
|
||||||
"email": "schmittjoh@gmail.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.",
|
|
||||||
"homepage": "https://www.doctrine-project.org/projects/cache.html",
|
|
||||||
"keywords": [
|
|
||||||
"abstraction",
|
|
||||||
"apcu",
|
|
||||||
"cache",
|
|
||||||
"caching",
|
|
||||||
"couchdb",
|
|
||||||
"memcached",
|
|
||||||
"php",
|
|
||||||
"redis",
|
|
||||||
"xcache"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/doctrine/cache/issues",
|
|
||||||
"source": "https://github.com/doctrine/cache/tree/2.2.0"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://www.doctrine-project.org/sponsorship.html",
|
|
||||||
"type": "custom"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://www.patreon.com/phpdoctrine",
|
|
||||||
"type": "patreon"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache",
|
|
||||||
"type": "tidelift"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2022-05-20T20:07:39+00:00"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "doctrine/collections",
|
"name": "doctrine/collections",
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
@ -393,28 +420,31 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/dbal",
|
"name": "doctrine/dbal",
|
||||||
"version": "3.9.5",
|
"version": "3.10.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/doctrine/dbal.git",
|
"url": "https://github.com/doctrine/dbal.git",
|
||||||
"reference": "4a4e2eed3134036ee36a147ee0dac037dfa17868"
|
"reference": "1cf840d696373ea0d58ad0a8875c0fadcfc67214"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/doctrine/dbal/zipball/4a4e2eed3134036ee36a147ee0dac037dfa17868",
|
"url": "https://api.github.com/repos/doctrine/dbal/zipball/1cf840d696373ea0d58ad0a8875c0fadcfc67214",
|
||||||
"reference": "4a4e2eed3134036ee36a147ee0dac037dfa17868",
|
"reference": "1cf840d696373ea0d58ad0a8875c0fadcfc67214",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"composer-runtime-api": "^2",
|
"composer-runtime-api": "^2",
|
||||||
"doctrine/cache": "^1.11|^2.0",
|
|
||||||
"doctrine/deprecations": "^0.5.3|^1",
|
"doctrine/deprecations": "^0.5.3|^1",
|
||||||
"doctrine/event-manager": "^1|^2",
|
"doctrine/event-manager": "^1|^2",
|
||||||
"php": "^7.4 || ^8.0",
|
"php": "^7.4 || ^8.0",
|
||||||
"psr/cache": "^1|^2|^3",
|
"psr/cache": "^1|^2|^3",
|
||||||
"psr/log": "^1|^2|^3"
|
"psr/log": "^1|^2|^3"
|
||||||
},
|
},
|
||||||
|
"conflict": {
|
||||||
|
"doctrine/cache": "< 1.11"
|
||||||
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
"doctrine/cache": "^1.11|^2.0",
|
||||||
"doctrine/coding-standard": "13.0.0",
|
"doctrine/coding-standard": "13.0.0",
|
||||||
"fig/log-test": "^1",
|
"fig/log-test": "^1",
|
||||||
"jetbrains/phpstorm-stubs": "2023.1",
|
"jetbrains/phpstorm-stubs": "2023.1",
|
||||||
@ -484,7 +514,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/doctrine/dbal/issues",
|
"issues": "https://github.com/doctrine/dbal/issues",
|
||||||
"source": "https://github.com/doctrine/dbal/tree/3.9.5"
|
"source": "https://github.com/doctrine/dbal/tree/3.10.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -500,7 +530,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-06-15T22:40:05+00:00"
|
"time": "2025-07-10T21:11:04+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/deprecations",
|
"name": "doctrine/deprecations",
|
||||||
@ -552,16 +582,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/doctrine-bundle",
|
"name": "doctrine/doctrine-bundle",
|
||||||
"version": "2.15.0",
|
"version": "2.15.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/doctrine/DoctrineBundle.git",
|
"url": "https://github.com/doctrine/DoctrineBundle.git",
|
||||||
"reference": "d88294521a1bca943240adca65fa19ca8a7288c6"
|
"reference": "5a305c5e776f9d3eb87f5b94d40d50aff439211d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/d88294521a1bca943240adca65fa19ca8a7288c6",
|
"url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/5a305c5e776f9d3eb87f5b94d40d50aff439211d",
|
||||||
"reference": "d88294521a1bca943240adca65fa19ca8a7288c6",
|
"reference": "5a305c5e776f9d3eb87f5b94d40d50aff439211d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -654,7 +684,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/doctrine/DoctrineBundle/issues",
|
"issues": "https://github.com/doctrine/DoctrineBundle/issues",
|
||||||
"source": "https://github.com/doctrine/DoctrineBundle/tree/2.15.0"
|
"source": "https://github.com/doctrine/DoctrineBundle/tree/2.15.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -670,7 +700,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-06-16T19:53:58+00:00"
|
"time": "2025-07-30T15:48:28+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/doctrine-migrations-bundle",
|
"name": "doctrine/doctrine-migrations-bundle",
|
||||||
@ -1088,16 +1118,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/migrations",
|
"name": "doctrine/migrations",
|
||||||
"version": "3.9.1",
|
"version": "3.9.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/doctrine/migrations.git",
|
"url": "https://github.com/doctrine/migrations.git",
|
||||||
"reference": "0f1e0c960ac29866d648a4f50142a74fe1cb6999"
|
"reference": "fa94c6f06b1bc6d4759481ec20b8b81d13e861be"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/doctrine/migrations/zipball/0f1e0c960ac29866d648a4f50142a74fe1cb6999",
|
"url": "https://api.github.com/repos/doctrine/migrations/zipball/fa94c6f06b1bc6d4759481ec20b8b81d13e861be",
|
||||||
"reference": "0f1e0c960ac29866d648a4f50142a74fe1cb6999",
|
"reference": "fa94c6f06b1bc6d4759481ec20b8b81d13e861be",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1115,18 +1145,18 @@
|
|||||||
"doctrine/orm": "<2.12 || >=4"
|
"doctrine/orm": "<2.12 || >=4"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"doctrine/coding-standard": "^12",
|
"doctrine/coding-standard": "^13",
|
||||||
"doctrine/orm": "^2.13 || ^3",
|
"doctrine/orm": "^2.13 || ^3",
|
||||||
"doctrine/persistence": "^2 || ^3 || ^4",
|
"doctrine/persistence": "^2 || ^3 || ^4",
|
||||||
"doctrine/sql-formatter": "^1.0",
|
"doctrine/sql-formatter": "^1.0",
|
||||||
"ext-pdo_sqlite": "*",
|
"ext-pdo_sqlite": "*",
|
||||||
"fig/log-test": "^1",
|
"fig/log-test": "^1",
|
||||||
"phpstan/phpstan": "^1.10",
|
"phpstan/phpstan": "^2",
|
||||||
"phpstan/phpstan-deprecation-rules": "^1.1",
|
"phpstan/phpstan-deprecation-rules": "^2",
|
||||||
"phpstan/phpstan-phpunit": "^1.3",
|
"phpstan/phpstan-phpunit": "^2",
|
||||||
"phpstan/phpstan-strict-rules": "^1.4",
|
"phpstan/phpstan-strict-rules": "^2",
|
||||||
"phpstan/phpstan-symfony": "^1.3",
|
"phpstan/phpstan-symfony": "^2",
|
||||||
"phpunit/phpunit": "^10.3",
|
"phpunit/phpunit": "^10.3 || ^11.0 || ^12.0",
|
||||||
"symfony/cache": "^5.4 || ^6.0 || ^7.0",
|
"symfony/cache": "^5.4 || ^6.0 || ^7.0",
|
||||||
"symfony/process": "^5.4 || ^6.0 || ^7.0",
|
"symfony/process": "^5.4 || ^6.0 || ^7.0",
|
||||||
"symfony/yaml": "^5.4 || ^6.0 || ^7.0"
|
"symfony/yaml": "^5.4 || ^6.0 || ^7.0"
|
||||||
@ -1171,7 +1201,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/doctrine/migrations/issues",
|
"issues": "https://github.com/doctrine/migrations/issues",
|
||||||
"source": "https://github.com/doctrine/migrations/tree/3.9.1"
|
"source": "https://github.com/doctrine/migrations/tree/3.9.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -1187,7 +1217,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-06-27T07:19:23+00:00"
|
"time": "2025-07-29T11:36:14+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/orm",
|
"name": "doctrine/orm",
|
||||||
@ -1929,16 +1959,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/commonmark",
|
"name": "league/commonmark",
|
||||||
"version": "2.7.0",
|
"version": "2.7.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/thephpleague/commonmark.git",
|
"url": "https://github.com/thephpleague/commonmark.git",
|
||||||
"reference": "6fbb36d44824ed4091adbcf4c7d4a3923cdb3405"
|
"reference": "10732241927d3971d28e7ea7b5712721fa2296ca"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/6fbb36d44824ed4091adbcf4c7d4a3923cdb3405",
|
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/10732241927d3971d28e7ea7b5712721fa2296ca",
|
||||||
"reference": "6fbb36d44824ed4091adbcf4c7d4a3923cdb3405",
|
"reference": "10732241927d3971d28e7ea7b5712721fa2296ca",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1967,7 +1997,7 @@
|
|||||||
"symfony/process": "^5.4 | ^6.0 | ^7.0",
|
"symfony/process": "^5.4 | ^6.0 | ^7.0",
|
||||||
"symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0",
|
"symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0",
|
||||||
"unleashedtech/php-coding-standard": "^3.1.1",
|
"unleashedtech/php-coding-standard": "^3.1.1",
|
||||||
"vimeo/psalm": "^4.24.0 || ^5.0.0"
|
"vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"symfony/yaml": "v2.3+ required if using the Front Matter extension"
|
"symfony/yaml": "v2.3+ required if using the Front Matter extension"
|
||||||
@ -2032,7 +2062,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-05-05T12:20:28+00:00"
|
"time": "2025-07-20T12:47:49+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/config",
|
"name": "league/config",
|
||||||
@ -4657,16 +4687,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/flex",
|
"name": "symfony/flex",
|
||||||
"version": "v2.8.0",
|
"version": "v2.8.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/flex.git",
|
"url": "https://github.com/symfony/flex.git",
|
||||||
"reference": "68cdcde0b7e36b008a08bcf3709c07a20e757a29"
|
"reference": "423c36e369361003dc31ef11c5f15fb589e52c01"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/flex/zipball/68cdcde0b7e36b008a08bcf3709c07a20e757a29",
|
"url": "https://api.github.com/repos/symfony/flex/zipball/423c36e369361003dc31ef11c5f15fb589e52c01",
|
||||||
"reference": "68cdcde0b7e36b008a08bcf3709c07a20e757a29",
|
"reference": "423c36e369361003dc31ef11c5f15fb589e52c01",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -4705,7 +4735,7 @@
|
|||||||
"description": "Composer plugin for Symfony",
|
"description": "Composer plugin for Symfony",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/symfony/flex/issues",
|
"issues": "https://github.com/symfony/flex/issues",
|
||||||
"source": "https://github.com/symfony/flex/tree/v2.8.0"
|
"source": "https://github.com/symfony/flex/tree/v2.8.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -4721,7 +4751,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-07-04T06:30:46+00:00"
|
"time": "2025-07-05T07:45:19+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/form",
|
"name": "symfony/form",
|
||||||
@ -7516,16 +7546,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/stimulus-bundle",
|
"name": "symfony/stimulus-bundle",
|
||||||
"version": "v2.27.0",
|
"version": "v2.28.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/stimulus-bundle.git",
|
"url": "https://github.com/symfony/stimulus-bundle.git",
|
||||||
"reference": "defaeb91bd366f9f43dbe54dbdfd9bc3c4138814"
|
"reference": "4ebef4b41e2524b7b797a103144256e5f7b39226"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/stimulus-bundle/zipball/defaeb91bd366f9f43dbe54dbdfd9bc3c4138814",
|
"url": "https://api.github.com/repos/symfony/stimulus-bundle/zipball/4ebef4b41e2524b7b797a103144256e5f7b39226",
|
||||||
"reference": "defaeb91bd366f9f43dbe54dbdfd9bc3c4138814",
|
"reference": "4ebef4b41e2524b7b797a103144256e5f7b39226",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -7565,7 +7595,7 @@
|
|||||||
"symfony-ux"
|
"symfony-ux"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/stimulus-bundle/tree/v2.27.0"
|
"source": "https://github.com/symfony/stimulus-bundle/tree/v2.28.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -7576,12 +7606,16 @@
|
|||||||
"url": "https://github.com/fabpot",
|
"url": "https://github.com/fabpot",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nicolas-grekas",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-06-22T19:07:55+00:00"
|
"time": "2025-07-29T15:18:27+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/stopwatch",
|
"name": "symfony/stopwatch",
|
||||||
@ -8181,16 +8215,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/ux-turbo",
|
"name": "symfony/ux-turbo",
|
||||||
"version": "v2.27.0",
|
"version": "v2.28.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/ux-turbo.git",
|
"url": "https://github.com/symfony/ux-turbo.git",
|
||||||
"reference": "b9ce9b30a9cf9bbd090c7ad290bdaf84a0e100b2"
|
"reference": "6094406d9cddc4bf2b583cef86c20edce1d534fa"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/ux-turbo/zipball/b9ce9b30a9cf9bbd090c7ad290bdaf84a0e100b2",
|
"url": "https://api.github.com/repos/symfony/ux-turbo/zipball/6094406d9cddc4bf2b583cef86c20edce1d534fa",
|
||||||
"reference": "b9ce9b30a9cf9bbd090c7ad290bdaf84a0e100b2",
|
"reference": "6094406d9cddc4bf2b583cef86c20edce1d534fa",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -8260,7 +8294,7 @@
|
|||||||
"turbo-stream"
|
"turbo-stream"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/ux-turbo/tree/v2.27.0"
|
"source": "https://github.com/symfony/ux-turbo/tree/v2.28.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -8271,12 +8305,16 @@
|
|||||||
"url": "https://github.com/fabpot",
|
"url": "https://github.com/fabpot",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nicolas-grekas",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-06-06T20:27:21+00:00"
|
"time": "2025-07-29T15:18:27+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/validator",
|
"name": "symfony/validator",
|
||||||
@ -9575,16 +9613,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "masterminds/html5",
|
"name": "masterminds/html5",
|
||||||
"version": "2.9.0",
|
"version": "2.10.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Masterminds/html5-php.git",
|
"url": "https://github.com/Masterminds/html5-php.git",
|
||||||
"reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6"
|
"reference": "fcf91eb64359852f00d921887b219479b4f21251"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
|
"url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251",
|
||||||
"reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
|
"reference": "fcf91eb64359852f00d921887b219479b4f21251",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -9636,22 +9674,22 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/Masterminds/html5-php/issues",
|
"issues": "https://github.com/Masterminds/html5-php/issues",
|
||||||
"source": "https://github.com/Masterminds/html5-php/tree/2.9.0"
|
"source": "https://github.com/Masterminds/html5-php/tree/2.10.0"
|
||||||
},
|
},
|
||||||
"time": "2024-03-31T07:05:07+00:00"
|
"time": "2025-07-25T09:04:22+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "myclabs/deep-copy",
|
"name": "myclabs/deep-copy",
|
||||||
"version": "1.13.2",
|
"version": "1.13.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||||
"reference": "d25e62e636b0a9b01e3bdebb7823b474876dd829"
|
"reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/d25e62e636b0a9b01e3bdebb7823b474876dd829",
|
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
|
||||||
"reference": "d25e62e636b0a9b01e3bdebb7823b474876dd829",
|
"reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -9690,7 +9728,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
||||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.2"
|
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -9698,20 +9736,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-07-04T14:07:32+00:00"
|
"time": "2025-08-01T08:46:24+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nikic/php-parser",
|
"name": "nikic/php-parser",
|
||||||
"version": "v5.5.0",
|
"version": "v5.6.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||||
"reference": "ae59794362fe85e051a58ad36b289443f57be7a9"
|
"reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9",
|
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/221b0d0fdf1369c71047ad1d18bb5880017bbc56",
|
||||||
"reference": "ae59794362fe85e051a58ad36b289443f57be7a9",
|
"reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -9754,9 +9792,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0"
|
"source": "https://github.com/nikic/PHP-Parser/tree/v5.6.0"
|
||||||
},
|
},
|
||||||
"time": "2025-05-31T08:24:38+00:00"
|
"time": "2025-07-27T20:03:57+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phar-io/manifest",
|
"name": "phar-io/manifest",
|
||||||
@ -9878,16 +9916,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan",
|
"name": "phpstan/phpstan",
|
||||||
"version": "2.1.17",
|
"version": "2.1.21",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan.git",
|
"url": "https://github.com/phpstan/phpstan.git",
|
||||||
"reference": "89b5ef665716fa2a52ecd2633f21007a6a349053"
|
"reference": "1ccf445757458c06a04eb3f803603cb118fe5fa6"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/89b5ef665716fa2a52ecd2633f21007a6a349053",
|
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/1ccf445757458c06a04eb3f803603cb118fe5fa6",
|
||||||
"reference": "89b5ef665716fa2a52ecd2633f21007a6a349053",
|
"reference": "1ccf445757458c06a04eb3f803603cb118fe5fa6",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -9932,20 +9970,20 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-05-21T20:55:28+00:00"
|
"time": "2025-07-28T19:35:08+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan-doctrine",
|
"name": "phpstan/phpstan-doctrine",
|
||||||
"version": "2.0.3",
|
"version": "2.0.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan-doctrine.git",
|
"url": "https://github.com/phpstan/phpstan-doctrine.git",
|
||||||
"reference": "4497663eb17b9d29211830df5aceaa3a4d256a35"
|
"reference": "6271e66ce37545bd2edcddbe6bcbdd3b665ab7b8"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/4497663eb17b9d29211830df5aceaa3a4d256a35",
|
"url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/6271e66ce37545bd2edcddbe6bcbdd3b665ab7b8",
|
||||||
"reference": "4497663eb17b9d29211830df5aceaa3a4d256a35",
|
"reference": "6271e66ce37545bd2edcddbe6bcbdd3b665ab7b8",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -10002,22 +10040,22 @@
|
|||||||
"description": "Doctrine extensions for PHPStan",
|
"description": "Doctrine extensions for PHPStan",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpstan/phpstan-doctrine/issues",
|
"issues": "https://github.com/phpstan/phpstan-doctrine/issues",
|
||||||
"source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.3"
|
"source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.4"
|
||||||
},
|
},
|
||||||
"time": "2025-05-05T15:28:52+00:00"
|
"time": "2025-07-17T11:57:55+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan-symfony",
|
"name": "phpstan/phpstan-symfony",
|
||||||
"version": "2.0.6",
|
"version": "2.0.7",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan-symfony.git",
|
"url": "https://github.com/phpstan/phpstan-symfony.git",
|
||||||
"reference": "5005288e07583546ea00b52de4a9ac412eb869d7"
|
"reference": "392f7ab8f52a0a776977be4e62535358c28e1b15"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/5005288e07583546ea00b52de4a9ac412eb869d7",
|
"url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/392f7ab8f52a0a776977be4e62535358c28e1b15",
|
||||||
"reference": "5005288e07583546ea00b52de4a9ac412eb869d7",
|
"reference": "392f7ab8f52a0a776977be4e62535358c28e1b15",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -10073,9 +10111,9 @@
|
|||||||
"description": "Symfony Framework extensions and rules for PHPStan",
|
"description": "Symfony Framework extensions and rules for PHPStan",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpstan/phpstan-symfony/issues",
|
"issues": "https://github.com/phpstan/phpstan-symfony/issues",
|
||||||
"source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.6"
|
"source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.7"
|
||||||
},
|
},
|
||||||
"time": "2025-05-14T07:00:05+00:00"
|
"time": "2025-07-22T09:40:57+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
|
@ -15,4 +15,6 @@ return [
|
|||||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||||
Oneup\UploaderBundle\OneupUploaderBundle::class => ['all' => true],
|
Oneup\UploaderBundle\OneupUploaderBundle::class => ['all' => true],
|
||||||
FOS\RestBundle\FOSRestBundle::class => ['all' => true],
|
FOS\RestBundle\FOSRestBundle::class => ['all' => true],
|
||||||
|
Bnine\FilesBundle\BnineFilesBundle::class => ['all' => true],
|
||||||
|
Bnine\MdEditorBundle\BnineMdEditorBundle::class => ['all' => true],
|
||||||
];
|
];
|
||||||
|
@ -4,5 +4,5 @@ oneup_uploader:
|
|||||||
frontend: dropzone
|
frontend: dropzone
|
||||||
logo:
|
logo:
|
||||||
frontend: dropzone
|
frontend: dropzone
|
||||||
file:
|
bninefile:
|
||||||
frontend: dropzone
|
frontend: dropzone
|
@ -3,6 +3,9 @@ twig:
|
|||||||
form_themes: ['bootstrap_5_layout.html.twig']
|
form_themes: ['bootstrap_5_layout.html.twig']
|
||||||
globals:
|
globals:
|
||||||
appName: "%appName%"
|
appName: "%appName%"
|
||||||
|
paths:
|
||||||
|
'%kernel.project_dir%/vendor/bnine/filesbundle/templates': BnineFilesBundle
|
||||||
|
'%kernel.project_dir%/vendor/bnine/mdeditorbundle/templates': BnineMdEditorBundle
|
||||||
when@test:
|
when@test:
|
||||||
twig:
|
twig:
|
||||||
strict_variables: true
|
strict_variables: true
|
||||||
|
@ -3,3 +3,11 @@ controllers:
|
|||||||
path: ../src/Controller/
|
path: ../src/Controller/
|
||||||
namespace: App\Controller
|
namespace: App\Controller
|
||||||
type: attribute
|
type: attribute
|
||||||
|
|
||||||
|
bninefilesbundle:
|
||||||
|
resource: '@BnineFilesBundle/config/routes.yaml'
|
||||||
|
prefix: '/bninefiles'
|
||||||
|
|
||||||
|
bninemdeditorbundle:
|
||||||
|
resource: '@BnineMdEditorBundle/config/routes.yaml'
|
||||||
|
prefix: '/bninemdeditor'
|
@ -29,4 +29,3 @@ services:
|
|||||||
App\Security\DynamicAuthenticator:
|
App\Security\DynamicAuthenticator:
|
||||||
arguments:
|
arguments:
|
||||||
$modeAuth: '%env(MODE_AUTH)%'
|
$modeAuth: '%env(MODE_AUTH)%'
|
||||||
|
|
||||||
|
@ -1,109 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Controller;
|
|
||||||
|
|
||||||
use App\Service\FileService;
|
|
||||||
use Oneup\UploaderBundle\Uploader\Response\ResponseInterface;
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
||||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
|
||||||
|
|
||||||
class FileController extends AbstractController
|
|
||||||
{
|
|
||||||
private FileService $fileService;
|
|
||||||
|
|
||||||
public function __construct(FileService $fileService)
|
|
||||||
{
|
|
||||||
$this->fileService = $fileService;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Route('/user/file/{domain}/{id}/{editable}', name: 'app_files', methods: ['GET'])]
|
|
||||||
public function browse(string $domain, int $id, int $editable, Request $request): Response
|
|
||||||
{
|
|
||||||
$relativePath = $request->query->get('path', '');
|
|
||||||
|
|
||||||
try {
|
|
||||||
$files = $this->fileService->list($domain, (string) $id, $relativePath);
|
|
||||||
|
|
||||||
return $this->render('file/browse.html.twig', [
|
|
||||||
'domain' => $domain,
|
|
||||||
'id' => $id,
|
|
||||||
'files' => $files,
|
|
||||||
'path' => $relativePath,
|
|
||||||
'editable' => $editable,
|
|
||||||
]);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->addFlash('danger', $e->getMessage());
|
|
||||||
dd($e->getMessage());
|
|
||||||
|
|
||||||
return $this->redirectToRoute('app_files', [
|
|
||||||
'domain' => $domain,
|
|
||||||
'id' => $id,
|
|
||||||
'editable' => $editable,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Route('/user/uploadmodal/{domain}/{id}', name: 'app_files_uploadmodal', methods: ['GET'])]
|
|
||||||
public function uploadmodal(string $domain, int $id, Request $request): Response
|
|
||||||
{
|
|
||||||
$relativePath = $request->query->get('path', '');
|
|
||||||
|
|
||||||
return $this->render('file\upload.html.twig', [
|
|
||||||
'useheader' => false,
|
|
||||||
'usemenu' => false,
|
|
||||||
'usesidebar' => false,
|
|
||||||
'endpoint' => 'file',
|
|
||||||
'domain' => $domain,
|
|
||||||
'id' => $id,
|
|
||||||
'path' => $relativePath,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Route('/user/uploadfile', name: 'app_files_uploadfile', methods: ['POST'])]
|
|
||||||
public function upload(Request $request): Response|ResponseInterface
|
|
||||||
{
|
|
||||||
/** @var UploadedFile $file */
|
|
||||||
$file = $request->files->get('file');
|
|
||||||
$domain = $request->query->get('domain');
|
|
||||||
$id = $request->query->get('id');
|
|
||||||
$relativePath = $request->query->get('path', '');
|
|
||||||
|
|
||||||
if (!$file || !$domain || !$id) {
|
|
||||||
return new Response('Invalid parameters', 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
$baseDir = $this->getParameter('kernel.project_dir').'/uploads/'.$domain.'/'.$id.'/'.ltrim($relativePath, '/');
|
|
||||||
|
|
||||||
if (!is_dir($baseDir)) {
|
|
||||||
mkdir($baseDir, 0775, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$originalName = $file->getClientOriginalName();
|
|
||||||
$file->move($baseDir, $originalName);
|
|
||||||
|
|
||||||
return new JsonResponse(['success' => true]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Route('/user/file/{domain}/{id}/delete', name: 'app_files_delete', methods: ['POST'])]
|
|
||||||
public function delete(string $domain, int $id, Request $request): JsonResponse
|
|
||||||
{
|
|
||||||
$data = json_decode($request->getContent(), true);
|
|
||||||
$relativePath = $data['path'] ?? null;
|
|
||||||
|
|
||||||
if (!$relativePath) {
|
|
||||||
return $this->json(['error' => 'Chemin non fourni.'], 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$this->fileService->delete($domain, (string) $id, $relativePath);
|
|
||||||
|
|
||||||
return $this->json(['success' => true]);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return $this->json(['error' => $e->getMessage()], 400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,7 +6,7 @@ use App\Entity\Project;
|
|||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Form\ProjectType;
|
use App\Form\ProjectType;
|
||||||
use App\Repository\ProjectRepository;
|
use App\Repository\ProjectRepository;
|
||||||
use App\Service\FileService;
|
use Bnine\FilesBundle\Service\FileService;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
@ -78,7 +78,7 @@ class ProjectController extends AbstractController
|
|||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
$em->flush();
|
$em->flush();
|
||||||
|
|
||||||
return $this->redirectToRoute('app_admin_project');
|
// return $this->redirectToRoute('app_admin_project');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render('project/edit.html.twig', [
|
return $this->render('project/edit.html.twig', [
|
||||||
|
@ -8,8 +8,18 @@ use Doctrine\Common\Collections\Collection;
|
|||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
#[ORM\Entity(repositoryClass: ProjectRepository::class)]
|
#[ORM\Entity(repositoryClass: ProjectRepository::class)]
|
||||||
|
#[ORM\HasLifecycleCallbacks]
|
||||||
class Project
|
class Project
|
||||||
{
|
{
|
||||||
|
public const DRAFT = 'Brouillon';
|
||||||
|
public const TOVOTE = 'A Voter';
|
||||||
|
public const VOTED = 'Voté';
|
||||||
|
public const ARCHIVED = 'Archivé';
|
||||||
|
|
||||||
|
public const NATURE_COLLECTIVE = 'Collective';
|
||||||
|
public const NATURE_STRATEGIC = 'Stratégique';
|
||||||
|
public const NATURE_TACTICAL = 'Tactique';
|
||||||
|
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\GeneratedValue]
|
#[ORM\GeneratedValue]
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
@ -18,11 +28,20 @@ class Project
|
|||||||
#[ORM\Column(length: 255, unique: true)]
|
#[ORM\Column(length: 255, unique: true)]
|
||||||
private ?string $title = null;
|
private ?string $title = null;
|
||||||
|
|
||||||
#[ORM\Column()]
|
#[ORM\Column(type: 'text')]
|
||||||
private int $status;
|
private string $summary;
|
||||||
|
|
||||||
|
#[ORM\Column(type: 'text', nullable: true)]
|
||||||
|
private ?string $description = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: 'string', length: 20)]
|
||||||
|
private string $nature;
|
||||||
|
|
||||||
|
#[ORM\Column]
|
||||||
|
private string $status;
|
||||||
|
|
||||||
#[ORM\Column(type: 'datetime', nullable: true)]
|
#[ORM\Column(type: 'datetime', nullable: true)]
|
||||||
private ?\DateTimeInterface $updateAt = null;
|
private ?\DateTimeInterface $dueDate = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<int, User>
|
* @var Collection<int, User>
|
||||||
@ -30,9 +49,17 @@ class Project
|
|||||||
#[ORM\ManyToMany(targetEntity: User::class, mappedBy: 'projects')]
|
#[ORM\ManyToMany(targetEntity: User::class, mappedBy: 'projects')]
|
||||||
private Collection $users;
|
private Collection $users;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Collection<int, ProjectTimeline>
|
||||||
|
*/
|
||||||
|
#[ORM\OneToMany(mappedBy: 'project', targetEntity: ProjectTimeline::class, cascade: ['remove'])]
|
||||||
|
#[ORM\OrderBy(['createdAt' => 'DESC'])]
|
||||||
|
private Collection $timelines;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->users = new ArrayCollection();
|
$this->users = new ArrayCollection();
|
||||||
|
$this->timelines = new ArrayCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
@ -52,30 +79,66 @@ class Project
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUpdateAt(): ?\DateTimeInterface
|
public function getSummary(): string
|
||||||
{
|
{
|
||||||
return $this->updateAt;
|
return $this->summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setUpdateAt(?\DateTimeInterface $updateAt): static
|
public function setSummary(string $summary): static
|
||||||
{
|
{
|
||||||
$this->updateAt = $updateAt;
|
$this->summary = $summary;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStatus(): ?int
|
public function getDescription(): ?string
|
||||||
|
{
|
||||||
|
return $this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDescription(?string $description): static
|
||||||
|
{
|
||||||
|
$this->description = $description;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNature(): string
|
||||||
|
{
|
||||||
|
return $this->nature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setNature(string $nature): static
|
||||||
|
{
|
||||||
|
$this->nature = $nature;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStatus(): string
|
||||||
{
|
{
|
||||||
return $this->status;
|
return $this->status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setStatus(int $status): static
|
public function setStatus(string $status): static
|
||||||
{
|
{
|
||||||
$this->status = $status;
|
$this->status = $status;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDueDate(): ?\DateTimeInterface
|
||||||
|
{
|
||||||
|
return $this->dueDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDueDate(?\DateTimeInterface $dueDate): static
|
||||||
|
{
|
||||||
|
$this->dueDate = $dueDate;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection<int, User>
|
* @return Collection<int, User>
|
||||||
*/
|
*/
|
||||||
@ -102,4 +165,9 @@ class Project
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getTimelines(): Collection
|
||||||
|
{
|
||||||
|
return $this->timelines;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
86
src/Entity/ProjectTimeline.php
Normal file
86
src/Entity/ProjectTimeline.php
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use App\Repository\ProjectTimelineRepository;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
#[ORM\Entity(repositoryClass: ProjectTimelineRepository::class)]
|
||||||
|
class ProjectTimeline
|
||||||
|
{
|
||||||
|
#[ORM\Id]
|
||||||
|
#[ORM\GeneratedValue]
|
||||||
|
#[ORM\Column]
|
||||||
|
private ?int $id = null;
|
||||||
|
|
||||||
|
#[ORM\ManyToOne(targetEntity: Project::class, inversedBy: 'timelines')]
|
||||||
|
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||||
|
private Project $project;
|
||||||
|
|
||||||
|
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||||
|
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||||
|
private User $user;
|
||||||
|
|
||||||
|
#[ORM\Column(type: 'datetime')]
|
||||||
|
private \DateTimeInterface $createdAt;
|
||||||
|
|
||||||
|
#[ORM\Column(type: 'array')]
|
||||||
|
private array $description;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): ?int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProject(): Project
|
||||||
|
{
|
||||||
|
return $this->project;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setProject(Project $project): static
|
||||||
|
{
|
||||||
|
$this->project = $project;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUser(): User
|
||||||
|
{
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUser(User $user): static
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreatedAt(): \DateTimeInterface
|
||||||
|
{
|
||||||
|
return $this->createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCreatedAt(\DateTimeInterface $createAt): static
|
||||||
|
{
|
||||||
|
$this->createdAt = $createAt;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription(): array
|
||||||
|
{
|
||||||
|
return $this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDescription(array $description): static
|
||||||
|
{
|
||||||
|
$this->description = $description;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
@ -105,6 +105,11 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function hasRole(string $role): bool
|
||||||
|
{
|
||||||
|
return in_array($role, $this->getRoles());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see PasswordAuthenticatedUserInterface
|
* @see PasswordAuthenticatedUserInterface
|
||||||
*/
|
*/
|
||||||
|
155
src/EventListener/ProjectListener.php
Normal file
155
src/EventListener/ProjectListener.php
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\EventListener;
|
||||||
|
|
||||||
|
use App\Entity\Project;
|
||||||
|
use App\Entity\ProjectTimeline;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
|
||||||
|
use Doctrine\Common\Collections\Collection;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\Event\OnFlushEventArgs;
|
||||||
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
|
|
||||||
|
#[AsDoctrineListener(event: 'onFlush')]
|
||||||
|
class ProjectListener
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly Security $security,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onFlush(OnFlushEventArgs $args): void
|
||||||
|
{
|
||||||
|
$em = $args->getObjectManager();
|
||||||
|
if (!$em instanceof EntityManagerInterface) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$uow = $em->getUnitOfWork();
|
||||||
|
|
||||||
|
$user = $this->security->getUser();
|
||||||
|
if (!$user) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($uow->getScheduledEntityInsertions() as $entity) {
|
||||||
|
if (!$entity instanceof Project) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timeline = new ProjectTimeline();
|
||||||
|
$timeline->setProject($entity);
|
||||||
|
$timeline->setUser($user);
|
||||||
|
$timeline->setCreatedAt(new \DateTime());
|
||||||
|
$timeline->setDescription(['created' => true]);
|
||||||
|
|
||||||
|
$em->persist($timeline);
|
||||||
|
$meta = $em->getClassMetadata(ProjectTimeline::class);
|
||||||
|
$uow->computeChangeSet($meta, $timeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($uow->getScheduledEntityUpdates() as $entity) {
|
||||||
|
if (!$entity instanceof Project) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$changeSet = $uow->getEntityChangeSet($entity);
|
||||||
|
$changes = [];
|
||||||
|
|
||||||
|
foreach ($changeSet as $field => [$old, $new]) {
|
||||||
|
$oldStr = $this->stringify($old);
|
||||||
|
$newStr = $this->stringify($new);
|
||||||
|
|
||||||
|
if ($oldStr !== $newStr) {
|
||||||
|
$changes[$field] = [
|
||||||
|
'old' => $oldStr,
|
||||||
|
'new' => $newStr,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($changes)) {
|
||||||
|
$timeline = new ProjectTimeline();
|
||||||
|
$timeline->setProject($entity);
|
||||||
|
$timeline->setUser($user);
|
||||||
|
$timeline->setCreatedAt(new \DateTime());
|
||||||
|
$timeline->setDescription($changes);
|
||||||
|
|
||||||
|
$em->persist($timeline);
|
||||||
|
$meta = $em->getClassMetadata(ProjectTimeline::class);
|
||||||
|
$uow->computeChangeSet($meta, $timeline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($uow->getScheduledCollectionUpdates() as $col) {
|
||||||
|
$owner = $col->getOwner();
|
||||||
|
|
||||||
|
if (!$owner instanceof Project) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mapping = $col->getMapping();
|
||||||
|
$fieldName = $mapping['fieldName'];
|
||||||
|
|
||||||
|
$added = $col->getInsertDiff();
|
||||||
|
$removed = $col->getDeleteDiff();
|
||||||
|
|
||||||
|
$changes = [];
|
||||||
|
|
||||||
|
foreach ($added as $addedEntity) {
|
||||||
|
$changes[$fieldName.'_added'][] = $this->stringify($addedEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($removed as $removedEntity) {
|
||||||
|
$changes[$fieldName.'_removed'][] = $this->stringify($removedEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($changes)) {
|
||||||
|
$timeline = new ProjectTimeline();
|
||||||
|
$timeline->setProject($owner);
|
||||||
|
$timeline->setUser($user);
|
||||||
|
$timeline->setCreatedAt(new \DateTime());
|
||||||
|
$timeline->setDescription($changes);
|
||||||
|
|
||||||
|
$em->persist($timeline);
|
||||||
|
$meta = $em->getClassMetadata(ProjectTimeline::class);
|
||||||
|
$uow->computeChangeSet($meta, $timeline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function stringify(mixed $value): string
|
||||||
|
{
|
||||||
|
if (is_null($value)) {
|
||||||
|
return 'null';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_scalar($value)) {
|
||||||
|
return (string) $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value instanceof Collection || is_array($value)) {
|
||||||
|
$elements = [];
|
||||||
|
foreach ($value as $item) {
|
||||||
|
$elements[] = $this->stringify($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '['.implode(', ', $elements).']';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_object($value)) {
|
||||||
|
if (method_exists($value, '__toString')) {
|
||||||
|
return (string) $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (['getUsername', 'getName', 'getTitle', 'getEmail', 'getId'] as $method) {
|
||||||
|
if (method_exists($value, $method)) {
|
||||||
|
return (string) $value->{$method}();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_class($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_encode($value);
|
||||||
|
}
|
||||||
|
}
|
@ -4,10 +4,12 @@ namespace App\Form;
|
|||||||
|
|
||||||
use App\Entity\Project;
|
use App\Entity\Project;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
|
use Bnine\MdEditorBundle\Form\Type\MarkdownType;
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
@ -26,8 +28,32 @@ class ProjectType extends AbstractType
|
|||||||
'label' => 'Titre',
|
'label' => 'Titre',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
->add('summary', TextareaType::class, [
|
||||||
|
'label' => 'Résumé',
|
||||||
|
])
|
||||||
|
|
||||||
->add('status', ChoiceType::class, [
|
->add('status', ChoiceType::class, [
|
||||||
'choices' => ['Brouillon' => 0],
|
'label' => 'Statut',
|
||||||
|
'choices' => [
|
||||||
|
Project::DRAFT => Project::DRAFT,
|
||||||
|
Project::TOVOTE => Project::TOVOTE,
|
||||||
|
Project::VOTED => Project::VOTED,
|
||||||
|
Project::ARCHIVED => Project::ARCHIVED,
|
||||||
|
],
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('nature', ChoiceType::class, [
|
||||||
|
'label' => 'Nature',
|
||||||
|
'choices' => [
|
||||||
|
Project::NATURE_COLLECTIVE => Project::NATURE_COLLECTIVE,
|
||||||
|
Project::NATURE_STRATEGIC => Project::NATURE_STRATEGIC,
|
||||||
|
Project::NATURE_TACTICAL => Project::NATURE_TACTICAL,
|
||||||
|
],
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('description', MarkdownType::class, [
|
||||||
|
'label' => 'Description du Projet',
|
||||||
|
'markdown_height' => 900,
|
||||||
])
|
])
|
||||||
|
|
||||||
->add('users', EntityType::class, [
|
->add('users', EntityType::class, [
|
||||||
|
18
src/Repository/ProjectTimelineRepository.php
Normal file
18
src/Repository/ProjectTimelineRepository.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Entity\ProjectTimeline;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends ServiceEntityRepository<ProjectTimeline>
|
||||||
|
*/
|
||||||
|
class ProjectTimelineRepository extends ServiceEntityRepository
|
||||||
|
{
|
||||||
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
{
|
||||||
|
parent::__construct($registry, ProjectTimeline::class);
|
||||||
|
}
|
||||||
|
}
|
72
src/Security/FileVoter.php
Normal file
72
src/Security/FileVoter.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Security;
|
||||||
|
|
||||||
|
use App\Entity\User;
|
||||||
|
use App\Repository\ProjectRepository;
|
||||||
|
use Bnine\FilesBundle\Security\AbstractFileVoter;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
|
||||||
|
class FileVoter extends AbstractFileVoter
|
||||||
|
{
|
||||||
|
private ProjectRepository $projectRepository;
|
||||||
|
|
||||||
|
public function __construct(ProjectRepository $projectRepository)
|
||||||
|
{
|
||||||
|
$this->projectRepository = $projectRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function canView(string $domain, $id, TokenInterface $token): bool
|
||||||
|
{
|
||||||
|
$user = $token->getUser();
|
||||||
|
if (!$user instanceof User) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function canEdit(string $domain, $id, TokenInterface $token): bool
|
||||||
|
{
|
||||||
|
$user = $token->getUser();
|
||||||
|
if (!$user instanceof User) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($user->hasRole('ROLE_ADMIN')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($domain) {
|
||||||
|
case 'project':
|
||||||
|
$project = $this->projectRepository->find($id);
|
||||||
|
if ($project && $project->getUsers()->contains($user)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function canDelete(string $domain, $id, TokenInterface $token): bool
|
||||||
|
{
|
||||||
|
$user = $token->getUser();
|
||||||
|
if (!$user instanceof User) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($user->hasRole('ROLE_ADMIN')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($domain) {
|
||||||
|
case 'project':
|
||||||
|
$project = $this->projectRepository->find($id);
|
||||||
|
if ($project && $project->getUsers()->contains($user)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
60
src/Security/ProjectVoter.php
Normal file
60
src/Security/ProjectVoter.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Security;
|
||||||
|
|
||||||
|
use App\Entity\Project;
|
||||||
|
use App\Entity\User;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
|
|
||||||
|
class ProjectVoter extends Voter
|
||||||
|
{
|
||||||
|
// Les actions que ce voter supporte
|
||||||
|
private const EDIT = 'EDIT';
|
||||||
|
private const VIEW = 'VIEW';
|
||||||
|
private const DELETE = 'DELETE';
|
||||||
|
private const MOVEDRAFT = 'MOVEDRAFT';
|
||||||
|
|
||||||
|
protected function supports(string $attribute, $subject): bool
|
||||||
|
{
|
||||||
|
$attributes = [self::EDIT, self::VIEW, self::DELETE, self::MOVEDRAFT];
|
||||||
|
|
||||||
|
return in_array($attribute, $attributes) && $subject instanceof Project;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function canView(Project $project, User $user): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function canEdit(Project $project, User $user): bool
|
||||||
|
{
|
||||||
|
return $user->hasRole('ROLE_ADMIN') || (Project::DRAFT === $project->getStatus() && $project->getUsers()->contains($user));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function canDelete(Project $project, User $user): bool
|
||||||
|
{
|
||||||
|
return $user->hasRole('ROLE_ADMIN') || (Project::DRAFT === $project->getStatus() && $project->getUsers()->contains($user));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function canMoveDraft(Project $project, User $user): bool
|
||||||
|
{
|
||||||
|
return $user->hasRole('ROLE_ADMIN') || (Project::TOVOTE === $project->getStatus() && $project->getUsers()->contains($user));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function voteOnAttribute(string $attribute, $project, TokenInterface $token): bool
|
||||||
|
{
|
||||||
|
$user = $token->getUser();
|
||||||
|
if (!$user instanceof User) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return match ($attribute) {
|
||||||
|
self::VIEW => $this->canView($project, $user),
|
||||||
|
self::EDIT => $this->canEdit($project, $user),
|
||||||
|
self::DELETE => $this->canDelete($project, $user),
|
||||||
|
self::MOVEDRAFT => $this->canMoveDraft($project, $user),
|
||||||
|
default => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,101 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Service;
|
|
||||||
|
|
||||||
use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
|
|
||||||
use Symfony\Component\Filesystem\Filesystem;
|
|
||||||
use Symfony\Component\Finder\Finder;
|
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
|
||||||
use Symfony\Component\HttpKernel\KernelInterface;
|
|
||||||
|
|
||||||
class FileService
|
|
||||||
{
|
|
||||||
private string $basePath;
|
|
||||||
private Filesystem $filesystem;
|
|
||||||
|
|
||||||
public function __construct(KernelInterface $kernel)
|
|
||||||
{
|
|
||||||
$this->filesystem = new Filesystem();
|
|
||||||
$projectDir = $kernel->getProjectDir(); // chemin racine du projet
|
|
||||||
$this->basePath = $projectDir.'/uploads';
|
|
||||||
|
|
||||||
if (!is_dir($this->basePath)) {
|
|
||||||
// On crée le dossier uploads s'il n'existe pas
|
|
||||||
try {
|
|
||||||
$this->filesystem->mkdir($this->basePath, 0775);
|
|
||||||
} catch (IOExceptionInterface $e) {
|
|
||||||
throw new \RuntimeException('Impossible de créer le dossier /uploads : '.$e->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialise un répertoire pour une entité (ex: project/123)
|
|
||||||
*/
|
|
||||||
public function init(string $domain, string $id): void
|
|
||||||
{
|
|
||||||
$entityPath = $this->getEntityPath($domain, $id);
|
|
||||||
if (!is_dir($entityPath)) {
|
|
||||||
try {
|
|
||||||
$this->filesystem->mkdir($entityPath, 0775);
|
|
||||||
} catch (IOExceptionInterface $e) {
|
|
||||||
throw new \RuntimeException(sprintf('Impossible de créer le répertoire pour %s/%s : %s', $domain, $id, $e->getMessage()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Liste les fichiers d’un répertoire lié à une entité (ex: project/123)
|
|
||||||
*/
|
|
||||||
public function list(string $domain, string $id, string $relativePath = ''): array
|
|
||||||
{
|
|
||||||
$targetPath = $this->getEntityPath($domain, $id).'/'.ltrim($relativePath, '/');
|
|
||||||
$realPath = realpath($targetPath);
|
|
||||||
|
|
||||||
$baseEntityPath = $this->getEntityPath($domain, $id);
|
|
||||||
if (!$realPath || !str_starts_with($realPath, $baseEntityPath)) {
|
|
||||||
throw new NotFoundHttpException('Répertoire non autorisé ou inexistant.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$finder = new Finder();
|
|
||||||
$finder->depth('== 0')->in($realPath);
|
|
||||||
|
|
||||||
$results = [];
|
|
||||||
foreach ($finder as $file) {
|
|
||||||
$results[] = [
|
|
||||||
'name' => $file->getFilename(),
|
|
||||||
'isDirectory' => $file->isDir(),
|
|
||||||
'path' => ltrim(str_replace($baseEntityPath, '', $file->getRealPath()), '/'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $results;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Supprime un fichier ou dossier (de façon sécurisée)
|
|
||||||
*/
|
|
||||||
public function delete(string $domain, string $id, string $relativePath): void
|
|
||||||
{
|
|
||||||
$baseEntityPath = $this->getEntityPath($domain, $id);
|
|
||||||
$targetPath = realpath($baseEntityPath.'/'.ltrim($relativePath, '/'));
|
|
||||||
|
|
||||||
if (!$targetPath || !str_starts_with($targetPath, $baseEntityPath)) {
|
|
||||||
throw new NotFoundHttpException('Fichier ou dossier non autorisé.');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$this->filesystem->remove($targetPath);
|
|
||||||
} catch (IOExceptionInterface $e) {
|
|
||||||
throw new \RuntimeException('Erreur lors de la suppression : '.$e->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construit le chemin absolu d’un domaine/id
|
|
||||||
*/
|
|
||||||
private function getEntityPath(string $domain, string $id): string
|
|
||||||
{
|
|
||||||
return $this->basePath.'/'.$domain.'/'.$id;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,13 @@
|
|||||||
{
|
{
|
||||||
|
"bnine/files-bundle": {
|
||||||
|
"version": "v1.0.1"
|
||||||
|
},
|
||||||
|
"bnine/filesbundle": {
|
||||||
|
"version": "v1.0.4"
|
||||||
|
},
|
||||||
|
"bnine/mdeditorbundle": {
|
||||||
|
"version": "v1.1.1"
|
||||||
|
},
|
||||||
"doctrine/deprecations": {
|
"doctrine/deprecations": {
|
||||||
"version": "1.1",
|
"version": "1.1",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
<script src="{{ asset('lib/jquery/jquery.min.js') }}"></script>
|
<script src="{{ asset('lib/jquery/jquery.min.js') }}"></script>
|
||||||
<script src="{{ asset('lib/bootstrap/js/bootstrap.bundle.min.js') }}"></script>
|
<script src="{{ asset('lib/bootstrap/js/bootstrap.bundle.min.js') }}"></script>
|
||||||
<script src="{{ asset('lib/fontawesome/fontawesome-free.index.js') }}"></script>
|
<!-- <script src="{{ asset('lib/fontawesome/fontawesome-free.index.js') }}"></script> -->
|
||||||
<script src="{{ asset('lib/datatables/datatables.min.js') }}"></script>
|
<script src="{{ asset('lib/datatables/datatables.min.js') }}"></script>
|
||||||
<script src="{{ asset('lib/datatables/datatables.init.js') }}"></script>
|
<script src="{{ asset('lib/datatables/datatables.init.js') }}"></script>
|
||||||
<script src="{{ asset('lib/select2/select2.min.js') }}"></script>
|
<script src="{{ asset('lib/select2/select2.min.js') }}"></script>
|
||||||
@ -30,6 +30,7 @@
|
|||||||
<script src="{{ asset('lib/jqueryui/jquery-ui.min.js') }}"></script>
|
<script src="{{ asset('lib/jqueryui/jquery-ui.min.js') }}"></script>
|
||||||
<script src="{{ asset('lib/chart/chart.js') }}"></script>
|
<script src="{{ asset('lib/chart/chart.js') }}"></script>
|
||||||
|
|
||||||
|
<script src="{{ asset('bundles/bninemdeditor/bninemdeditor.js') }}"></script>
|
||||||
<script src="{{ asset('lib/app/app.js') }}"></script>
|
<script src="{{ asset('lib/app/app.js') }}"></script>
|
||||||
|
|
||||||
{% block javascripts %}
|
{% block javascripts %}
|
||||||
|
@ -1,124 +0,0 @@
|
|||||||
<div id="file-browser-{{ domain }}-{{ id|e('html_attr') }}"
|
|
||||||
class="file-browser"
|
|
||||||
data-domain="{{ domain }}"
|
|
||||||
data-id="{{ id }}"
|
|
||||||
data-base-path="{{ path('app_files', { domain: domain, id: id, editable: editable }) }}"
|
|
||||||
data-current-path="{{ path }}">
|
|
||||||
|
|
||||||
<div class="card mt-3">
|
|
||||||
<div class="card-header">Fichiers</div>
|
|
||||||
<div class="card-body">
|
|
||||||
|
|
||||||
{% if editable %}
|
|
||||||
<div class="mb-3">
|
|
||||||
<a class="btn btn-info" style="max-width:100%; margin-bottom:15px;" data-bs-toggle="modal" data-bs-target="#mymodal" onClick="ModalLoad('mymodal','Upload','{{ path('app_files_uploadmodal',{domain:domain, id:id,path:path}) }}');" title='Upload'>Upload</a>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<p><strong>Chemin :</strong> {{ path ?: '/' }}</p>
|
|
||||||
|
|
||||||
{% set parentPath = path|split('/')|slice(0, -1)|join('/') %}
|
|
||||||
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
{% if path %}
|
|
||||||
<li><a href="#" class="file-nav" data-path="{{ parentPath }}">⬅️ ..</a></li>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% for file in files %}
|
|
||||||
<li>
|
|
||||||
{% if file.isDirectory %}
|
|
||||||
📁 <a href="#" class="file-nav" data-path="{{ path ? path ~ '/' ~ file.name : file.name }}">{{ file.name }}/</a>
|
|
||||||
{% if editable %}
|
|
||||||
<button class="btn btn-sm btn-danger btn-delete ms-2" data-path="{{ file.path }}">🗑️</button>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
📄 {{ file.name }}
|
|
||||||
{% if editable %}
|
|
||||||
<button class="btn btn-sm btn-danger btn-delete ms-2" data-path="{{ file.path }}">🗑️</button>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
|
||||||
{% else %}
|
|
||||||
<li><em>Dossier vide</em></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
$(function () {
|
|
||||||
function refreshContainer(containerId, path) {
|
|
||||||
const $oldContainer = $('#' + containerId);
|
|
||||||
const base = $oldContainer.data('base-path');
|
|
||||||
|
|
||||||
$.get(base, { path: path }, function (html) {
|
|
||||||
console.log(html);
|
|
||||||
const $doc = $('<div>').html(html);
|
|
||||||
const $newContainer = $doc.find('#' + containerId);
|
|
||||||
console.log(containerId);
|
|
||||||
if ($newContainer.length) {
|
|
||||||
console.log("HHHHHHHHHHHHHHHHHHHH");
|
|
||||||
$oldContainer.replaceWith($newContainer);
|
|
||||||
initFileBrowser($newContainer); // rebind events
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function initFileBrowser($container) {
|
|
||||||
const containerId = $container.attr('id');
|
|
||||||
|
|
||||||
// Clear any previous bindings (important!)
|
|
||||||
$container.off('click');
|
|
||||||
|
|
||||||
// Navigation dossier
|
|
||||||
$container.on('click', '.file-nav', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const path = $(this).data('path');
|
|
||||||
refreshContainer(containerId, path);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Suppression fichier ou dossier
|
|
||||||
$container.on('click', '.btn-delete', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!confirm('Supprimer ce fichier ?')) return;
|
|
||||||
|
|
||||||
const pathToDelete = $(this).data('path');
|
|
||||||
const currentPath = $container.data('current-path');
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: '/user/file/' + $container.data('domain') + '/' + $container.data('id') + '/delete',
|
|
||||||
method: 'POST',
|
|
||||||
contentType: 'application/json',
|
|
||||||
data: JSON.stringify({ path: pathToDelete }),
|
|
||||||
success: function (res) {
|
|
||||||
if (res.success) {
|
|
||||||
refreshContainer(containerId, currentPath);
|
|
||||||
} else {
|
|
||||||
alert('Erreur : ' + res.error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function (xhr) {
|
|
||||||
alert('Erreur lors de la suppression : ' + xhr.responseText);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init navigateur fichiers
|
|
||||||
const containerId = 'file-browser-{{ domain }}-{{ id|e('html_attr') }}';
|
|
||||||
const $browser = $('#' + containerId);
|
|
||||||
initFileBrowser($browser);
|
|
||||||
|
|
||||||
// Rafraîchir après fermeture modale
|
|
||||||
$('#mymodal').on('hidden.bs.modal', function () {
|
|
||||||
const $browser = $('#' + containerId);
|
|
||||||
const currentPath = $browser.data('current-path') || '';
|
|
||||||
refreshContainer(containerId, currentPath);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
@ -1,45 +0,0 @@
|
|||||||
{% extends 'base.html.twig' %}
|
|
||||||
|
|
||||||
{% block localstyle %}
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<a class="btn btn-secondary" onClick="closeModal();">Annuler</a>
|
|
||||||
<form action="{{ path('app_files_uploadfile', {
|
|
||||||
domain: domain,
|
|
||||||
id: id,
|
|
||||||
path: path
|
|
||||||
}) }}"
|
|
||||||
class="dropzone" id="myDropzone" style="margin-top:10px"></form>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block localscript %}
|
|
||||||
<script>
|
|
||||||
Dropzone.options.myDropzone = {
|
|
||||||
paramName: "{{endpoint}}",
|
|
||||||
maxFilesize: 20, // MB
|
|
||||||
parallelUploads: 5,
|
|
||||||
uploadMultiple: false,
|
|
||||||
dictDefaultMessage: "Déposez vos fichiers ici pour les téléverser",
|
|
||||||
successmultiple: function (files, response) {
|
|
||||||
console.log("multi uploaded", files);
|
|
||||||
},
|
|
||||||
queuecomplete: function () {
|
|
||||||
// Quand tous les fichiers sont uploadés, on ferme la modale et rafraîchit le navigateur
|
|
||||||
window.parent.$("#mymodal").modal('hide');
|
|
||||||
if (typeof window.parent.refreshFileBrowser === 'function') {
|
|
||||||
window.parent.refreshFileBrowser(); // à définir côté parent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function closeModal() {
|
|
||||||
window.parent.$("#mymodal").modal('hide');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
@ -1,12 +1,25 @@
|
|||||||
{% extends 'base.html.twig' %}
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{%block body%}
|
{%block body%}
|
||||||
<h2>Projets</h2>
|
<h2>Projets</h2>
|
||||||
|
<div class='d-flex' style='justify-content: left'>
|
||||||
<div class='d-flex' style='justify-content: center'>
|
|
||||||
{% for project in projects %}
|
{% for project in projects %}
|
||||||
<div class='card'>
|
<div class='card' style='width:300px'>
|
||||||
<h5>{{project.title}}</h5>
|
<div class='card-header d-flex justify-content-between align-items-center'>
|
||||||
{{ render(path("app_files",{domain:'project',id:project.id, editable:0})) }}
|
{{project.title}}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{% if is_granted('EDIT', project) %}
|
||||||
|
<button type="button" class="btn btn-primary btn-sm"><i class="fas fa-pencil"></i></button>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm"><i class="fas fa-eye"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class='card-body'>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
@ -14,31 +14,40 @@
|
|||||||
{% include('include/error.html.twig') %}
|
{% include('include/error.html.twig') %}
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 mx-auto">
|
<div class="col-md-4 mx-auto">
|
||||||
<div class="card mt-3">
|
<div class="card mt-3">
|
||||||
<div class="card-header">Information</div>
|
<div class="card-header">Information</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
{{ form_row(form.title) }}
|
{{ form_row(form.title) }}
|
||||||
|
{{ form_row(form.nature) }}
|
||||||
{{ form_row(form.status) }}
|
{{ form_row(form.status) }}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-6 mx-auto">
|
|
||||||
<div class="card mt-3">
|
|
||||||
<div class="card-header">Permissions</div>
|
|
||||||
<div class="card-body">
|
|
||||||
{{ form_row(form.users) }}
|
{{ form_row(form.users) }}
|
||||||
</div>
|
{{ form_row(form.summary) }}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if mode=="update" %}
|
{{ render(path("bninefiles_files",{domain:'project',id:project.id, editable:1})) }}
|
||||||
{{ render(path("app_files",{domain:'project',id:project.id, editable:1})) }}
|
|
||||||
{% endif %}
|
<div class="card mt-3">
|
||||||
|
<div class="card-header">Timeline</div>
|
||||||
|
<div class="card-body">
|
||||||
|
{% include('project/timeline.html.twig') %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-8 mx-auto">
|
||||||
|
<div class="card mt-3">
|
||||||
|
<div class="card-header">Description du Projet</div>
|
||||||
|
<div class="card-body">
|
||||||
|
{{ form_widget(form.description) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{{ form_end(form) }}
|
{{ form_end(form) }}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block localscript %}
|
{% block localscript %}
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
<a href="{{ path(routeupdate,{id:project.id}) }}" class="me-2"><i class="fas fa-file fa-2x"></i></a>
|
<a href="{{ path(routeupdate,{id:project.id}) }}" class="me-2"><i class="fas fa-file fa-2x"></i></a>
|
||||||
</td>
|
</td>
|
||||||
<td>{{project.title}}</td>
|
<td>{{project.title}}</td>
|
||||||
<td>{{project.updateAt}}</td>
|
<td>{{project.timelines.first ? project.timelines.first.createdAt|date('d/m/Y H:i') : '' }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
57
templates/project/timeline.html.twig
Normal file
57
templates/project/timeline.html.twig
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<div class="timeline">
|
||||||
|
{% for event in project.timelines %}
|
||||||
|
<div class="timeline-item">
|
||||||
|
<div class="timeline-marker"></div>
|
||||||
|
<div class="timeline-content">
|
||||||
|
<p class="text-muted small mb-1">
|
||||||
|
{{ event.createdAt|date('d/m/Y H:i') }} par <strong>{{ event.user.username }}</strong><br>
|
||||||
|
{% for field, change in event.description %}
|
||||||
|
<strong>{{ field }}</strong>
|
||||||
|
{% if field=="status" %}
|
||||||
|
<span class="text-danger">{{ change.old ?? '' }}</span>
|
||||||
|
→
|
||||||
|
<span class="text-success">{{ change.new ?? '' }}</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if not change.old is defined %}
|
||||||
|
= <span>{{ change[0] }}</span>
|
||||||
|
{% endif %}
|
||||||
|
<br>
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.timeline {
|
||||||
|
border-left: 3px solid #dee2e6;
|
||||||
|
padding-left: 2rem;
|
||||||
|
position: relative;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.timeline-item {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-left: -23px;
|
||||||
|
}
|
||||||
|
.timeline-content {
|
||||||
|
line-height:14px;
|
||||||
|
}
|
||||||
|
.timeline-marker {
|
||||||
|
position: absolute;
|
||||||
|
left: -1.1rem;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #0d6efd;
|
||||||
|
border: 2px solid #fff;
|
||||||
|
box-shadow: 0 0 0 3px #0d6efd44;
|
||||||
|
}
|
||||||
|
.timeline-content {
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
Reference in New Issue
Block a user