Compare commits
16 Commits
2e99457e16
...
f2166b604e
| Author | SHA1 | Date |
|---|---|---|
|
|
f2166b604e | |
|
|
8d92d3f9fc | |
|
|
5ceed1f2f2 | |
|
|
450543fab7 | |
|
|
d543e69863 | |
|
|
7021b28163 | |
|
|
c55e9fa039 | |
|
|
bdf9f0478e | |
|
|
1053a2ab22 | |
|
|
6efbeb0fa2 | |
|
|
1ee9a0110b | |
|
|
cbdb47fb17 | |
|
|
e6c8d5a462 | |
|
|
7e272b2b2f | |
|
|
6670fbc8b8 | |
|
|
1e8d5e1eaf |
5
.env
5
.env
|
|
@ -62,3 +62,8 @@ MERCURE_PUBLIC_URL=https://example.com/.well-known/mercure
|
||||||
# The secret used to sign the JWTs
|
# The secret used to sign the JWTs
|
||||||
MERCURE_JWT_SECRET="!ChangeThisMercureHubJWTSecretKey!"
|
MERCURE_JWT_SECRET="!ChangeThisMercureHubJWTSecretKey!"
|
||||||
###< symfony/mercure-bundle ###
|
###< symfony/mercure-bundle ###
|
||||||
|
|
||||||
|
###> aws/aws-sdk-php-symfony ###
|
||||||
|
AWS_KEY=not-a-real-key
|
||||||
|
AWS_SECRET=@@not-a-real-secret
|
||||||
|
###< aws/aws-sdk-php-symfony ###
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
/.env.*.local
|
/.env.*.local
|
||||||
/config/secrets/prod/prod.decrypt.private.php
|
/config/secrets/prod/prod.decrypt.private.php
|
||||||
/public/bundles/
|
/public/bundles/
|
||||||
|
/public/uploads/
|
||||||
/var/
|
/var/
|
||||||
/vendor/
|
/vendor/
|
||||||
###< symfony/framework-bundle ###
|
###< symfony/framework-bundle ###
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,16 @@
|
||||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/mercure" />
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/mercure" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/mercure-bundle" />
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/mercure-bundle" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/vendor/firebase/php-jwt" />
|
<excludeFolder url="file://$MODULE_DIR$/vendor/firebase/php-jwt" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/aws/aws-crt-php" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/aws/aws-sdk-php" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/aws/aws-sdk-php-symfony" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/guzzle" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/promises" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/knplabs/knp-time-bundle" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/mtdowling/jmespath.php" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-client" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/ralouphie/getallheaders" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,6 @@
|
||||||
<path value="$PROJECT_DIR$/vendor/lcobucci/jwt" />
|
<path value="$PROJECT_DIR$/vendor/lcobucci/jwt" />
|
||||||
<path value="$PROJECT_DIR$/vendor/lcobucci/clock" />
|
<path value="$PROJECT_DIR$/vendor/lcobucci/clock" />
|
||||||
<path value="$PROJECT_DIR$/vendor/twig/twig" />
|
<path value="$PROJECT_DIR$/vendor/twig/twig" />
|
||||||
<path value="$PROJECT_DIR$/vendor/twig/extra-bundle" />
|
|
||||||
<path value="$PROJECT_DIR$/vendor/composer" />
|
<path value="$PROJECT_DIR$/vendor/composer" />
|
||||||
<path value="$PROJECT_DIR$/vendor/theseer/tokenizer" />
|
<path value="$PROJECT_DIR$/vendor/theseer/tokenizer" />
|
||||||
<path value="$PROJECT_DIR$/vendor/symfony/deprecation-contracts" />
|
<path value="$PROJECT_DIR$/vendor/symfony/deprecation-contracts" />
|
||||||
|
|
@ -163,6 +162,17 @@
|
||||||
<path value="$PROJECT_DIR$/vendor/symfony/mercure" />
|
<path value="$PROJECT_DIR$/vendor/symfony/mercure" />
|
||||||
<path value="$PROJECT_DIR$/vendor/symfony/mercure-bundle" />
|
<path value="$PROJECT_DIR$/vendor/symfony/mercure-bundle" />
|
||||||
<path value="$PROJECT_DIR$/vendor/firebase/php-jwt" />
|
<path value="$PROJECT_DIR$/vendor/firebase/php-jwt" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/doctrine/cache" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/knplabs/knp-time-bundle" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/aws/aws-sdk-php" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/aws/aws-crt-php" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/ralouphie/getallheaders" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/aws/aws-sdk-php-symfony" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/guzzlehttp/promises" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/guzzlehttp/guzzle" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/mtdowling/jmespath.php" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/psr/http-client" />
|
||||||
</include_path>
|
</include_path>
|
||||||
</component>
|
</component>
|
||||||
<component name="PhpProjectSharedConfiguration" php_language_level="8.2" />
|
<component name="PhpProjectSharedConfiguration" php_language_level="8.2" />
|
||||||
|
|
|
||||||
24
README.MD
24
README.MD
|
|
@ -6,20 +6,20 @@
|
||||||
- Stimulus
|
- Stimulus
|
||||||
- Turbo
|
- Turbo
|
||||||
- Bootstrap 5.3
|
- Bootstrap 5.3
|
||||||
- Symfony UX toogle password (https://ux.symfony.com/toggle-password)
|
|
||||||
- Les icones sont gérées via symfony UX (https://ux.symfony.com/icons)
|
|
||||||
- Les icones sont prises en prioritées dans la bibliothèque bootstrap
|
|
||||||
- Les icones n'éxistants pas dans cette bibliothèques seront prises en priorité dans fontawesome regular (pour une cohérence visuelle)
|
|
||||||
- Sinon privilégier la bibliothèque ayant le visuel le plus proche
|
|
||||||
|
|
||||||
### Version 0.1 : (17/03/2025)
|
|
||||||
- Contient la logique de login mot de passe avec une entité user (email et password seuelement)
|
|
||||||
- Une base de template twig public est gérée pour les page n'ayant pas besoin de menu
|
|
||||||
- La page de login est designé
|
|
||||||
- Une base de template est gérée pour toutes les pages de l'application aya,t besoin de l'entête et du menu général
|
|
||||||
- Une ébauche de page d'accueil est en cours
|
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
#### Database
|
||||||
|
```bash
|
||||||
|
php bin/console doctrine:database:create
|
||||||
|
php bin/console doctrine:schema:update --force
|
||||||
|
```
|
||||||
|
#### SQL
|
||||||
|
```bash
|
||||||
|
insert into public.roles (id, name, created_at)
|
||||||
|
values (3, 'USER', '2025-05-21 13:22:52'),
|
||||||
|
(2, 'ADMIN', '2025-05-21 13:22:52'),
|
||||||
|
(1, 'SUPER ADMIN', '2025-05-21 13:22:52');
|
||||||
|
```
|
||||||
#### Choices.js
|
#### Choices.js
|
||||||
```bash
|
```bash
|
||||||
php bin/console importmap:require choices.js
|
php bin/console importmap:require choices.js
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
|
|
@ -6,6 +6,8 @@
|
||||||
--delete : #E42E31;
|
--delete : #E42E31;
|
||||||
--disable : #A3A3A3;
|
--disable : #A3A3A3;
|
||||||
--check : #80F20E;
|
--check : #80F20E;
|
||||||
|
--secondary : #cc664c;
|
||||||
|
--secondary-dark : #a5543d;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
|
|
@ -114,3 +116,16 @@ body {
|
||||||
.color-primary-dark{
|
.color-primary-dark{
|
||||||
color: var(--primary-blue-dark);
|
color: var(--primary-blue-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-secondary{
|
||||||
|
background: var(--secondary);
|
||||||
|
color : #FFFFFF;
|
||||||
|
border: var(--secondary);
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover{
|
||||||
|
background: var(--secondary-dark);
|
||||||
|
color : #FFFFFF;
|
||||||
|
border: var(--secondary);
|
||||||
|
}
|
||||||
|
|
@ -8,11 +8,13 @@
|
||||||
"ext-ctype": "*",
|
"ext-ctype": "*",
|
||||||
"ext-iconv": "*",
|
"ext-iconv": "*",
|
||||||
"ext-openssl": "*",
|
"ext-openssl": "*",
|
||||||
|
"aws/aws-sdk-php-symfony": "^2.8",
|
||||||
"doctrine/dbal": "^3",
|
"doctrine/dbal": "^3",
|
||||||
"doctrine/doctrine-bundle": "^2.14",
|
"doctrine/doctrine-bundle": "^2.14",
|
||||||
"doctrine/doctrine-migrations-bundle": "^3.4",
|
"doctrine/doctrine-migrations-bundle": "^3.4",
|
||||||
"doctrine/orm": "^3.3",
|
"doctrine/orm": "^3.3",
|
||||||
"firebase/php-jwt": "^6.11",
|
"firebase/php-jwt": "^6.11",
|
||||||
|
"knplabs/knp-time-bundle": "^2.4",
|
||||||
"league/oauth2-server-bundle": "^0.11.0",
|
"league/oauth2-server-bundle": "^0.11.0",
|
||||||
"nelmio/cors-bundle": "^2.5",
|
"nelmio/cors-bundle": "^2.5",
|
||||||
"phpdocumentor/reflection-docblock": "^5.6",
|
"phpdocumentor/reflection-docblock": "^5.6",
|
||||||
|
|
@ -49,7 +51,6 @@
|
||||||
"symfony/validator": "7.2.*",
|
"symfony/validator": "7.2.*",
|
||||||
"symfony/web-link": "7.2.*",
|
"symfony/web-link": "7.2.*",
|
||||||
"symfony/yaml": "7.2.*",
|
"symfony/yaml": "7.2.*",
|
||||||
"twig/extra-bundle": "^2.12|^3.0",
|
|
||||||
"twig/twig": "^2.12|^3.0"
|
"twig/twig": "^2.12|^3.0"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -9,7 +9,6 @@ return [
|
||||||
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
||||||
Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true],
|
Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true],
|
||||||
Symfony\UX\Turbo\TurboBundle::class => ['all' => true],
|
Symfony\UX\Turbo\TurboBundle::class => ['all' => true],
|
||||||
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
|
|
||||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
||||||
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
|
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
|
||||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||||
|
|
@ -18,4 +17,6 @@ return [
|
||||||
League\Bundle\OAuth2ServerBundle\LeagueOAuth2ServerBundle::class => ['all' => true],
|
League\Bundle\OAuth2ServerBundle\LeagueOAuth2ServerBundle::class => ['all' => true],
|
||||||
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
|
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
|
||||||
Symfony\Bundle\MercureBundle\MercureBundle::class => ['all' => true],
|
Symfony\Bundle\MercureBundle\MercureBundle::class => ['all' => true],
|
||||||
|
Knp\Bundle\TimeBundle\KnpTimeBundle::class => ['all' => true],
|
||||||
|
Aws\Symfony\AwsBundle::class => ['all' => true],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
aws:
|
||||||
|
version: latest
|
||||||
|
region: "%env(AWS_REGION)%"
|
||||||
|
credentials:
|
||||||
|
key: "%env(AWS_KEY)%"
|
||||||
|
secret: "%env(AWS_SECRET)%"
|
||||||
|
S3:
|
||||||
|
region: "%env(AWS_REGION)%"
|
||||||
|
endpoint: "%env(AWS_ENDPOINT)%"
|
||||||
|
use_path_style_endpoint: true
|
||||||
|
signature_version: 'v4'
|
||||||
|
|
@ -4,6 +4,9 @@
|
||||||
# Put parameters here that don't need to change on each machine where the app is deployed
|
# Put parameters here that don't need to change on each machine where the app is deployed
|
||||||
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
|
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
|
||||||
parameters:
|
parameters:
|
||||||
|
aws_url: '%env(AWS_ENDPOINT)%'
|
||||||
|
aws_public_url: '%env(AWS_ENDPOINT)%'
|
||||||
|
logos_directory: '%kernel.project_dir%/public/uploads/logos'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# default configuration for services in *this* file
|
# default configuration for services in *this* file
|
||||||
|
|
@ -22,6 +25,9 @@ services:
|
||||||
App\EventSubscriber\:
|
App\EventSubscriber\:
|
||||||
resource: '../src/EventSubscriber/'
|
resource: '../src/EventSubscriber/'
|
||||||
tags: ['kernel.event_subscriber']
|
tags: ['kernel.event_subscriber']
|
||||||
|
App\Service\AwsService:
|
||||||
|
arguments:
|
||||||
|
$awsPublicUrl: '%aws_public_url%'
|
||||||
App\EventSubscriber\ScopeResolveListener:
|
App\EventSubscriber\ScopeResolveListener:
|
||||||
tags:
|
tags:
|
||||||
- { name: kernel.event_listener, event: league.oauth2_server.event.scope_resolve, method: onScopeResolve }
|
- { name: kernel.event_listener, event: league.oauth2_server.event.scope_resolve, method: onScopeResolve }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20250804084150 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('ALTER TABLE actions ADD organization_id INT DEFAULT NULL');
|
||||||
|
$this->addSql('ALTER TABLE actions ADD CONSTRAINT FK_548F1EF32C8A3DE FOREIGN KEY (organization_id) REFERENCES organizations (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||||
|
$this->addSql('CREATE INDEX IDX_548F1EF32C8A3DE ON actions (organization_id)');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('CREATE SCHEMA public');
|
||||||
|
$this->addSql('ALTER TABLE actions DROP CONSTRAINT FK_548F1EF32C8A3DE');
|
||||||
|
$this->addSql('DROP INDEX IDX_548F1EF32C8A3DE');
|
||||||
|
$this->addSql('ALTER TABLE actions DROP organization_id');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20250804085615 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('DROP INDEX uniq_548f1ef67b3b43d');
|
||||||
|
$this->addSql('CREATE INDEX IDX_548F1EF67B3B43D ON actions (users_id)');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('CREATE SCHEMA public');
|
||||||
|
$this->addSql('DROP INDEX IDX_548F1EF67B3B43D');
|
||||||
|
$this->addSql('CREATE UNIQUE INDEX uniq_548f1ef67b3b43d ON actions (users_id)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20250804101742 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('ALTER TABLE actions ADD description VARCHAR(255) DEFAULT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('CREATE SCHEMA public');
|
||||||
|
$this->addSql('ALTER TABLE actions DROP description');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20250804121445 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('ALTER TABLE apps ADD description_small VARCHAR(255) DEFAULT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('CREATE SCHEMA public');
|
||||||
|
$this->addSql('ALTER TABLE apps DROP description_small');
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
|
|
@ -5,16 +5,19 @@ namespace App\Controller;
|
||||||
use App\Entity\Apps;
|
use App\Entity\Apps;
|
||||||
use App\Entity\Roles;
|
use App\Entity\Roles;
|
||||||
use App\Entity\UsersOrganizations;
|
use App\Entity\UsersOrganizations;
|
||||||
|
use App\Form\OrganizationForm;
|
||||||
|
use App\Service\ActionService;
|
||||||
use App\Service\OrganizationsService;
|
use App\Service\OrganizationsService;
|
||||||
use App\Service\UserOrganizationService;
|
use App\Service\UserOrganizationService;
|
||||||
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\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
use App\Entity\Organizations;
|
use App\Entity\Organizations;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Contracts\Service\Attribute\Required;
|
||||||
|
|
||||||
#[Route(path: '/organization', name: 'organization_')]
|
#[Route(path: '/organization', name: 'organization_')]
|
||||||
|
|
||||||
class OrganizationController extends AbstractController
|
class OrganizationController extends AbstractController
|
||||||
{
|
{
|
||||||
private const NOT_FOUND = 'Entity not found';
|
private const NOT_FOUND = 'Entity not found';
|
||||||
|
|
@ -26,13 +29,12 @@ class OrganizationController extends AbstractController
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Route(path: '/' , name: 'index', methods: ['GET'])]
|
#[Route('/', name: 'index', methods: ['GET'])]
|
||||||
public function index():Response
|
public function index(): Response
|
||||||
{
|
|
||||||
if($this->isGranted('ROLE_SUPER_ADMIN'))
|
|
||||||
{
|
{
|
||||||
|
if ($this->isGranted('ROLE_SUPER_ADMIN')) {
|
||||||
$organizations = $this->entityManager->getRepository(Organizations::class)->findBy(['isActive' => true]);
|
$organizations = $this->entityManager->getRepository(Organizations::class)->findBy(['isActive' => true]);
|
||||||
} else{
|
} else {
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
if (!$user) {
|
if (!$user) {
|
||||||
return $this->redirectToRoute('app_login');
|
return $this->redirectToRoute('app_login');
|
||||||
|
|
@ -40,7 +42,7 @@ class OrganizationController extends AbstractController
|
||||||
$userIdentifier = $user->getUserIdentifier();
|
$userIdentifier = $user->getUserIdentifier();
|
||||||
|
|
||||||
$organizations = $this->entityManager->getRepository(UsersOrganizations::class)->findOrganizationsByUserEmailAndRoleName($userIdentifier, 'ADMIN');
|
$organizations = $this->entityManager->getRepository(UsersOrganizations::class)->findOrganizationsByUserEmailAndRoleName($userIdentifier, 'ADMIN');
|
||||||
if(!$organizations) {
|
if (!$organizations) {
|
||||||
// if user is not admin in any organization, throw access denied
|
// if user is not admin in any organization, throw access denied
|
||||||
throw $this->createNotFoundException(self::ACCESS_DENIED);
|
throw $this->createNotFoundException(self::ACCESS_DENIED);
|
||||||
}
|
}
|
||||||
|
|
@ -51,23 +53,52 @@ class OrganizationController extends AbstractController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Route(path: '/{id}', name: 'show', methods: ['GET'])]
|
#[Route('/new', name: 'new', methods: ['GET', 'POST'])]
|
||||||
public function show(int $id): Response
|
public function new(Request $request): Response
|
||||||
|
{
|
||||||
|
if (!$this->isGranted('ROLE_SUPER_ADMIN')) {
|
||||||
|
throw $this->createNotFoundException(self::ACCESS_DENIED);
|
||||||
|
}
|
||||||
|
$form = $this->createForm(OrganizationForm::class);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
$organization = $form->getData();
|
||||||
|
// dd($form);
|
||||||
|
$logoFile = $form->get('logoUrl')->getData();
|
||||||
|
|
||||||
|
if ($logoFile) {
|
||||||
|
$currentDate = (new \DateTime())->format('Y-m-d');
|
||||||
|
$organizationName = preg_replace('/[^a-zA-Z0-9]/', '_', $organization->getName());
|
||||||
|
$extension = $logoFile->guessExtension();
|
||||||
|
$newFilename = $currentDate . '_' . $organizationName . $extension;
|
||||||
|
// Move the file to the directory where logos are stored
|
||||||
|
$logoFile->move(
|
||||||
|
$this->getParameter('logos_directory'),
|
||||||
|
$newFilename
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update the 'logoUrl' property to store the file name
|
||||||
|
$organization->setLogoUrl($newFilename);
|
||||||
|
}
|
||||||
|
$this->entityManager->persist($organization);
|
||||||
|
$this->entityManager->flush();
|
||||||
|
$this->addFlash('success', 'Organization created successfully');
|
||||||
|
return $this->redirectToRoute('organization_index');
|
||||||
|
}
|
||||||
|
return $this->render('organization/new.html.twig', [
|
||||||
|
'form' => $form->createView(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/{id}', name: 'show', requirements: ['id' => '\d+'], methods: ['GET'])]
|
||||||
|
public function show(int $id, ActionService $actionService): Response
|
||||||
{
|
{
|
||||||
if ($this->isGranted('ROLE_ADMIN')) {
|
if ($this->isGranted('ROLE_ADMIN')) {
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
if (!$user) {
|
if (!$user) {
|
||||||
return $this->redirectToRoute('app_login');
|
return $this->redirectToRoute('app_login');
|
||||||
}
|
}
|
||||||
$roleAdmin = $this->entityManager->getRepository(Roles::class)->findOneBy(['name' => 'ADMIN']);
|
|
||||||
$uo = $this->entityManager->getRepository(UsersOrganizations::class)->findOneBy([
|
|
||||||
'users' => $user,
|
|
||||||
'organization' => $id,
|
|
||||||
'role' => $roleAdmin
|
|
||||||
]);
|
|
||||||
if (!$uo) {
|
|
||||||
throw $this->createNotFoundException(self::ACCESS_DENIED);
|
|
||||||
}
|
|
||||||
//Don't care about the null pointer because if no UO found, it won't pass the previous check
|
//Don't care about the null pointer because if no UO found, it won't pass the previous check
|
||||||
$organization = $this->entityManager->getRepository(Organizations::class)->find($id);
|
$organization = $this->entityManager->getRepository(Organizations::class)->find($id);
|
||||||
$newUsers = $this->entityManager->getRepository(UsersOrganizations::class)->getLastNewActiveUsersByOrganization($organization);
|
$newUsers = $this->entityManager->getRepository(UsersOrganizations::class)->getLastNewActiveUsersByOrganization($organization);
|
||||||
|
|
@ -78,7 +109,21 @@ class OrganizationController extends AbstractController
|
||||||
// get all applications
|
// get all applications
|
||||||
$applications = $this->organizationsService->getApplicationsWithAccessStatus($organization);
|
$applications = $this->organizationsService->getApplicationsWithAccessStatus($organization);
|
||||||
|
|
||||||
}else{
|
$actions = $organization->getActions()->toArray();
|
||||||
|
usort($actions, static function ($a, $b) {
|
||||||
|
return $b->getDate() <=> $a->getDate();
|
||||||
|
});
|
||||||
|
//get the last 10 activities
|
||||||
|
$actions = array_slice($actions, 0, 10);
|
||||||
|
$activities = array_map(static function ($activity) use ($actionService) {
|
||||||
|
return [
|
||||||
|
'date' => $activity->getDate(), // or however you access the date
|
||||||
|
'actionType' => $activity->getActionType(),
|
||||||
|
'users' => $activity->getUsers(),
|
||||||
|
'color' => $actionService->getActivityColor($activity->getDate())
|
||||||
|
];
|
||||||
|
}, $actions);
|
||||||
|
} else {
|
||||||
throw $this->createNotFoundException(self::ACCESS_DENIED);
|
throw $this->createNotFoundException(self::ACCESS_DENIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,9 +131,52 @@ class OrganizationController extends AbstractController
|
||||||
'organization' => $organization,
|
'organization' => $organization,
|
||||||
'adminUsers' => $adminUsers,
|
'adminUsers' => $adminUsers,
|
||||||
'newUsers' => $newUsers,
|
'newUsers' => $newUsers,
|
||||||
'org' => $org[0],
|
'org' => !empty($org) ? $org[0] : null,
|
||||||
'applications' => $applications,
|
'applications' => $applications,
|
||||||
|
'activities' => $activities
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/edit/{id}', name: 'edit', requirements: ['id' => '\d+'], methods: ['GET', 'POST'])]
|
||||||
|
public function edit(Request $request): Response
|
||||||
|
{
|
||||||
|
$id = $request->attributes->get('id');
|
||||||
|
if (!$this->isGranted('ROLE_SUPER_ADMIN')) {
|
||||||
|
throw $this->createNotFoundException(self::ACCESS_DENIED);
|
||||||
|
}
|
||||||
|
$organization = $this->entityManager->getRepository(Organizations::class)->find($id);
|
||||||
|
if (!$organization) {
|
||||||
|
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||||
|
}
|
||||||
|
$form = $this->createForm(OrganizationForm::class, $organization);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
$logoFile = $form->get('logoUrl')->getData();
|
||||||
|
|
||||||
|
if ($logoFile) {
|
||||||
|
$currentDate = (new \DateTime())->format('Y-m-d');
|
||||||
|
$organizationName = preg_replace('/[^a-zA-Z0-9]/', '_', $organization->getName());
|
||||||
|
$extension = $logoFile->guessExtension();
|
||||||
|
$newFilename = $currentDate . '_' . $organizationName . '.' . $extension;
|
||||||
|
// Move the file to the directory where logos are stored
|
||||||
|
$logoFile->move(
|
||||||
|
$this->getParameter('logos_directory'),
|
||||||
|
$newFilename
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update the 'logoUrl' property to store the file name
|
||||||
|
$organization->setLogoUrl($newFilename);
|
||||||
|
}
|
||||||
|
$this->entityManager->persist($organization);
|
||||||
|
$this->entityManager->flush();
|
||||||
|
$this->addFlash('success', 'Organization updated successfully');
|
||||||
|
return $this->redirectToRoute('organization_index');
|
||||||
|
}
|
||||||
|
return $this->render('organization/edit.html.twig', [
|
||||||
|
'form' => $form->createView(),
|
||||||
|
'organization' => $organization,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Entity\Actions;
|
||||||
use App\Entity\Apps;
|
use App\Entity\Apps;
|
||||||
|
use App\Entity\Organizations;
|
||||||
use App\Entity\Roles;
|
use App\Entity\Roles;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Form\UserForm;
|
use App\Form\UserForm;
|
||||||
|
|
@ -61,7 +63,7 @@ class UserController extends AbstractController
|
||||||
* GET /user/{id} - Show specific user (show/member)
|
* GET /user/{id} - Show specific user (show/member)
|
||||||
*/
|
*/
|
||||||
#[Route('/{id}', name: 'show', requirements: ['id' => '\d+'], methods: ['GET'])]
|
#[Route('/{id}', name: 'show', requirements: ['id' => '\d+'], methods: ['GET'])]
|
||||||
public function show(int $id, EntityManagerInterface $entityManager): Response
|
public function show(int $id, EntityManagerInterface $entityManager, Request $request): Response
|
||||||
{
|
{
|
||||||
if (!$this->isGranted('ROLE_SUPER_ADMIN')) {
|
if (!$this->isGranted('ROLE_SUPER_ADMIN')) {
|
||||||
throw $this->createAccessDeniedException(self::ACCESS_DENIED);
|
throw $this->createAccessDeniedException(self::ACCESS_DENIED);
|
||||||
|
|
@ -71,8 +73,11 @@ class UserController extends AbstractController
|
||||||
if (!$user) {
|
if (!$user) {
|
||||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
if($request->query->has('organizationId')) {
|
||||||
|
$userOrganizations = $this->userOrganizationService->getUserOrganizations($user, $request->query->get('organizationId'));
|
||||||
|
}else{
|
||||||
$userOrganizations = $this->userOrganizationService->getUserOrganizations($user);
|
$userOrganizations = $this->userOrganizationService->getUserOrganizations($user);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->render('user/show.html.twig', [
|
return $this->render('user/show.html.twig', [
|
||||||
'user' => $user,
|
'user' => $user,
|
||||||
|
|
@ -87,6 +92,7 @@ class UserController extends AbstractController
|
||||||
public function new(Request $request): Response
|
public function new(Request $request): Response
|
||||||
{
|
{
|
||||||
$form = $this->createForm(UserForm::class);
|
$form = $this->createForm(UserForm::class);
|
||||||
|
$organizationId = $request->query->get('organizationId');
|
||||||
|
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
|
@ -95,12 +101,38 @@ class UserController extends AbstractController
|
||||||
$data = $form->getData();
|
$data = $form->getData();
|
||||||
// Handle user creation logic here
|
// Handle user creation logic here
|
||||||
|
|
||||||
|
|
||||||
//FOR DEV PURPOSES ONLY
|
//FOR DEV PURPOSES ONLY
|
||||||
$data->setPictureUrl("");
|
$data->setPictureUrl("");
|
||||||
$data->setPassword($this->userService->generateRandomPassword());
|
$data->setPassword($this->userService->generateRandomPassword());
|
||||||
//FOR DEV PURPOSES ONLY
|
//FOR DEV PURPOSES ONLY
|
||||||
|
$orgId = $request->get('organization_id');
|
||||||
|
if ($orgId) {
|
||||||
|
$organization = $this->entityManager->getRepository(Organizations::class)->find($orgId);
|
||||||
|
$roleUser = $this->entityManager->getRepository(Roles::class)->findOneBy(['name' => 'USER']);
|
||||||
|
if (!$organization || !$roleUser) {
|
||||||
|
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||||
|
}
|
||||||
|
$uo = new UsersOrganizations();
|
||||||
|
$uo->setOrganization($organization);
|
||||||
|
$uo->setRole($roleUser);
|
||||||
|
$uo->setUsers($data);
|
||||||
|
|
||||||
|
//log the action
|
||||||
|
$action = new Actions();
|
||||||
|
$action->setActionType('Création utilisateur dans une organisation');
|
||||||
|
$action->setUsers($this->getUser());
|
||||||
|
$action->setOrganization($organization);
|
||||||
|
$this->entityManager->persist($uo);
|
||||||
|
}else{
|
||||||
|
$action = new Actions();
|
||||||
|
$action->setActionType('Création utilisateur');
|
||||||
|
$action->setUsers($this->getUser());
|
||||||
|
}
|
||||||
$this->entityManager->persist($data);
|
$this->entityManager->persist($data);
|
||||||
|
$this->entityManager->persist($action);
|
||||||
|
|
||||||
|
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
// Redirect to user index
|
// Redirect to user index
|
||||||
|
|
@ -109,13 +141,14 @@ class UserController extends AbstractController
|
||||||
|
|
||||||
return $this->render('user/new.html.twig', [
|
return $this->render('user/new.html.twig', [
|
||||||
'form' => $form->createView(),
|
'form' => $form->createView(),
|
||||||
|
'organizationId' => $organizationId,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /user/{id}/edit - Show form to edit user
|
* GET /user/{id}/edit - Show form to edit user
|
||||||
*/
|
*/
|
||||||
#[Route('/{id}/edit', name: 'edit', requirements: ['id' => '\d+'], methods: ['GET', 'PUT', 'POST'])]
|
#[Route('/edit/{id}', name: 'edit', requirements: ['id' => '\d+'], methods: ['GET', 'PUT', 'POST'])]
|
||||||
public function edit(int $id, EntityManagerInterface $entityManager, Request $request): Response
|
public function edit(int $id, EntityManagerInterface $entityManager, Request $request): Response
|
||||||
{
|
{
|
||||||
//Handle access control
|
//Handle access control
|
||||||
|
|
@ -137,6 +170,11 @@ class UserController extends AbstractController
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
//Persist changes to the user entity
|
//Persist changes to the user entity
|
||||||
$entityManager->persist($user);
|
$entityManager->persist($user);
|
||||||
|
//Log the action
|
||||||
|
$action = new Actions();
|
||||||
|
$action->setActionType('Modification utilisateur');
|
||||||
|
$action->setUsers($this->getUser());
|
||||||
|
$entityManager->persist($action);
|
||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
|
|
||||||
//Redirect to user profile after successful edit
|
//Redirect to user profile after successful edit
|
||||||
|
|
@ -171,6 +209,11 @@ class UserController extends AbstractController
|
||||||
// Handle user deletion logic
|
// Handle user deletion logic
|
||||||
$user->setIsDeleted(true);
|
$user->setIsDeleted(true);
|
||||||
$entityManager->persist($user);
|
$entityManager->persist($user);
|
||||||
|
// Log the action
|
||||||
|
$action = new Actions();
|
||||||
|
$action->setActionType('Suppression utilisateur');
|
||||||
|
$action->setUsers($this->getUser());
|
||||||
|
$entityManager->persist($action);
|
||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
|
|
||||||
return $this->redirectToRoute('user_index');
|
return $this->redirectToRoute('user_index');
|
||||||
|
|
@ -193,6 +236,11 @@ class UserController extends AbstractController
|
||||||
|
|
||||||
// Handle user deletion logic
|
// Handle user deletion logic
|
||||||
$entityManager->remove($user);
|
$entityManager->remove($user);
|
||||||
|
// Log the action
|
||||||
|
$action = new Actions();
|
||||||
|
$action->setActionType('Suppression définitive utilisateur');
|
||||||
|
$action->setUsers($this->getUser());
|
||||||
|
$entityManager->persist($action);
|
||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
|
|
||||||
return $this->redirectToRoute('user_index');
|
return $this->redirectToRoute('user_index');
|
||||||
|
|
@ -215,6 +263,11 @@ class UserController extends AbstractController
|
||||||
}
|
}
|
||||||
$user->setIsActive(false);
|
$user->setIsActive(false);
|
||||||
$entityManager->persist($user);
|
$entityManager->persist($user);
|
||||||
|
// Log the action
|
||||||
|
$action = new Actions();
|
||||||
|
$action->setActionType('Désactivation utilisateur');
|
||||||
|
$action->setUsers($this->getUser());
|
||||||
|
$entityManager->persist($action);
|
||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
return $this->redirectToRoute('user_index');
|
return $this->redirectToRoute('user_index');
|
||||||
}
|
}
|
||||||
|
|
@ -260,7 +313,7 @@ class UserController extends AbstractController
|
||||||
|
|
||||||
// Fetch all roles and apps
|
// Fetch all roles and apps
|
||||||
$roles = $entityManager->getRepository(Roles::class)->findAll();
|
$roles = $entityManager->getRepository(Roles::class)->findAll();
|
||||||
$apps = $entityManager->getRepository(Apps::class)->findAll();
|
$apps = $organization->getApps() ?? throw $this->createNotFoundException(self::NOT_FOUND);
|
||||||
if (!$roles) {
|
if (!$roles) {
|
||||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ class Actions
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
private ?int $id = null;
|
private ?int $id = null;
|
||||||
|
|
||||||
#[ORM\OneToOne(cascade: ['persist', 'remove'])]
|
#[ORM\ManyToOne(cascade: ['persist', 'remove'])]
|
||||||
private ?user $users = null;
|
private ?user $users = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 255)]
|
#[ORM\Column(length: 255)]
|
||||||
|
|
@ -22,6 +22,17 @@ class Actions
|
||||||
#[ORM\Column(options: ['default' => 'CURRENT_TIMESTAMP'])]
|
#[ORM\Column(options: ['default' => 'CURRENT_TIMESTAMP'])]
|
||||||
private ?\DateTimeImmutable $date = null;
|
private ?\DateTimeImmutable $date = null;
|
||||||
|
|
||||||
|
#[ORM\ManyToOne(inversedBy: 'actions')]
|
||||||
|
private ?Organizations $Organization = null;
|
||||||
|
|
||||||
|
#[ORM\Column(length: 255, nullable: true)]
|
||||||
|
private ?string $description = null;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->date = new \DateTimeImmutable();
|
||||||
|
}
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
|
|
@ -62,4 +73,28 @@ class Actions
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getOrganization(): ?Organizations
|
||||||
|
{
|
||||||
|
return $this->Organization;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setOrganization(?Organizations $Organization): static
|
||||||
|
{
|
||||||
|
$this->Organization = $Organization;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription(): ?string
|
||||||
|
{
|
||||||
|
return $this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDescription(?string $description): static
|
||||||
|
{
|
||||||
|
$this->description = $description;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,9 @@ class Apps
|
||||||
#[ORM\ManyToMany(targetEntity: organizations::class, inversedBy: 'apps')]
|
#[ORM\ManyToMany(targetEntity: organizations::class, inversedBy: 'apps')]
|
||||||
private Collection $organization;
|
private Collection $organization;
|
||||||
|
|
||||||
|
#[ORM\Column(length: 255, nullable: true)]
|
||||||
|
private ?string $descriptionSmall = null;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->organization = new ArrayCollection();
|
$this->organization = new ArrayCollection();
|
||||||
|
|
@ -144,4 +147,16 @@ class Apps
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDescriptionSmall(): ?string
|
||||||
|
{
|
||||||
|
return $this->descriptionSmall;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDescriptionSmall(?string $descriptionSmall): static
|
||||||
|
{
|
||||||
|
$this->descriptionSmall = $descriptionSmall;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,10 @@ class Organizations
|
||||||
private ?\DateTimeImmutable $createdAt = null;
|
private ?\DateTimeImmutable $createdAt = null;
|
||||||
|
|
||||||
#[ORM\Column(options: ['default' => false])]
|
#[ORM\Column(options: ['default' => false])]
|
||||||
private ?bool $isDeleted = null;
|
private ?bool $isDeleted = false;
|
||||||
|
|
||||||
#[ORM\Column(options: ['default' => true])]
|
#[ORM\Column(options: ['default' => true])]
|
||||||
private ?bool $isActive = null;
|
private ?bool $isActive = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<int, Apps>
|
* @var Collection<int, Apps>
|
||||||
|
|
@ -45,9 +45,17 @@ class Organizations
|
||||||
#[ORM\Column(length: 255, nullable: true)]
|
#[ORM\Column(length: 255, nullable: true)]
|
||||||
private ?string $name = null;
|
private ?string $name = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Collection<int, Actions>
|
||||||
|
*/
|
||||||
|
#[ORM\OneToMany(targetEntity: Actions::class, mappedBy: 'Organization')]
|
||||||
|
private Collection $actions;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->apps = new ArrayCollection();
|
$this->apps = new ArrayCollection();
|
||||||
|
$this->actions = new ArrayCollection();
|
||||||
|
$this->createdAt = new \DateTimeImmutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
|
|
@ -177,4 +185,34 @@ class Organizations
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection<int, Actions>
|
||||||
|
*/
|
||||||
|
public function getActions(): Collection
|
||||||
|
{
|
||||||
|
return $this->actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addAction(Actions $action): static
|
||||||
|
{
|
||||||
|
if (!$this->actions->contains($action)) {
|
||||||
|
$this->actions->add($action);
|
||||||
|
$action->setOrganization($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeAction(Actions $action): static
|
||||||
|
{
|
||||||
|
if ($this->actions->removeElement($action)) {
|
||||||
|
// set the owning side to null (unless already changed)
|
||||||
|
if ($action->getOrganization() === $this) {
|
||||||
|
$action->setOrganization(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form;
|
||||||
|
|
||||||
|
use App\Entity\Organizations;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\FileType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
class OrganizationForm extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('email', EmailType::class, ['required' => true, 'label' => 'Email*'])
|
||||||
|
->add('name', TextType::class, ['required' => true, 'label' => 'Nom de l\'organisation*'])
|
||||||
|
->add('address', TextType::class, ['required' => false, 'label' => 'Adresse'])
|
||||||
|
->add('number', TextType::class, ['required' => false, 'label' => 'Numéro de téléphone'])
|
||||||
|
->add('logoUrl', FileType::class, [
|
||||||
|
'required' => false,
|
||||||
|
'label' => 'Logo',
|
||||||
|
'mapped' => false, // Important if the entity property is not directly mapped
|
||||||
|
'attr' => ['accept' => 'image/*'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => Organizations::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
class ActionService
|
||||||
|
{
|
||||||
|
|
||||||
|
public function getActivityColor(\DateTimeImmutable $activityTime): string
|
||||||
|
{
|
||||||
|
$now = new \DateTimeImmutable();
|
||||||
|
$diffInSeconds = $now->getTimestamp() - $activityTime->getTimestamp();
|
||||||
|
|
||||||
|
if ($diffInSeconds < 15 * 60) { // less than 15 minutes
|
||||||
|
return '#086572';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($diffInSeconds < 60 * 60) { // less than 1 hour
|
||||||
|
return '#247208';
|
||||||
|
}
|
||||||
|
return '#C76633';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,206 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
use Aws\S3\S3Client;
|
||||||
|
|
||||||
|
class AwsService
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private S3Client $s3Client,
|
||||||
|
private string $awsPublicUrl
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to generate UUID Version 4
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function generateUUIDv4():string {
|
||||||
|
$uuid = uuid_create(4);
|
||||||
|
|
||||||
|
$isValid = uuid_is_valid($uuid);
|
||||||
|
|
||||||
|
if( $isValid == true){
|
||||||
|
$retour = $uuid;
|
||||||
|
}else{
|
||||||
|
$retour = 'une erreur est survenue !';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $retour;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get public url for files download or visualisation
|
||||||
|
*
|
||||||
|
* @param string $bucket nom du conteneur S3
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPublicUrl(string $bucket): string{
|
||||||
|
$publicUrl = substr_replace($this->awsPublicUrl, $bucket.'.', 8, 0);
|
||||||
|
$publicUrl .= '/';
|
||||||
|
|
||||||
|
return $publicUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CREATE bucket S3 for new project
|
||||||
|
*
|
||||||
|
* @return string|array
|
||||||
|
*/
|
||||||
|
public function createBucket(): string|array{
|
||||||
|
|
||||||
|
$bucket = $this->generateUUIDv4();
|
||||||
|
|
||||||
|
$result = $this->s3Client->createBucket([
|
||||||
|
'Bucket' => $bucket,
|
||||||
|
'ObjectOwnership' => 'BucketOwnerPreferred'
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ( $result['@metadata']['statusCode'] == 200){
|
||||||
|
return $bucket;
|
||||||
|
}else{
|
||||||
|
return $result['@metadata'];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE bucket S3
|
||||||
|
*
|
||||||
|
* @param string $bucket nom du conteneur S3
|
||||||
|
* @return string|array
|
||||||
|
*/
|
||||||
|
public function DeleteBucket(string $bucket): string|array{
|
||||||
|
|
||||||
|
$result = $this->s3Client->deleteBucket([
|
||||||
|
'Bucket' => $bucket,
|
||||||
|
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ( $result['@metadata']['statusCode'] == 200){
|
||||||
|
return $bucket;
|
||||||
|
}else{
|
||||||
|
return $result['@metadata'];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list files infos in the Bucket S3
|
||||||
|
* If prefix NULL get ALL FILES else get FILES in this prefix
|
||||||
|
*
|
||||||
|
* @param string $bucket nom du conteneur S3
|
||||||
|
* @param string|null $prefix arborescence dans le bucket
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function getListObject(string $bucket, string|null $prefix = null):array|null{
|
||||||
|
|
||||||
|
$results = $this->s3Client->listObjectsV2([
|
||||||
|
'Bucket' => $bucket,
|
||||||
|
'Prefix' => $prefix
|
||||||
|
]);
|
||||||
|
|
||||||
|
if( isset($results['Contents']) ){
|
||||||
|
$return = $results['Contents'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUT file Object in bucket S3
|
||||||
|
*
|
||||||
|
* @param string $bucket nom du conteneur S3
|
||||||
|
* @param object $file fichier à déposer dans le bucket
|
||||||
|
* @param string $filename nom du fichier enregistré dans la bdd métier
|
||||||
|
* @param string $mimeType type du fichier
|
||||||
|
* @param string|null $prefix arborescence dans le bucket
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function PutDocObj(string $bucket, object $file, string $filename, $mimeType, string|null $prefix = null): int{
|
||||||
|
|
||||||
|
$body = fopen( $file, 'r');
|
||||||
|
$hashRaw = hash_file('sha256', $file, true);
|
||||||
|
$hash = base64_encode($hashRaw);
|
||||||
|
|
||||||
|
rewind($body);
|
||||||
|
|
||||||
|
$doc = $this->s3Client->putObject([
|
||||||
|
'Bucket' => $bucket,
|
||||||
|
'ChecksumAlgorithm' => 'SHA256',
|
||||||
|
'ChecksumSHA256' => $hash,
|
||||||
|
'Key' => $prefix.$filename,
|
||||||
|
'Body' => $body,
|
||||||
|
'ACL' => 'public-read',
|
||||||
|
'ContentType' => $mimeType // pour rendre l'image publique si besoin
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $doc['@metadata']['statusCode'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE file Object in bucket S3
|
||||||
|
*
|
||||||
|
* @param string $bucket nom du conteneur S3
|
||||||
|
* @param string $filename nom du fichier
|
||||||
|
* @param string|null $prefix arborescence dans le bucket
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function DeleteDocObj(string $bucket, string $filename, string|null $prefix = null): int{
|
||||||
|
|
||||||
|
$doc = $this->s3Client->deleteObject([
|
||||||
|
'Bucket' => $bucket,
|
||||||
|
'Key' => $prefix.$filename,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $doc['@metadata']['statusCode'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RENAME file Object in bucket S3
|
||||||
|
*
|
||||||
|
* @param string $bucket nom du conteneur S3
|
||||||
|
* @param string $filename nom du fichier
|
||||||
|
* @param string $newFilename
|
||||||
|
* @param string|null $prefix arborescence dans le bucket
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function renameDocObj(string $bucket, string $filename, string $newFilename, string|null $prefix = null): int{
|
||||||
|
|
||||||
|
$doc = $this->s3Client->copyObject([
|
||||||
|
'Bucket' => $bucket,
|
||||||
|
'CopySource' => $prefix.$filename,
|
||||||
|
'Key' => $prefix.$newFilename,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->DeleteDocObj($bucket, $filename, $prefix);
|
||||||
|
|
||||||
|
return $doc['@metadata']['statusCode'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MOVE file Object in bucket S3
|
||||||
|
*
|
||||||
|
* @param string $bucket nom du conteneur S3
|
||||||
|
* @param string $filename nom du fichier
|
||||||
|
* @param string|null $prefix arborescence dans le bucket
|
||||||
|
* @param string|null $newPrefix nouvel emplacement dans le bucket
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function moveDocObj(string $bucket, string $filename, string|null $prefix = null, string|null $newPrefix = null): int{
|
||||||
|
|
||||||
|
$doc = $this->s3Client->copyObject([
|
||||||
|
'Bucket' => $bucket,
|
||||||
|
'CopySource' => $prefix.$filename,
|
||||||
|
'Key' => $newPrefix.$filename,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->DeleteDocObj($bucket, $filename, $prefix);
|
||||||
|
|
||||||
|
return $doc['@metadata']['statusCode'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Service;
|
namespace App\Service;
|
||||||
|
|
||||||
|
use App\Entity\Actions;
|
||||||
use App\Entity\Apps;
|
use App\Entity\Apps;
|
||||||
use App\Entity\Organizations;
|
use App\Entity\Organizations;
|
||||||
use App\Entity\Roles;
|
use App\Entity\Roles;
|
||||||
|
|
@ -333,6 +334,12 @@ readonly class UserOrganizationService
|
||||||
]);
|
]);
|
||||||
foreach ($userOrganizations as $uo) {
|
foreach ($userOrganizations as $uo) {
|
||||||
$uo->setIsActive(false);
|
$uo->setIsActive(false);
|
||||||
|
//Log action
|
||||||
|
$action = new Actions();
|
||||||
|
$action->setActionType("Désactivation role" );
|
||||||
|
$action->setDescription("Désactivation du rôle " . $uo->getRole()->getName() . " pour l'utilisateur " . $user->getUserIdentifier() . " dans l'organisation " . $organization->getName());
|
||||||
|
$action->setOrganization($organization);
|
||||||
|
$action->setUsers($user);
|
||||||
$this->entityManager->persist($uo);
|
$this->entityManager->persist($uo);
|
||||||
}
|
}
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
@ -401,12 +408,12 @@ readonly class UserOrganizationService
|
||||||
*/
|
*/
|
||||||
public function findActiveUsersByOrganizations(array $organizations): array
|
public function findActiveUsersByOrganizations(array $organizations): array
|
||||||
{
|
{
|
||||||
|
|
||||||
if (empty($organizations)) {
|
if (empty($organizations)) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$userOrgs = $this->entityManager->getRepository(UsersOrganizations::class)->getAllActiveUserOrganizationLinks($organizations);
|
$userOrgs = $this->entityManager->getRepository(UsersOrganizations::class)->getAllActiveUserOrganizationLinks($organizations);
|
||||||
|
|
||||||
$usersByOrg = [];
|
$usersByOrg = [];
|
||||||
foreach ($userOrgs as $uo) {
|
foreach ($userOrgs as $uo) {
|
||||||
$org = $uo->getOrganization();
|
$org = $uo->getOrganization();
|
||||||
|
|
|
||||||
18
symfony.lock
18
symfony.lock
|
|
@ -1,4 +1,16 @@
|
||||||
{
|
{
|
||||||
|
"aws/aws-sdk-php-symfony": {
|
||||||
|
"version": "2.8",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes-contrib",
|
||||||
|
"branch": "main",
|
||||||
|
"version": "1.3",
|
||||||
|
"ref": "d1753f9e2a669c464b2b0618af9b0123426b67b4"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/packages/aws.yaml"
|
||||||
|
]
|
||||||
|
},
|
||||||
"doctrine/deprecations": {
|
"doctrine/deprecations": {
|
||||||
"version": "1.1",
|
"version": "1.1",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
|
|
@ -35,6 +47,9 @@
|
||||||
"migrations/.gitignore"
|
"migrations/.gitignore"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"knplabs/knp-time-bundle": {
|
||||||
|
"version": "v2.4.0"
|
||||||
|
},
|
||||||
"league/oauth2-server-bundle": {
|
"league/oauth2-server-bundle": {
|
||||||
"version": "0.11",
|
"version": "0.11",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
|
|
@ -381,8 +396,5 @@
|
||||||
"files": [
|
"files": [
|
||||||
"config/packages/messenger.yaml"
|
"config/packages/messenger.yaml"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"twig/extra-bundle": {
|
|
||||||
"version": "v3.20.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="card-title">
|
||||||
|
<h3><img width=10% src="{{ asset(application.application.logoUrl) }}" alt="Logo application">
|
||||||
|
{{ application.application.name }}</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body d-flex flex-column align-items-center">
|
||||||
|
<p class="card-text">{{ application.application.descriptionSmall }}</p>
|
||||||
|
{% if application.has_access %}
|
||||||
|
<div >
|
||||||
|
<a href="http://{{ application.application.subDomain }}.solutions-easy.moi" class="btn btn-primary me-2">Y
|
||||||
|
accéder</a>
|
||||||
|
<a href="#" class="btn btn-secondary">Gérer l'application</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% else %}<a href="#" class="btn btn-primary">Demander l'accès</a>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -2,36 +2,24 @@
|
||||||
|
|
||||||
|
|
||||||
<div class="card border-0">
|
<div class="card border-0">
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
<div class="card-header d-flex justify-content-between align-items-center border-0">
|
||||||
<h3>{{ title }}</h3>
|
<h3>{{ title }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
{# {% if activities|length == 0 %}#}
|
{% if activities|length == 0 %}
|
||||||
{# <p>Aucune activité récente.</p>#}
|
<p>Aucune activité récente.</p>
|
||||||
{# {% else %}#}
|
{% else %}
|
||||||
|
{% set sortedActivities = activities|sort((a, b) => a.date <=> b.date)|reverse %}
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
{# {% for activity in activities %}#}
|
{% for activity in sortedActivities%}
|
||||||
<li class="list-group-item">
|
{% include 'user/organization/userActivity.html.twig' with {
|
||||||
<p> 5 mins ago</p>
|
activityTime: activity.date,
|
||||||
</li>
|
action: activity.actionType,
|
||||||
<li class="list-group-item">
|
userName: activity.users.name,
|
||||||
<p> 5 mins ago</p>
|
color: activity.color
|
||||||
</li>
|
} %}
|
||||||
<li class="list-group-item">
|
{% endfor %}
|
||||||
<p> 5 mins ago</p>
|
|
||||||
</li>
|
|
||||||
<li class="list-group-item">
|
|
||||||
<p> 5 mins ago</p>
|
|
||||||
</li>
|
|
||||||
<li class="list-group-item">
|
|
||||||
<p> 5 mins ago</p>
|
|
||||||
</li>
|
|
||||||
<li class="list-group-item">
|
|
||||||
<p> 5 mins ago</p>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
{# {% endfor %}#}
|
|
||||||
</ul>
|
</ul>
|
||||||
{# {% endif %}#}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div class=" col-md-10 m-auto p-5">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title shadow-sm p-3 d-flex justify-content-between align-items-center">
|
||||||
|
<h2>Modifier l'organisation</h2>
|
||||||
|
{% if is_granted("ROLE_SUPER_ADMIN") %}
|
||||||
|
{# <a href="{{ path('organization_delete', {'id': organization.id}) }}" class="btn btn-danger">Supprimer</a>#}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
{{ form_start(form, {'action': path('organization_edit', {'id': organization.id}), 'method': 'PUT'}) }}
|
||||||
|
{{ form_widget(form) }}
|
||||||
|
<button type="submit" class="btn btn-primary">Enregistrer</button>
|
||||||
|
{{ form_end(form) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -6,11 +6,16 @@
|
||||||
<div class="w-100 h-100 p-5 m-auto" data-controller="organization">
|
<div class="w-100 h-100 p-5 m-auto" data-controller="organization">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h1>Gestion des organisations</h1>
|
<h1>Gestion des organisations</h1>
|
||||||
{# <a href="{{ path('organization_new') }}" class="btn btn-primary">Ajouter une organisation</a>#}
|
{% if is_granted("ROLE_SUPER_ADMIN") %}
|
||||||
|
<a href="{{ path('organization_new') }}" class="btn btn-primary">Ajouter une organisation</a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if organizations|length == 0 %}
|
{% if organizations|length == 0 %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-center">Aucune organisation trouvée.</td>
|
<td colspan="4" class="text-center">Aucune organisation trouvée.</td>
|
||||||
|
<td colspan="4" class="text-center">
|
||||||
|
<a href="{{ path('organization_new') }}" class="btn btn-primary">Créer une organisation</a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% else %}
|
{% else %}
|
||||||
<table class="table align-middle shadow">
|
<table class="table align-middle shadow">
|
||||||
|
|
@ -27,7 +32,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{% if organization.logoUrl %}
|
{% if organization.logoUrl %}
|
||||||
<img src="{{ asset(organization.logoUrl) }}" alt="Organization logo" class="rounded-circle" style="width:40px; height:40px;">
|
<img src="{{ asset('uploads/logos/' ~ organization.logoUrl) }}" alt="Organization logo" class="rounded-circle" style="width:40px; height:40px;">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ organization.name }}</td>
|
<td>{{ organization.name }}</td>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block title %}Ajouter une organisation{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div class=" col-md-10 m-auto p-5">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title shadow-sm p-3 d-flex justify-content-between align-items-center">
|
||||||
|
<h1>Ajouter une organisation</h1>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="post" action="{{ path('organization_new') }}" enctype="multipart/form-data">
|
||||||
|
{{ form_start(form) }}
|
||||||
|
{{ form_widget(form) }}
|
||||||
|
<button type="submit" class="btn btn-primary">Enregistrer</button>
|
||||||
|
{{ form_end(form) }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -3,12 +3,18 @@
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="col-md-12 m-auto p-5">
|
<div class="col-md-12 m-auto p-5">
|
||||||
<div class="col d-flex justify-content-between align-items-center ">
|
<div class="col d-flex justify-content-between align-items-center ">
|
||||||
<h1 class="mb-4">{{ organization.name|title }} - Dashboard</h1>
|
<h1 class="mb-4">
|
||||||
{% if is_granted("ROLE_SUER_ADMIN") %}
|
{% if organization.logoUrl %}
|
||||||
{# <a href="{{ path('user_deactivate', {'id': user.id}) }}" class="btn btn-danger">Désactiver</a> #}
|
<img src="{{ asset('uploads/logos/' ~ organization.logoUrl) }}" alt="Organization logo"
|
||||||
|
class="rounded-circle" style="width:40px; height:40px;">
|
||||||
|
{% endif %}
|
||||||
|
{{ organization.name|title }} - Dashboard</h1>
|
||||||
|
{% if is_granted("ROLE_SUPER_ADMIN") %}
|
||||||
|
<a href="{{ path('organization_edit', {'id': organization.id}) }}" class="btn btn-primary">Gérer mon
|
||||||
|
organisation</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{# USER ROW#}
|
{# USER ROW #}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
|
|
@ -16,7 +22,8 @@
|
||||||
{% include 'user/userListSmall.html.twig' with {
|
{% include 'user/userListSmall.html.twig' with {
|
||||||
title: 'Nouveaux utilisateurs',
|
title: 'Nouveaux utilisateurs',
|
||||||
users: newUsers,
|
users: newUsers,
|
||||||
empty_message: 'Aucun nouvel utilisateur trouvé.'
|
empty_message: 'Aucun nouveaux utilisateurs trouvé.',
|
||||||
|
organizationId: organization.id
|
||||||
} %}
|
} %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col mb-3 mb-sm-0">
|
<div class="col mb-3 mb-sm-0">
|
||||||
|
|
@ -30,8 +37,20 @@
|
||||||
<div class="m-auto">
|
<div class="m-auto">
|
||||||
{% include 'user/userList.html.twig' with {
|
{% include 'user/userList.html.twig' with {
|
||||||
title: 'Mes utilisateurs',
|
title: 'Mes utilisateurs',
|
||||||
|
organizationId: organization.id,
|
||||||
|
empty_message: 'Aucun utilisateurs trouvé.'
|
||||||
} %}
|
} %}
|
||||||
</div>
|
</div>
|
||||||
|
{# APPLICATION ROW #}
|
||||||
|
<div class="row ">
|
||||||
|
{% for application in applications %}
|
||||||
|
<div class="col-6 mb-3">
|
||||||
|
{% include 'applications/appSmall.html.twig' with {
|
||||||
|
application: application
|
||||||
|
} %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-3 m-auto">
|
<div class="col-3 m-auto">
|
||||||
|
|
@ -43,11 +62,9 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# APPLICATION ROW#}
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,11 @@
|
||||||
<form method="post" action="{{ path('user_new') }}" enctype="multipart/form-data">
|
<form method="post" action="{{ path('user_new') }}" enctype="multipart/form-data">
|
||||||
{{ form_start(form) }}
|
{{ form_start(form) }}
|
||||||
{{ form_widget(form) }}
|
{{ form_widget(form) }}
|
||||||
|
{% if organizationId is defined %}
|
||||||
|
<div class="form-group">
|
||||||
|
<input hidden type="text" value="{{ organizationId }}" name="organization_id">
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<button type="submit" class="btn btn-primary">Enregistrer</button>
|
<button type="submit" class="btn btn-primary">Enregistrer</button>
|
||||||
{{ form_end(form) }}
|
{{ form_end(form) }}
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
<div class="card border">
|
||||||
|
<div class="card-header d-flex align-items-center border-0">
|
||||||
|
<div class="row align-items-center">
|
||||||
|
|
||||||
|
<h4 class="mb-0">
|
||||||
|
<span style="display:inline-block; width:16px; height:16px; border-radius:50%; background:{{ color }}; margin-right:10px;"></span>
|
||||||
|
{{ activityTime|ago }}</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p>{{ userName }} - {{ action }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -22,11 +22,15 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% if org.users|length == 0 %}
|
{% if org|length == 0 %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="6" class="text-center">Aucun utilisateur trouvé.</td>
|
<td colspan="6" class="text-center">Aucun utilisateur trouvé.</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% elseif org.users|length == 0 %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="6" class="text-center">Aucun utilisateur trouvé.</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
{% for user in org.users %}
|
{% for user in org.users %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
@ -47,13 +51,21 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ path('user_show', {'id': user.users.id}) }}" class="p-3 align-middle">
|
{% if organizationId is defined %}
|
||||||
<i class="icon-grid menu-icon color-primary">
|
<a href="{{ path('user_show', {'id': user.users.id, 'organizationId': organizationId}) }}"
|
||||||
|
class="p-3 align-middle color-primary">
|
||||||
{{ ux_icon('fa6-regular:eye', {height: '30px', width: '30px'}) }}
|
{{ ux_icon('fa6-regular:eye', {height: '30px', width: '30px'}) }}
|
||||||
</a>
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ path('user_show', {'id': user.users.id}) }}"
|
||||||
|
class="p-3 align-middle color-primary">
|
||||||
|
{{ ux_icon('fa6-regular:eye', {height: '30px', width: '30px'}) }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@
|
||||||
<div class="card border-0">
|
<div class="card border-0">
|
||||||
<div class="card-title p-3 d-flex justify-content-between align-items-center ">
|
<div class="card-title p-3 d-flex justify-content-between align-items-center ">
|
||||||
<h3>{{ title }}</h3>
|
<h3>{{ title }}</h3>
|
||||||
|
{% if organizationId is defined %}
|
||||||
|
<a href="{{ path('user_new', {'organizationId': organizationId}) }}" class="btn btn-primary">Ajouter un utilisateur</a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<table class="table align-middle table-borderless">
|
<table class="table align-middle table-borderless">
|
||||||
|
|
@ -29,10 +32,18 @@
|
||||||
</td>
|
</td>
|
||||||
<td>{{ user.email }}</td>
|
<td>{{ user.email }}</td>
|
||||||
<td>
|
<td>
|
||||||
|
{% if organizationId is defined %}
|
||||||
|
<a href="{{ path('user_show', {'id': user.id, 'organizationId': organizationId}) }}"
|
||||||
|
class="p-3 align-middle color-primary">
|
||||||
|
{{ ux_icon('fa6-regular:eye', {height: '30px', width: '30px'}) }}
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
<a href="{{ path('user_show', {'id': user.id}) }}"
|
<a href="{{ path('user_show', {'id': user.id}) }}"
|
||||||
class="p-3 align-middle color-primary">
|
class="p-3 align-middle color-primary">
|
||||||
{{ ux_icon('fa6-regular:eye', {height: '30px', width: '30px'}) }}
|
{{ ux_icon('fa6-regular:eye', {height: '30px', width: '30px'}) }}
|
||||||
</a>
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue