From b1afcb025184e0b6475d728133dbd3b26c4fb95c Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Tue, 19 Mar 2024 15:46:32 -0500 Subject: [PATCH] Internal: Admin: Adding title and active fields to color theme #3793 --- .../vue/views/admin/AdminConfigureColors.vue | 10 +++- src/CoreBundle/Controller/ThemeController.php | 20 +++++--- src/CoreBundle/Entity/ColorTheme.php | 49 +++++++++++++++++++ src/CoreBundle/EventListener/TwigListener.php | 23 ++++++++- .../Schema/V200/Version20240318105600.php | 2 +- .../Repository/ColorThemeRepository.php | 40 +++++++++++++++ .../Resources/views/Layout/head.html.twig | 6 ++- src/CoreBundle/State/ColorThemeProcessor.php | 15 ++++-- 8 files changed, 151 insertions(+), 14 deletions(-) create mode 100644 src/CoreBundle/Repository/ColorThemeRepository.php diff --git a/assets/vue/views/admin/AdminConfigureColors.vue b/assets/vue/views/admin/AdminConfigureColors.vue index fc7c3e85e0..6ba384cd6a 100644 --- a/assets/vue/views/admin/AdminConfigureColors.vue +++ b/assets/vue/views/admin/AdminConfigureColors.vue @@ -40,7 +40,12 @@ /> -
+
+ + { let colors = getColors() // TODO send colors to backend, then notify if was correct or incorrect await axios.post("/api/color_themes", { + title: themeTitle.value, variables: colors, }) } diff --git a/src/CoreBundle/Controller/ThemeController.php b/src/CoreBundle/Controller/ThemeController.php index 6ec8fc8d3d..4616cf1297 100644 --- a/src/CoreBundle/Controller/ThemeController.php +++ b/src/CoreBundle/Controller/ThemeController.php @@ -6,6 +6,7 @@ declare(strict_types=1); namespace Chamilo\CoreBundle\Controller; +use Chamilo\CoreBundle\Repository\ColorThemeRepository; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Filesystem\Filesystem; @@ -16,18 +17,23 @@ class ThemeController extends AbstractController { public function __construct( private readonly ParameterBagInterface $parameterBag, + private readonly ColorThemeRepository $colorThemeRepository, ) {} #[Route('/theme/colors.css', name: 'chamilo_color_theme', methods: ['GET'])] - public function colorTehemeAction(): Response + public function colorThemeAction(): Response { - $fs = new Filesystem(); - $path = $this->parameterBag->get('kernel.project_dir').'/var/theme/colors.css'; + $response = new Response(''); - if ($fs->exists($path)) { - $response = $this->file($path); - } else { - $response = new Response(''); + $colorTheme = $this->colorThemeRepository->getActiveOne(); + + if ($colorTheme) { + $fs = new Filesystem(); + $path = $this->parameterBag->get('kernel.project_dir')."/var/theme/{$colorTheme->getSlug()}/colors.css"; + + if ($fs->exists($path)) { + $response = $this->file($path); + } } $response->headers->add(['Content-Type' => 'text/css']); diff --git a/src/CoreBundle/Entity/ColorTheme.php b/src/CoreBundle/Entity/ColorTheme.php index 4815b26d90..f9e24aee96 100644 --- a/src/CoreBundle/Entity/ColorTheme.php +++ b/src/CoreBundle/Entity/ColorTheme.php @@ -11,6 +11,7 @@ use ApiPlatform\Metadata\Post; use Chamilo\CoreBundle\State\ColorThemeProcessor; use Chamilo\CoreBundle\Traits\TimestampableTypedEntity; use Doctrine\ORM\Mapping as ORM; +use Gedmo\Mapping\Annotation as Gedmo; use Symfony\Component\Serializer\Annotation\Groups; #[ORM\Entity] @@ -34,6 +35,10 @@ class ColorTheme #[ORM\Column] private ?int $id = null; + #[Groups(['color_theme:write'])] + #[ORM\Column(length: 255)] + private ?string $title = null; + /** * @var array */ @@ -41,11 +46,31 @@ class ColorTheme #[ORM\Column] private array $variables = []; + #[Gedmo\Slug(fields: ['title'])] + #[ORM\Column(length: 255)] + private ?string $slug = null; + + #[Groups(['color_theme:write'])] + #[ORM\Column] + private ?bool $active = null; + public function getId(): ?int { return $this->id; } + public function getTitle(): ?string + { + return $this->title; + } + + public function setTitle(string $title): static + { + $this->title = $title; + + return $this; + } + public function getVariables(): array { return $this->variables; @@ -57,4 +82,28 @@ class ColorTheme return $this; } + + public function getSlug(): ?string + { + return $this->slug; + } + + public function setSlug(string $slug): static + { + $this->slug = $slug; + + return $this; + } + + public function isActive(): ?bool + { + return $this->active; + } + + public function setActive(bool $active): static + { + $this->active = $active; + + return $this; + } } diff --git a/src/CoreBundle/EventListener/TwigListener.php b/src/CoreBundle/EventListener/TwigListener.php index f67ed24ef6..8d6f1c0d1e 100644 --- a/src/CoreBundle/EventListener/TwigListener.php +++ b/src/CoreBundle/EventListener/TwigListener.php @@ -6,9 +6,11 @@ declare(strict_types=1); namespace Chamilo\CoreBundle\EventListener; +use Chamilo\CoreBundle\Repository\ColorThemeRepository; use Chamilo\CoreBundle\Repository\LanguageRepository; use Chamilo\CoreBundle\Settings\SettingsManager; use Symfony\Component\HttpKernel\Event\RequestEvent; +use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Serializer\SerializerInterface; @@ -31,7 +33,9 @@ class TwigListener SerializerInterface $serializer, TokenStorageInterface $tokenStorage, SettingsManager $settingsManager, - LanguageRepository $languageRepository + LanguageRepository $languageRepository, + private readonly ColorThemeRepository $colorThemeRepository, + private readonly RouterInterface $router, ) { $this->twig = $twig; $this->tokenStorage = $tokenStorage; @@ -99,5 +103,22 @@ class TwigListener $this->twig->addGlobal('access_url_id', $request->getSession()->get('access_url_id')); $this->twig->addGlobal('config_json', json_encode($config)); $this->twig->addGlobal('languages_json', json_encode($languages)); + + $this->loadColorTheme(); + } + + private function loadColorTheme(): void + { + $link = null; + + $colorTheme = $this->colorThemeRepository->getActiveOne(); + + if ($colorTheme) { + $path = $this->router->generate('chamilo_color_theme'); + + $link = ''; + } + + $this->twig->addGlobal('color_theme_link', $link); } } diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20240318105600.php b/src/CoreBundle/Migrations/Schema/V200/Version20240318105600.php index 0e2c4e9ad7..4bcb4b3e66 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20240318105600.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20240318105600.php @@ -13,6 +13,6 @@ class Version20240318105600 extends AbstractMigrationChamilo { public function up(Schema $schema): void { - $this->addSql("CREATE TABLE color_theme (id INT AUTO_INCREMENT NOT NULL, variables LONGTEXT NOT NULL COMMENT '(DC2Type:json)', created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime)', updated_at DATETIME NOT NULL COMMENT '(DC2Type:datetime)', PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB ROW_FORMAT = DYNAMIC"); + $this->addSql("CREATE TABLE color_theme (id INT AUTO_INCREMENT NOT NULL, title VARCHAR(255) NOT NULL, variables LONGTEXT NOT NULL COMMENT '(DC2Type:json)', slug VARCHAR(255) NOT NULL, active TINYINT(1) NOT NULL, created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime)', updated_at DATETIME NOT NULL COMMENT '(DC2Type:datetime)', PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB ROW_FORMAT = DYNAMIC"); } } diff --git a/src/CoreBundle/Repository/ColorThemeRepository.php b/src/CoreBundle/Repository/ColorThemeRepository.php new file mode 100644 index 0000000000..d9d59ac4a5 --- /dev/null +++ b/src/CoreBundle/Repository/ColorThemeRepository.php @@ -0,0 +1,40 @@ +getEntityManager()->createQueryBuilder(); + $qb + ->update(ColorTheme::class, 'ct') + ->set('ct.active', ':inactive') + ->where( + $qb->expr()->eq('ct.active', ':active') + ) + ->setParameters(['active' => true, 'inactive' => false]) + ->getQuery() + ->execute() + ; + } + + public function getActiveOne(): ?ColorTheme + { + return $this->findOneBy( + ['active' => true], + ['createdAt' => 'DESC'] + ); + } +} diff --git a/src/CoreBundle/Resources/views/Layout/head.html.twig b/src/CoreBundle/Resources/views/Layout/head.html.twig index e9e083ffea..ce1b3479f0 100644 --- a/src/CoreBundle/Resources/views/Layout/head.html.twig +++ b/src/CoreBundle/Resources/views/Layout/head.html.twig @@ -30,7 +30,11 @@ {{ encore_entry_link_tags('legacy_document') }} {{ encore_entry_link_tags('vue') }} {{ encore_entry_link_tags('app') }} - + + {% if color_theme_link %} + {{ color_theme_link|raw }} + {% endif %} + {# Files app.css is generated from "assets/css/app.scss" file using the file webpack.config.js #} {# {{ encore_entry_link_tags('app') }} #} {% if theme is defined %} diff --git a/src/CoreBundle/State/ColorThemeProcessor.php b/src/CoreBundle/State/ColorThemeProcessor.php index 19d9446317..77490fb0e1 100644 --- a/src/CoreBundle/State/ColorThemeProcessor.php +++ b/src/CoreBundle/State/ColorThemeProcessor.php @@ -9,6 +9,7 @@ namespace Chamilo\CoreBundle\State; use ApiPlatform\Metadata\Operation; use ApiPlatform\State\ProcessorInterface; use Chamilo\CoreBundle\Entity\ColorTheme; +use Chamilo\CoreBundle\Repository\ColorThemeRepository; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Filesystem\Filesystem; @@ -19,12 +20,18 @@ class ColorThemeProcessor implements ProcessorInterface public function __construct( private readonly ProcessorInterface $persistProcessor, private readonly ParameterBagInterface $parameterBag, + private readonly ColorThemeRepository $colorThemeRepository, ) {} public function process($data, Operation $operation, array $uriVariables = [], array $context = []) { \assert($data instanceof ColorTheme); + $this->colorThemeRepository->deactivateAll(); + + $data->setActive(true); + + /** @var ColorTheme $colorTheme */ $colorTheme = $this->persistProcessor->process($data, $operation, $uriVariables, $context); if ($colorTheme) { @@ -33,16 +40,18 @@ class ColorThemeProcessor implements ProcessorInterface $contentParts = []; $contentParts[] = ':root {'; - foreach ($data->getVariables() as $variable => $value) { + foreach ($colorTheme->getVariables() as $variable => $value) { $contentParts[] = " $variable: $value;"; } $contentParts[] = '}'; + $dirName = $projectDir."/var/theme/{$colorTheme->getSlug()}"; + $fs = new Filesystem(); - $fs->mkdir($projectDir.'/var/theme'); + $fs->mkdir($dirName); $fs->dumpFile( - $projectDir.'/var/theme/colors.css', + $dirName.'/colors.css', implode(PHP_EOL, $contentParts) ); }