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)
);
}