Internal: Admin: Adding title and active fields to color theme #3793

pull/5266/head
Angel Fernando Quiroz Campos 2 years ago
parent 91eeac4021
commit b1afcb0251
  1. 10
      assets/vue/views/admin/AdminConfigureColors.vue
  2. 20
      src/CoreBundle/Controller/ThemeController.php
  3. 49
      src/CoreBundle/Entity/ColorTheme.php
  4. 23
      src/CoreBundle/EventListener/TwigListener.php
  5. 2
      src/CoreBundle/Migrations/Schema/V200/Version20240318105600.php
  6. 40
      src/CoreBundle/Repository/ColorThemeRepository.php
  7. 6
      src/CoreBundle/Resources/views/Layout/head.html.twig
  8. 15
      src/CoreBundle/State/ColorThemeProcessor.php

@ -40,7 +40,12 @@
/>
</div>
<div class="flex flex-wrap mb-4">
<div class="flex flex-wrap mb-4 gap-4">
<BaseInputText
v-model="themeTitle"
:label="t('Title')"
/>
<BaseButton
type="primary"
icon="send"
@ -182,6 +187,8 @@ import axios from "axios"
const { t } = useI18n()
const { getColorTheme, getColors } = useTheme()
const themeTitle = ref()
let primaryColor = getColorTheme("--color-primary-base")
let primaryColorGradient = getColorTheme("--color-primary-gradient")
let secondaryColor = getColorTheme("--color-secondary-base")
@ -196,6 +203,7 @@ const saveColors = async () => {
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,
})
}

@ -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']);

@ -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<string, mixed>
*/
@ -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;
}
}

@ -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 = '<link rel="stylesheet" href="'.$path.'">';
}
$this->twig->addGlobal('color_theme_link', $link);
}
}

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

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace Chamilo\CoreBundle\Repository;
use Chamilo\CoreBundle\Entity\ColorTheme;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
class ColorThemeRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ColorTheme::class);
}
public function deactivateAll(): void
{
$qb = $this->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']
);
}
}

@ -30,7 +30,11 @@
{{ encore_entry_link_tags('legacy_document') }}
{{ encore_entry_link_tags('vue') }}
{{ encore_entry_link_tags('app') }}
<link rel="stylesheet" href="{{ path('chamilo_color_theme') }}">
{% 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 %}

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

Loading…
Cancel
Save