From c1f8baf04cefa416e380b42df31f1d5e14d67040 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com> Date: Wed, 26 Jun 2024 12:18:28 -0500 Subject: [PATCH] Admin: Allow to set color theme according the access url - refs BT#21621 --- assets/vue/services/colorThemeService.js | 21 +- .../vue/views/admin/AdminConfigureColors.vue | 778 +++++++++--------- src/CoreBundle/Entity/AccessUrl.php | 12 +- .../Entity/AccessUrlRelColorTheme.php | 21 +- .../Repository/ColorThemeRepository.php | 16 - .../AccessUrlRelColorThemeStateProcessor.php | 45 + .../State/ColorThemeStateProcessor.php | 16 +- 7 files changed, 483 insertions(+), 426 deletions(-) create mode 100644 src/CoreBundle/State/AccessUrlRelColorThemeStateProcessor.php diff --git a/assets/vue/services/colorThemeService.js b/assets/vue/services/colorThemeService.js index e74ee17df90..3f348da1b99 100644 --- a/assets/vue/services/colorThemeService.js +++ b/assets/vue/services/colorThemeService.js @@ -5,12 +5,10 @@ const url = "/api/color_themes" /** * Gets the color themes * - * @returns {Promise} + * @returns {Promise<{totalItems, items}>} */ -async function getThemes() { - const { items } = await baseService.getCollection(url) - - return items +async function findAll() { + return await baseService.getCollection(url) } /** @@ -35,7 +33,18 @@ async function updateTheme({ iri = null, title, colors }) { }) } +/** + * @param {string} iri + * @returns {Promise} + */ +async function changePlatformColorTheme(iri) { + return baseService.post("/api/access_url_rel_color_themes", { + colorTheme: iri, + }) +} + export default { - getThemes, + findAll, updateTheme, + changePlatformColorTheme, } diff --git a/assets/vue/views/admin/AdminConfigureColors.vue b/assets/vue/views/admin/AdminConfigureColors.vue index ed7e0349d34..69e0044b682 100644 --- a/assets/vue/views/admin/AdminConfigureColors.vue +++ b/assets/vue/views/admin/AdminConfigureColors.vue @@ -3,404 +3,416 @@
-
- + - - + - -
-
- - - - -
+ -
- - - -
+
+
+

-
- - -
+ -
- - - -
+ +
+
+ + + + +
-
- - - -
+
+ + + +
-
- - - -
+
+ + +
-
- - -
+
+ + + +
-
- -
-
+
+ + + +
- -
-
- - - -
+
+ + + +
-
- - - - -
+
+ + +
-
- +
+ +
-
- -
- -
-
- -
-

+ +
+
+ + + +
- +
+ + + + +
-
-
{{ t("You can see examples of how chamilo will look here") }}
+
+ +
+
-
-

{{ t("Buttons") }}

-
- +
- -
-
- - + +
- -
-
- -
-
-

{{ t("Dropdowns") }}

-
- - - + + +
+
{{ t("You can see examples of how chamilo will look here") }}
+ +
+

{{ t("Buttons") }}

+
+ + + + +
+
+ + + + +
+
+ + +
-
-
-

{{ t("Checkbox and radio buttons") }}

-
- -
- - +

{{ t("Dropdowns") }}

+
+ + +
-
-
-

{{ t("Toggle") }}

- -
+
+

{{ t("Checkbox and radio buttons") }}

+
+ +
+ + +
+
+
-
-

{{ t("Forms") }}

- - - -
+
+

{{ t("Toggle") }}

+ +
-
-

{{ t("Dialogs") }}

- - -
-
-

{{ t("Some more elements") }}

-
-
@@ -412,7 +424,7 @@ import BaseButton from "../../components/basecomponents/BaseButton.vue" import { useI18n } from "vue-i18n" import BaseMenu from "../../components/basecomponents/BaseMenu.vue" -import { onMounted, provide, ref, watch } from "vue" +import { provide, ref, watch } from "vue" import BaseCheckbox from "../../components/basecomponents/BaseCheckbox.vue" import BaseRadioButtons from "../../components/basecomponents/BaseRadioButtons.vue" import BaseDialogConfirmCancel from "../../components/basecomponents/BaseDialogConfirmCancel.vue" @@ -425,14 +437,17 @@ import BaseInputDate from "../../components/basecomponents/BaseInputDate.vue" import BaseToggleButton from "../../components/basecomponents/BaseToggleButton.vue" import Color from "colorjs.io" import themeService from "../../services/colorThemeService" -import BaseSelect from "../../components/basecomponents/BaseSelect.vue" import BaseDivider from "../../components/basecomponents/BaseDivider.vue" import SectionHeader from "../../components/layout/SectionHeader.vue" +import ColorThemeSelector from "../../components/platform/ColorThemeSelector.vue" +import colorThemeService from "../../services/colorThemeService" const { t } = useI18n() const { getColorTheme, getColors, setColors } = useTheme() const { showSuccessNotification, showErrorNotification } = useNotification() +const themeSelectorEl = ref() + let colorPrimary = getColorTheme("--color-primary-base") let colorPrimaryGradient = getColorTheme("--color-primary-gradient") let colorPrimaryButtonText = getColorTheme("--color-primary-button-text") @@ -464,28 +479,9 @@ let colorDangerButtonText = getColorTheme("--color-danger-button-text") let formColor = getColorTheme("--color-form-base") -const serverThemes = ref([]) -const isServerThemesLoading = ref(true) const themeTitle = ref() const selectedTheme = ref() -onMounted(async () => { - await refreshThemes() -}) - -watch(selectedTheme, (newValue) => { - if (!newValue) { - themeTitle.value = "" - } - - const found = serverThemes.value.find((e) => e["@id"] === newValue) ?? null - - if (found) { - themeTitle.value = found.title - setColors(found.variables) - } -}) - const saveColors = async () => { try { const updatedTheme = await themeService.updateTheme({ @@ -496,7 +492,7 @@ const saveColors = async () => { showSuccessNotification(t("Color updated")) - await refreshThemes() + await themeSelectorEl.value.loadThemes() selectedTheme.value = updatedTheme["@id"] } catch (error) { @@ -505,20 +501,6 @@ const saveColors = async () => { } } -const refreshThemes = async () => { - try { - serverThemes.value = await themeService.getThemes() - const found = serverThemes.value.find((e) => e.active) ?? null - if (found) { - selectedTheme.value = found["@id"] - } - isServerThemesLoading.value = false - } catch (error) { - showErrorNotification(t("We could not retrieve the themes")) - console.error(error) - } -} - const isAdvancedMode = ref(false) watch(colorPrimary, (newValue) => { @@ -687,4 +669,10 @@ const isSorting = ref(false) const isCustomizing = ref(false) provide("isSorting", isSorting) provide("isCustomizing", isCustomizing) + +async function onClickChangeColorTheme() { + if (selectedTheme.value) { + await colorThemeService.changePlatformColorTheme(selectedTheme.value) + } +} diff --git a/src/CoreBundle/Entity/AccessUrl.php b/src/CoreBundle/Entity/AccessUrl.php index 08ff589c3a9..293ff09865e 100644 --- a/src/CoreBundle/Entity/AccessUrl.php +++ b/src/CoreBundle/Entity/AccessUrl.php @@ -150,7 +150,7 @@ class AccessUrl extends AbstractResource implements ResourceInterface, Stringabl /** * @var Collection */ - #[ORM\OneToMany(mappedBy: 'url', targetEntity: AccessUrlRelColorTheme::class, orphanRemoval: true)] + #[ORM\OneToMany(mappedBy: 'url', targetEntity: AccessUrlRelColorTheme::class, cascade: ['persist'], orphanRemoval: true)] private Collection $colorThemes; public function __construct() @@ -537,4 +537,14 @@ public function removeColorTheme(AccessUrlRelColorTheme $colorTheme): static return $this; } + + public function getColorThemeByTheme(ColorTheme $theme): ?AccessUrlRelColorTheme + { + $criteria = Criteria::create(); + $criteria->where( + Criteria::expr()->eq('colorTheme', $theme) + ); + + return $this->colorThemes->matching($criteria)->first() ?: null; + } } diff --git a/src/CoreBundle/Entity/AccessUrlRelColorTheme.php b/src/CoreBundle/Entity/AccessUrlRelColorTheme.php index dee16da031a..de59cc32105 100644 --- a/src/CoreBundle/Entity/AccessUrlRelColorTheme.php +++ b/src/CoreBundle/Entity/AccessUrlRelColorTheme.php @@ -6,10 +6,24 @@ namespace Chamilo\CoreBundle\Entity; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Post; use Chamilo\CoreBundle\Repository\AccessUrlRelColorThemeRepository; +use Chamilo\CoreBundle\State\AccessUrlRelColorThemeStateProcessor; use Doctrine\ORM\Mapping as ORM; use Gedmo\Timestampable\Traits\TimestampableEntity; - +use Symfony\Component\Serializer\Attribute\Groups; + +#[ApiResource( + operations: [ + new Post(), + ], + denormalizationContext: [ + 'groups' => ['access_url_rel_color_theme:write'], + ], + security: "is_granted('ROLE_ADMIN')", + processor: AccessUrlRelColorThemeStateProcessor::class, +)] #[ORM\Entity(repositoryClass: AccessUrlRelColorThemeRepository::class)] final class AccessUrlRelColorTheme { @@ -24,12 +38,13 @@ final class AccessUrlRelColorTheme #[ORM\JoinColumn(nullable: false)] private ?AccessUrl $url = null; + #[Groups(['access_url_rel_color_theme:write'])] #[ORM\ManyToOne(inversedBy: 'urls')] #[ORM\JoinColumn(nullable: false)] private ?ColorTheme $colorTheme = null; #[ORM\Column] - private ?bool $active = null; + private bool $active = false; public function getId(): ?int { @@ -60,7 +75,7 @@ public function setColorTheme(?ColorTheme $colorTheme): static return $this; } - public function isActive(): ?bool + public function isActive(): bool { return $this->active; } diff --git a/src/CoreBundle/Repository/ColorThemeRepository.php b/src/CoreBundle/Repository/ColorThemeRepository.php index 80d6d85fc8b..12ceed45d71 100644 --- a/src/CoreBundle/Repository/ColorThemeRepository.php +++ b/src/CoreBundle/Repository/ColorThemeRepository.php @@ -14,20 +14,4 @@ public function __construct(ManagerRegistry $registry) { parent::__construct($registry, ColorTheme::class); } - - public function deactivateAllExcept(ColorTheme $colorTheme): void - { - $this->getEntityManager() - ->createQuery('UPDATE Chamilo\CoreBundle\Entity\ColorTheme t SET t.active = FALSE WHERE t.id <> :id') - ->execute(['id' => $colorTheme->getId()]) - ; - } - - public function getActiveOne(): ?ColorTheme - { - return $this->findOneBy( - ['active' => true], - ['createdAt' => 'DESC'] - ); - } } diff --git a/src/CoreBundle/State/AccessUrlRelColorThemeStateProcessor.php b/src/CoreBundle/State/AccessUrlRelColorThemeStateProcessor.php new file mode 100644 index 00000000000..4be89c45278 --- /dev/null +++ b/src/CoreBundle/State/AccessUrlRelColorThemeStateProcessor.php @@ -0,0 +1,45 @@ +accessUrlHelper->getCurrent(); + $accessUrl->getActiveColorTheme()?->setActive(false); + + $accessUrlRelColorTheme = $accessUrl->getColorThemeByTheme($data->getColorTheme()); + + if ($accessUrlRelColorTheme) { + $accessUrlRelColorTheme->setActive(true); + } else { + $data->setActive(true); + + $accessUrl->addColorTheme($data); + + $accessUrlRelColorTheme = $data; + } + + $this->entityManager->flush(); + + return $accessUrlRelColorTheme; + } +} diff --git a/src/CoreBundle/State/ColorThemeStateProcessor.php b/src/CoreBundle/State/ColorThemeStateProcessor.php index e0a6473c8e8..e78ba3dcd2c 100644 --- a/src/CoreBundle/State/ColorThemeStateProcessor.php +++ b/src/CoreBundle/State/ColorThemeStateProcessor.php @@ -8,8 +8,11 @@ use ApiPlatform\Metadata\Operation; use ApiPlatform\State\ProcessorInterface; +use Chamilo\CoreBundle\Entity\AccessUrlRelColorTheme; use Chamilo\CoreBundle\Entity\ColorTheme; use Chamilo\CoreBundle\Repository\ColorThemeRepository; +use Chamilo\CoreBundle\ServiceHelper\AccessUrlHelper; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Filesystem\Filesystem; @@ -20,20 +23,23 @@ final class ColorThemeStateProcessor implements ProcessorInterface public function __construct( private readonly ProcessorInterface $persistProcessor, private readonly ParameterBagInterface $parameterBag, - private readonly ColorThemeRepository $colorThemeRepository, + private readonly AccessUrlHelper $accessUrlHelper, + private readonly EntityManagerInterface $entityManager, ) {} public function process($data, Operation $operation, array $uriVariables = [], array $context = []) { \assert($data instanceof ColorTheme); - $data->setActive(true); - /** @var ColorTheme $colorTheme */ $colorTheme = $this->persistProcessor->process($data, $operation, $uriVariables, $context); if ($colorTheme) { - $this->colorThemeRepository->deactivateAllExcept($colorTheme); + $accessUrlRelColorTheme = (new AccessUrlRelColorTheme())->setColorTheme($colorTheme); + + $this->accessUrlHelper->getCurrent()->addColorTheme($accessUrlRelColorTheme); + + $this->entityManager->flush(); $projectDir = $this->parameterBag->get('kernel.project_dir'); @@ -46,7 +52,7 @@ public function process($data, Operation $operation, array $uriVariables = [], a $contentParts[] = '}'; - $dirName = $projectDir."/var/theme/{$colorTheme->getSlug()}"; + $dirName = $projectDir."/var/themes/{$colorTheme->getSlug()}"; $fs = new Filesystem(); $fs->mkdir($dirName);