From c2b8ed6b9e34666f94e7527bbe302199eec34dd8 Mon Sep 17 00:00:00 2001 From: christianbeeznst Date: Tue, 19 Mar 2024 11:24:14 -0500 Subject: [PATCH 01/15] Internal: Minor update: Add redirect response on NotAllowedException --- src/CoreBundle/EventListener/ExceptionListener.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/CoreBundle/EventListener/ExceptionListener.php b/src/CoreBundle/EventListener/ExceptionListener.php index 63b01a1a94..7425f486d1 100644 --- a/src/CoreBundle/EventListener/ExceptionListener.php +++ b/src/CoreBundle/EventListener/ExceptionListener.php @@ -8,6 +8,7 @@ namespace Chamilo\CoreBundle\EventListener; use Chamilo\CoreBundle\Component\Utils\ChamiloApi; use Chamilo\CoreBundle\Exception\NotAllowedException; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\ExceptionEvent; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; @@ -44,7 +45,8 @@ class ExceptionListener $redirectUrl = $baseUrl.$path.($query ? '?'.$query : ''); $loginUrl = $this->router->generate('login', ['redirect' => $redirectUrl], UrlGeneratorInterface::ABSOLUTE_URL); - ChamiloApi::redirectTo($loginUrl); + $event->setResponse(new RedirectResponse($loginUrl)); + return; } } From 41c1f58f56b46df0e782e617f658fef3163a8c60 Mon Sep 17 00:00:00 2001 From: christianbeeznst Date: Tue, 19 Mar 2024 14:35:31 -0500 Subject: [PATCH 02/15] Internal: Minor: Adjust sidebar behavior --- assets/vue/components/layout/Sidebar.vue | 39 +++++++++++++++++++----- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/assets/vue/components/layout/Sidebar.vue b/assets/vue/components/layout/Sidebar.vue index 5d9b17ca12..db7feee05e 100644 --- a/assets/vue/components/layout/Sidebar.vue +++ b/assets/vue/components/layout/Sidebar.vue @@ -5,7 +5,7 @@ {{ t("Menu") }}
- +
@@ -55,6 +55,7 @@ const securityStore = useSecurityStore() const { menuItems } = useSidebarMenu() const sidebarIsOpen = ref(window.localStorage.getItem("sidebarIsOpen") === "true") +const expandingDueToPanelClick = ref(false) const currentYear = new Date().getFullYear(); @@ -62,13 +63,35 @@ watch( sidebarIsOpen, (newValue) => { const appEl = document.querySelector("#app") - window.localStorage.setItem("sidebarIsOpen", newValue.toString()) - appEl.classList.toggle("app--sidebar-inactive", !newValue) - }, - { - immediate: true, - }, -) + + if (!newValue) { + if (!expandingDueToPanelClick.value) { + const expandedHeaders = document.querySelectorAll('.p-panelmenu-header.p-highlight') + expandedHeaders.forEach(header => { + header.click() + }) + sidebarIsOpen.value = false + window.localStorage.setItem("sidebarIsOpen", 'false') + } + } + expandingDueToPanelClick.value = false +}, { immediate: true }) + +const handlePanelHeaderClick = (event) => { + const header = event.target.closest('.p-panelmenu-header') + if (!header) return + + const contentId = header.getAttribute('aria-controls') + const contentPanel = document.getElementById(contentId) + + if (contentPanel && contentPanel.querySelector('.p-toggleable-content')) { + if (!sidebarIsOpen.value) { + expandingDueToPanelClick.value = true + sidebarIsOpen.value = true + window.localStorage.setItem("sidebarIsOpen", 'true') + } + } +} From 91eeac4021b13e6e1ebb850382b858719bdc51b9 Mon Sep 17 00:00:00 2001 From: NicoDucou Date: Tue, 19 Mar 2024 21:07:41 +0100 Subject: [PATCH 03/15] Migrations: put foreign key removal before key modification to fix migration process --- .../Migrations/Schema/V200/Version20200821224242.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20200821224242.php b/src/CoreBundle/Migrations/Schema/V200/Version20200821224242.php index 2a3ddac61e..2e8b1c24d0 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20200821224242.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20200821224242.php @@ -19,6 +19,12 @@ final class Version20200821224242 extends AbstractMigrationChamilo public function up(Schema $schema): void { $connection = $this->getEntityManager()->getConnection(); + if ($schema->hasTable('message_feedback')) { + $table = $schema->getTable('message_feedback'); + if ($table->hasForeignKey('FK_DB0F8049537A1329')) { + $this->addSql('ALTER TABLE message_feedback DROP FOREIGN KEY FK_DB0F8049537A1329'); + } + } $table = $schema->getTable('message'); $this->addSql('ALTER TABLE message CHANGE id id INT AUTO_INCREMENT NOT NULL, CHANGE parent_id parent_id INT DEFAULT NULL;'); @@ -207,9 +213,6 @@ final class Version20200821224242 extends AbstractMigrationChamilo $table = $schema->getTable('message_feedback'); - if ($table->hasForeignKey('FK_DB0F8049537A1329')) { - $this->addSql('ALTER TABLE message_feedback DROP FOREIGN KEY FK_DB0F8049537A1329'); - } if ($table->hasIndex('IDX_DB0F8049537A1329')) { $this->addSql('DROP INDEX IDX_DB0F8049537A1329 ON message_feedback'); } 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 04/15] 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) ); } From 007514dd6637046ffd70bd548cb4bf6392da785c Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Tue, 19 Mar 2024 18:16:33 -0500 Subject: [PATCH 05/15] Calendar: allow to subscribe/unsubscribe to personal events + improve info about invitations/subscriptions --- assets/css/scss/_calendar.scss | 19 +++++ assets/css/scss/index.scss | 2 +- .../ccalendarevent/CCalendarEventInfo.vue | 25 ++++--- .../CalendarEventInvitationsInfo.vue | 32 +++++++++ .../CalendarEventSubscriptionsInfo.vue | 69 +++++++++++++++++++ .../vue/composables/calendar/calendarEvent.js | 38 +++++++++- assets/vue/services/baseService.js | 6 +- assets/vue/services/resourceLinkService.js | 5 ++ .../ccalendarevent/CCalendarEventList.vue | 41 ++++++++++- src/CoreBundle/ApiResource/CalendarEvent.php | 2 + .../CalendarEventTransformer.php | 9 +++ src/CoreBundle/Entity/ResourceLink.php | 1 + src/CoreBundle/Entity/ResourceNode.php | 6 +- 13 files changed, 237 insertions(+), 18 deletions(-) create mode 100644 assets/css/scss/_calendar.scss create mode 100644 assets/vue/components/ccalendarevent/CalendarEventInvitationsInfo.vue create mode 100644 assets/vue/components/ccalendarevent/CalendarEventSubscriptionsInfo.vue create mode 100644 assets/vue/services/resourceLinkService.js diff --git a/assets/css/scss/_calendar.scss b/assets/css/scss/_calendar.scss new file mode 100644 index 0000000000..827430ff43 --- /dev/null +++ b/assets/css/scss/_calendar.scss @@ -0,0 +1,19 @@ +.calendar-event-info { + @apply flex flex-col space-y-4; + + .invitations-info { + @apply space-y-2; + + &__title { + @apply text-gray-50 mb-3; + } + + &__item { + @apply flex text-body-2 flex-row justify-between; + + p { + @apply first:font-semibold; + } + } + } +} diff --git a/assets/css/scss/index.scss b/assets/css/scss/index.scss index 84f92630e9..578d7eb90c 100755 --- a/assets/css/scss/index.scss +++ b/assets/css/scss/index.scss @@ -59,7 +59,7 @@ @import 'layout/main_container'; @import "admin_index"; - +@import "calendar"; @import "course_home"; @import "documents"; diff --git a/assets/vue/components/ccalendarevent/CCalendarEventInfo.vue b/assets/vue/components/ccalendarevent/CCalendarEventInfo.vue index 63120f2b72..ff3a7dfdd4 100644 --- a/assets/vue/components/ccalendarevent/CCalendarEventInfo.vue +++ b/assets/vue/components/ccalendarevent/CCalendarEventInfo.vue @@ -1,5 +1,5 @@ @@ -29,6 +34,8 @@ import { useFormatDate } from "../../composables/formatDate" import ShowLinks from "../resource_links/ShowLinks" import { useCalendarInvitations } from "../../composables/calendar/calendarInvitations" import { type } from "../../constants/entity/ccalendarevent" +import CalendarEventSubscriptionsInfo from "./CalendarEventSubscriptionsInfo.vue" +import CalendarEventInvitationsInfo from "./CalendarEventInvitationsInfo.vue" const { abbreviatedDatetime } = useFormatDate() const { allowCollectiveInvitations } = useCalendarInvitations() diff --git a/assets/vue/components/ccalendarevent/CalendarEventInvitationsInfo.vue b/assets/vue/components/ccalendarevent/CalendarEventInvitationsInfo.vue new file mode 100644 index 0000000000..469f0b9d40 --- /dev/null +++ b/assets/vue/components/ccalendarevent/CalendarEventInvitationsInfo.vue @@ -0,0 +1,32 @@ + + + diff --git a/assets/vue/components/ccalendarevent/CalendarEventSubscriptionsInfo.vue b/assets/vue/components/ccalendarevent/CalendarEventSubscriptionsInfo.vue new file mode 100644 index 0000000000..ed38d5e315 --- /dev/null +++ b/assets/vue/components/ccalendarevent/CalendarEventSubscriptionsInfo.vue @@ -0,0 +1,69 @@ + + + diff --git a/assets/vue/composables/calendar/calendarEvent.js b/assets/vue/composables/calendar/calendarEvent.js index 609a3bddba..d6b0e42e67 100644 --- a/assets/vue/composables/calendar/calendarEvent.js +++ b/assets/vue/composables/calendar/calendarEvent.js @@ -1,9 +1,13 @@ -import { type } from "../../constants/entity/ccalendarevent" +import { type, subscriptionVisibility } from "../../constants/entity/ccalendarevent" export function useCalendarEvent() { return { findUserLink, isEditableByUser, + isSubscribeable, + canSubscribeToEvent, + allowSubscribeToEvent, + allowUnsubscribeToEvent, } } @@ -36,3 +40,35 @@ function isEditableByUser(event, userId) { return false } + +function isSubscribeable(event) { + if (type.subscription !== event.invitationType) { + return false + } + + return subscriptionVisibility.no !== event.subscriptionVisibility +} + +/** + * @param {Object} event + * @returns {boolean} + */ +function canSubscribeToEvent(event) { + return event.resourceLinkListFromEntity.length < event.maxAttendees || 0 === event.maxAttendees +} + +function allowSubscribeToEvent(event) { + if (!isSubscribeable(event)) { + return false + } + + return canSubscribeToEvent(event) +} + +function allowUnsubscribeToEvent(event, userId) { + if (!isSubscribeable(event)) { + return false + } + + return !!findUserLink(event, userId) +} diff --git a/assets/vue/services/baseService.js b/assets/vue/services/baseService.js index d6052d443b..3dc7f2922b 100644 --- a/assets/vue/services/baseService.js +++ b/assets/vue/services/baseService.js @@ -8,6 +8,8 @@ async function find(iri) { return await api.get(iri) } -export { - find +async function post(params) { + return await api.post("/api/resource_links", params) } + +export { find, post } diff --git a/assets/vue/services/resourceLinkService.js b/assets/vue/services/resourceLinkService.js new file mode 100644 index 0000000000..0efa94e9c2 --- /dev/null +++ b/assets/vue/services/resourceLinkService.js @@ -0,0 +1,5 @@ +import { post } from "./baseService" + +export default { + post, +} diff --git a/assets/vue/views/ccalendarevent/CCalendarEventList.vue b/assets/vue/views/ccalendarevent/CCalendarEventList.vue index 2f69a74635..79b5a70d02 100644 --- a/assets/vue/views/ccalendarevent/CCalendarEventList.vue +++ b/assets/vue/views/ccalendarevent/CCalendarEventList.vue @@ -44,6 +44,7 @@ v-model:visible="dialogShow" :header="t('Event')" :modal="true" + :style="{ width: '30rem' }" > @@ -54,6 +55,22 @@ type="black" @click="dialogShow = false" /> + + + + store.getters["security/getUser"]) const { t } = useI18n() @@ -248,6 +268,8 @@ const calendarOptions = ref({ item.value["parentResourceNodeId"] = event.extendedProps.resourceNode.creator.id allowToEdit.value = isEditableByUser(item.value, currentUser.value.id) + allowToSubscribe.value = !allowToEdit.value && allowSubscribeToEvent(item.value) + allowToUnsubscribe.value = !allowToEdit.value && allowUnsubscribeToEvent(item.value, currentUser.value.id) dialogShow.value = true }, @@ -309,6 +331,23 @@ function confirmDelete() { }) } +async function subscribeToEvent() { + try { + await resourceLinkService.post({ + resourceNode: item.value.resourceNode["@id"], + user: currentUser.value["@id"], + visibility: RESOURCE_LINK_PUBLISHED, + }) + + allowToSubscribe.value = false + allowToUnsubscribe.value = true + } catch (e) { + console.error(e) + } +} + +async function unsubscribeToEvent() {} + const isLoading = computed(() => store.getters["ccalendarevent/isLoading"]) const createForm = ref(null) diff --git a/src/CoreBundle/ApiResource/CalendarEvent.php b/src/CoreBundle/ApiResource/CalendarEvent.php index 3ed873158a..0d235f2e98 100644 --- a/src/CoreBundle/ApiResource/CalendarEvent.php +++ b/src/CoreBundle/ApiResource/CalendarEvent.php @@ -37,6 +37,8 @@ class CalendarEvent extends AbstractResource #[Groups(['calendar_event:read'])] public ?int $subscriptionItemId = null, #[Groups(['calendar_event:read'])] + public ?string $subscriptionItemTitle = null, + #[Groups(['calendar_event:read'])] public int $maxAttendees = 0, #[Groups(['calendar_event:read'])] public ?ResourceNode $resourceNode = null, diff --git a/src/CoreBundle/DataTransformer/CalendarEventTransformer.php b/src/CoreBundle/DataTransformer/CalendarEventTransformer.php index 84ca91694b..e6145cf33c 100644 --- a/src/CoreBundle/DataTransformer/CalendarEventTransformer.php +++ b/src/CoreBundle/DataTransformer/CalendarEventTransformer.php @@ -10,6 +10,7 @@ use ApiPlatform\Core\DataTransformer\DataTransformerInterface; use Chamilo\CoreBundle\ApiResource\CalendarEvent; use Chamilo\CoreBundle\Entity\Session; use Chamilo\CoreBundle\Entity\SessionRelCourse; +use Chamilo\CoreBundle\Repository\Node\UsergroupRepository; use Chamilo\CourseBundle\Entity\CCalendarEvent; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RouterInterface; @@ -18,6 +19,7 @@ class CalendarEventTransformer implements DataTransformerInterface { public function __construct( private readonly RouterInterface $router, + private readonly UsergroupRepository $usergroupRepository, ) {} public function transform($object, string $to, array $context = []): object @@ -40,6 +42,12 @@ class CalendarEventTransformer implements DataTransformerInterface $object->setResourceLinkListFromEntity(); + $subscriptionItemTitle = null; + + if (CCalendarEvent::SUBSCRIPTION_VISIBILITY_CLASS == $object->getSubscriptionVisibility()) { + $subscriptionItemTitle = $this->usergroupRepository->find($object->getSubscriptionItemId())?->getTitle(); + } + return new CalendarEvent( 'calendar_event_'.$object->getIid(), $object->getTitle(), @@ -52,6 +60,7 @@ class CalendarEventTransformer implements DataTransformerInterface $object->isCollective(), $object->getSubscriptionVisibility(), $object->getSubscriptionItemId(), + $subscriptionItemTitle, $object->getMaxAttendees(), $object->getResourceNode(), $object->getResourceLinkListFromEntity(), diff --git a/src/CoreBundle/Entity/ResourceLink.php b/src/CoreBundle/Entity/ResourceLink.php index 6c5c7071c5..f90f6e4a3c 100644 --- a/src/CoreBundle/Entity/ResourceLink.php +++ b/src/CoreBundle/Entity/ResourceLink.php @@ -259,6 +259,7 @@ class ResourceLink implements Stringable public function setResourceNode(ResourceNode $resourceNode): self { $this->resourceNode = $resourceNode; + $this->resourceTypeGroup = $resourceNode->getResourceType()->getId(); return $this; } diff --git a/src/CoreBundle/Entity/ResourceNode.php b/src/CoreBundle/Entity/ResourceNode.php index 16d89024fe..94dff8d0b1 100644 --- a/src/CoreBundle/Entity/ResourceNode.php +++ b/src/CoreBundle/Entity/ResourceNode.php @@ -469,10 +469,8 @@ class ResourceNode implements Stringable public function addResourceLink(ResourceLink $link): self { - $link - ->setResourceNode($this) - ->setResourceTypeGroup($this->resourceType->getId()) - ; + $link->setResourceNode($this); + $this->resourceLinks->add($link); return $this; From 2f7a0a183ad84d672ca2eee483b364bb4d1a2fb4 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Tue, 19 Mar 2024 18:19:26 -0500 Subject: [PATCH 06/15] Internal: Clean session handler about cidReq context --- src/CoreBundle/EventListener/CourseListener.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/CoreBundle/EventListener/CourseListener.php b/src/CoreBundle/EventListener/CourseListener.php index e76266259b..643d708c9d 100644 --- a/src/CoreBundle/EventListener/CourseListener.php +++ b/src/CoreBundle/EventListener/CourseListener.php @@ -73,7 +73,7 @@ class CourseListener } if (true === $cidReset) { - $this->removeCourseFromSession($request); + $this->cleanSessionHandler($request); return; } @@ -190,6 +190,8 @@ class CourseListener $courseParams = $this->generateCourseUrl($course, $sessionId, $groupId, $origin); $sessionHandler->set('course_url_params', $courseParams); $twig->addGlobal('course_url_params', $courseParams); + } else { + $this->cleanSessionHandler($request); } } @@ -268,7 +270,7 @@ class CourseListener } } - public function removeCourseFromSession(Request $request): void + public function cleanSessionHandler(Request $request): void { $sessionHandler = $request->getSession(); $alreadyVisited = $sessionHandler->get('course_already_visited'); From 36f0e278c2c6b320c5c84fb6eb67a6ba21ec76b4 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Wed, 20 Mar 2024 05:15:24 -0500 Subject: [PATCH 07/15] Internal: Fix migrations about ticket to create exercise_id and lp_id columns --- .../Migrations/Schema/V200/Version20211005153900.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20211005153900.php b/src/CoreBundle/Migrations/Schema/V200/Version20211005153900.php index 331dc026a4..2952effba3 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20211005153900.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20211005153900.php @@ -46,6 +46,14 @@ class Version20211005153900 extends AbstractMigrationChamilo $this->addSql('CREATE INDEX IDX_EDE2C7686219A7B7 ON ticket_ticket (assigned_last_user)'); } + if (!$table->hasColumn('exercise_id')) { + $this->addSql("ALTER TABLE ticket_ticket ADD exercise_id INT DEFAULT NULL"); + } + + if (!$table->hasColumn('lp_id')) { + $this->addSql("ALTER TABLE ticket_ticket ADD lp_id INT DEFAULT NULL"); + } + $table = $schema->getTable('ticket_assigned_log'); if (!$table->hasForeignKey('FK_54B65868700047D2')) { $this->addSql('ALTER TABLE ticket_assigned_log ADD CONSTRAINT FK_54B65868700047D2 FOREIGN KEY (ticket_id) REFERENCES ticket_ticket (id) ON DELETE CASCADE;'); From bcd783e3afb5cd86db50c54c44a53a9bca4a6f51 Mon Sep 17 00:00:00 2001 From: NicoDucou Date: Wed, 20 Mar 2024 13:20:01 +0100 Subject: [PATCH 08/15] Migrations: fix case in Version20201215072918.php where event doesnt have a c_item_property and we need to create it to avoid error with no editdate --- src/CoreBundle/Migrations/AbstractMigrationChamilo.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/CoreBundle/Migrations/AbstractMigrationChamilo.php b/src/CoreBundle/Migrations/AbstractMigrationChamilo.php index 2933c3e7c2..443e848d10 100644 --- a/src/CoreBundle/Migrations/AbstractMigrationChamilo.php +++ b/src/CoreBundle/Migrations/AbstractMigrationChamilo.php @@ -264,8 +264,11 @@ abstract class AbstractMigrationChamilo extends AbstractMigration implements Con $userId = (int) $item['insert_user_id']; $sessionId = $item['session_id'] ?? 0; $groupId = $item['to_group_id'] ?? 0; - $lastUpdatedAt = new DateTime($item['lastedit_date'], new DateTimeZone('UTC')); - + if (empty($item['lastedit_date'])) { + $lastUpdatedAt = new DateTime('now', new DateTimeZone('UTC')); + } else { + $lastUpdatedAt = new DateTime($item['lastedit_date'], new DateTimeZone('UTC')); + } $newVisibility = ResourceLink::VISIBILITY_DRAFT; // Old 1.11.x visibility (item property) is based in this switch: From e9d7183bceb8d1a29e3197938b52211961113525 Mon Sep 17 00:00:00 2001 From: christianbeeznst Date: Wed, 20 Mar 2024 10:08:36 -0500 Subject: [PATCH 09/15] Migration: Update path for translation import --- .../Migrations/Schema/V200/Version20240122221400.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20240122221400.php b/src/CoreBundle/Migrations/Schema/V200/Version20240122221400.php index bacb5c63e6..14a20bc235 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20240122221400.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20240122221400.php @@ -88,9 +88,9 @@ final class Version20240122221400 extends AbstractMigrationChamilo $kernel = $container->get('kernel'); $rootPath = $kernel->getProjectDir(); - $langPath = $rootPath.'/public/main/lang/'.$englishName.'/trad4all.inc.php'; + $langPath = $rootPath.'/var/translations/import/'.$englishName.'/trad4all.inc.php'; $destinationFilePath = $rootPath.'/var/translations/messages.'.$isocode.'.po'; - $originalFile = $rootPath.'/public/main/lang/english/trad4all.inc.php'; + $originalFile = $rootPath.'/var/translations/import/english/trad4all.inc.php'; if (!file_exists($langPath)) { error_log("Original file not found: $langPath"); @@ -118,6 +118,8 @@ final class Version20240122221400 extends AbstractMigrationChamilo $langPath, true ); + + $termsInLanguage = []; foreach ($originalTermsInLanguage as $id => $content) { if (!isset($termsInLanguage[$id])) { $termsInLanguage[$id] = trim(rtrim($content, ';'), '"'); @@ -131,10 +133,8 @@ final class Version20240122221400 extends AbstractMigrationChamilo continue; } $doneTranslations[$englishTranslation] = true; - $translatedTerm = ''; - if (!empty($termsInLanguage[$term])) { - $translatedTerm = $termsInLanguage[$term]; - } + $translatedTerm = $termsInLanguage[$term] ?? ''; + // Here we apply a little correction to avoid unterminated strings // when a string ends with a \" if (preg_match('/\\\$/', $englishTranslation)) { From 853555aa623d9fa989bf3d152cac08ee83772141 Mon Sep 17 00:00:00 2001 From: christianbeeznst Date: Wed, 20 Mar 2024 10:27:37 -0500 Subject: [PATCH 10/15] Internal: Minor: Fix deprecated event --- assets/vue/components/layout/Sidebar.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/vue/components/layout/Sidebar.vue b/assets/vue/components/layout/Sidebar.vue index db7feee05e..9c5581c209 100644 --- a/assets/vue/components/layout/Sidebar.vue +++ b/assets/vue/components/layout/Sidebar.vue @@ -4,8 +4,8 @@

{{ t("Menu") }}

-
- +
+
From 87cad8c323ee4d5edd1906c1c1d2463fe6284578 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Wed, 20 Mar 2024 10:34:34 -0500 Subject: [PATCH 11/15] LP: Add cidReq to load scorm_api.php in lp view --- public/main/lp/lp_view.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/main/lp/lp_view.php b/public/main/lp/lp_view.php index ebeacf8be2..dd24baf76f 100644 --- a/public/main/lp/lp_view.php +++ b/public/main/lp/lp_view.php @@ -197,7 +197,7 @@ if (!isset($src)) { switch ($lpType) { case CLp::LP_TYPE: $oLP->stop_previous_item(); - $htmlHeadXtra[] = ''; + $htmlHeadXtra[] = ''; $preReqCheck = $oLP->prerequisites_match($lp_item_id); if (true === $preReqCheck) { @@ -239,7 +239,7 @@ if (!isset($src)) { case CLp::SCORM_TYPE: // save old if asset $oLP->stop_previous_item(); // save status manually if asset - $htmlHeadXtra[] = ''; + $htmlHeadXtra[] = ''; $preReqCheck = $oLP->prerequisites_match($lp_item_id); if (true === $preReqCheck) { From 91296bb940ae7b05843cf988e9a8814bce69c894 Mon Sep 17 00:00:00 2001 From: christianbeeznst Date: Wed, 20 Mar 2024 11:08:51 -0500 Subject: [PATCH 12/15] LP: Fix layout for lp view --- assets/vue/App.vue | 2 +- .../vue/components/layout/DashboardLayout.vue | 12 +++++------ .../Resources/views/Index/vue.html.twig | 20 ++++--------------- .../views/Layout/no_layout.html.twig | 5 ----- 4 files changed, 11 insertions(+), 28 deletions(-) diff --git a/assets/vue/App.vue b/assets/vue/App.vue index a424fa5523..a1e6bf7d82 100644 --- a/assets/vue/App.vue +++ b/assets/vue/App.vue @@ -79,7 +79,7 @@ const layout = computed(() => { const queryParams = new URLSearchParams(window.location.search) - if (queryParams.has("lp") || (queryParams.has("origin") && "learnpath" === queryParams.get("origin"))) { + if (queryParams.has("lp_id") || (queryParams.has("origin") && "learnpath" === queryParams.get("origin"))) { return "EmptyLayout" } diff --git a/assets/vue/components/layout/DashboardLayout.vue b/assets/vue/components/layout/DashboardLayout.vue index 5563058d6a..5d0769a699 100644 --- a/assets/vue/components/layout/DashboardLayout.vue +++ b/assets/vue/components/layout/DashboardLayout.vue @@ -1,7 +1,10 @@ diff --git a/src/CoreBundle/Resources/views/Index/vue.html.twig b/src/CoreBundle/Resources/views/Index/vue.html.twig index d2a5981cb4..0502d8bbd9 100644 --- a/src/CoreBundle/Resources/views/Index/vue.html.twig +++ b/src/CoreBundle/Resources/views/Index/vue.html.twig @@ -1,18 +1,6 @@ -{% extends "@ChamiloCore/Layout/base-layout.html.twig" %} -{% block chamilo_wrap %} - {%- autoescape %} - {% if not from_vue %} -
- {% endif %} - {% endautoescape -%} - {% autoescape false %} -
- {%- block content %} - {% include '@ChamiloCore/Layout/vue_setup.html.twig' %} - {% endblock -%} -
- {% endautoescape %} -{% endblock %} +{% extends "@ChamiloCore/Layout/no_layout.html.twig" %} -{% block chamilo_footer %} +{%- block content %} + {% include '@ChamiloCore/Layout/vue_setup.html.twig' %} + {# {{ encore_entry_script_tags('vue') }}#} {% endblock %} diff --git a/src/CoreBundle/Resources/views/Layout/no_layout.html.twig b/src/CoreBundle/Resources/views/Layout/no_layout.html.twig index 5a645357a7..389cce5758 100644 --- a/src/CoreBundle/Resources/views/Layout/no_layout.html.twig +++ b/src/CoreBundle/Resources/views/Layout/no_layout.html.twig @@ -13,11 +13,6 @@ {% endblock -%} {% endautoescape %} - {% endblock %} {% block chamilo_footer %} From 2580ebb364fe66255354e1895f0ebffaa8209b3c Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Wed, 20 Mar 2024 11:49:35 -0500 Subject: [PATCH 13/15] Calendar: Fix migration about invitations/subscriptions --- .../Entity/AgendaEventInvitation.php | 137 ------------------ src/CoreBundle/Entity/AgendaEventInvitee.php | 65 --------- .../Entity/AgendaEventSubscriber.php | 12 -- .../Entity/AgendaEventSubscription.php | 32 ---- .../Schema/V200/Version20230904173400.php | 25 ++-- .../Schema/V200/Version20230904173401.php | 34 +++++ .../Schema/V200/Version20240112131200.php | 6 - 7 files changed, 49 insertions(+), 262 deletions(-) delete mode 100644 src/CoreBundle/Entity/AgendaEventInvitation.php delete mode 100644 src/CoreBundle/Entity/AgendaEventInvitee.php delete mode 100644 src/CoreBundle/Entity/AgendaEventSubscriber.php delete mode 100644 src/CoreBundle/Entity/AgendaEventSubscription.php create mode 100644 src/CoreBundle/Migrations/Schema/V200/Version20230904173401.php diff --git a/src/CoreBundle/Entity/AgendaEventInvitation.php b/src/CoreBundle/Entity/AgendaEventInvitation.php deleted file mode 100644 index 019cc4e84f..0000000000 --- a/src/CoreBundle/Entity/AgendaEventInvitation.php +++ /dev/null @@ -1,137 +0,0 @@ - 'Chamilo\CoreBundle\Entity\AgendaEventInvitation', - 'subscription' => 'Chamilo\CoreBundle\Entity\AgendaEventSubscription', -])] -class AgendaEventInvitation -{ - use TimestampableTypedEntity; - - #[ORM\Id] - #[ORM\Column(type: 'integer')] - #[ORM\GeneratedValue(strategy: 'AUTO')] - protected ?int $id = null; - - #[ORM\OneToMany( - mappedBy: 'invitation', - targetEntity: AgendaEventInvitee::class, - cascade: ['persist', 'remove'], - orphanRemoval: true - )] - protected Collection $invitees; - - #[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'resourceNodes')] - #[ORM\JoinColumn(name: 'creator_id', referencedColumnName: 'id', nullable: true, onDelete: 'CASCADE')] - protected User $creator; - - public function __construct() - { - $this->invitees = new ArrayCollection(); - } - - public function getId(): ?int - { - return $this->id; - } - - public function getInvitees(): Collection - { - return $this->invitees; - } - - public function setInvitees(Collection $invitees): self - { - $this->invitees = $invitees; - - return $this; - } - - public function addInvitee(AgendaEventInvitee $invitee): self - { - $invitee->setInvitation($this); - $this->invitees->add($invitee); - - return $this; - } - - public function removeInviteeUser(User $user): self - { - /** @var AgendaEventInvitee $invitee */ - $invitee = $this - ->invitees - ->filter(function (AgendaEventInvitee $invitee) use ($user) { - return $invitee->getUser() === $user; - }) - ->first() - ; - - if ($invitee) { - $this->invitees->removeElement($invitee); - $invitee->setInvitation(null); - } - - return $this; - } - - public function removeInvitees(): self - { - $this->invitees = new ArrayCollection(); - - return $this; - } - - public function getCreator(): User - { - return $this->creator; - } - - public function setCreator(User $creator): self - { - $this->creator = $creator; - - return $this; - } - - public function hasUserAsInvitee(User $user): bool - { - return $this->invitees->exists( - function (int $key, AgendaEventInvitee $invitee) use ($user) { - return $invitee->getUser() === $user; - } - ); - } - - public function removeInviteesNotInIdList(array $idList): self - { - $toRemove = []; - - /** @var AgendaEventInvitee $invitee */ - foreach ($this->invitees as $key => $invitee) { - if (!\in_array($invitee->getUser()->getId(), $idList, true)) { - $toRemove[] = $key; - } - } - - foreach ($toRemove as $key) { - $this->invitees->remove($key); - } - - return $this; - } -} diff --git a/src/CoreBundle/Entity/AgendaEventInvitee.php b/src/CoreBundle/Entity/AgendaEventInvitee.php deleted file mode 100644 index cdbf2cdfcd..0000000000 --- a/src/CoreBundle/Entity/AgendaEventInvitee.php +++ /dev/null @@ -1,65 +0,0 @@ - AgendaEventInvitee::class, - 'subscriber' => AgendaEventSubscriber::class, -])] -class AgendaEventInvitee -{ - use TimestampableTypedEntity; - - #[ORM\Id] - #[ORM\Column(type: 'integer')] - #[ORM\GeneratedValue] - private int $id; - - #[ORM\ManyToOne(targetEntity: AgendaEventInvitation::class, inversedBy: 'invitees')] - #[ORM\JoinColumn(name: 'invitation_id', referencedColumnName: 'id', onDelete: 'CASCADE')] - private ?AgendaEventInvitation $invitation; - - #[ORM\ManyToOne(targetEntity: User::class)] - #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')] - private ?User $user; - - public function getId(): int - { - return $this->id; - } - - public function getInvitation(): ?AgendaEventInvitation - { - return $this->invitation; - } - - public function setInvitation(?AgendaEventInvitation $invitation): self - { - $this->invitation = $invitation; - - return $this; - } - - public function getUser(): ?User - { - return $this->user; - } - - public function setUser(?User $user): self - { - $this->user = $user; - - return $this; - } -} diff --git a/src/CoreBundle/Entity/AgendaEventSubscriber.php b/src/CoreBundle/Entity/AgendaEventSubscriber.php deleted file mode 100644 index 9575d7d153..0000000000 --- a/src/CoreBundle/Entity/AgendaEventSubscriber.php +++ /dev/null @@ -1,12 +0,0 @@ - 0])] - protected int $maxAttendees = 0; - - public function getMaxAttendees(): int - { - return $this->maxAttendees; - } - - public function setMaxAttendees(int $maxAttendees): self - { - $this->maxAttendees = $maxAttendees; - - return $this; - } -} diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20230904173400.php b/src/CoreBundle/Migrations/Schema/V200/Version20230904173400.php index db8846ce1b..59eafd68d3 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20230904173400.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20230904173400.php @@ -72,31 +72,36 @@ class Version20230904173400 extends AbstractMigrationChamilo $em->persist($calendarEvent); if ($collectiveInvitationsEnabled) { - $calendarEvent->setCollective((bool) $personalAgenda['collective']); - - $hasSubscriptions = false; $invitationsOrSubscriptionsInfo = []; if ($subscriptionsEnabled) { $subscriptionsInfo = $this->getSubscriptions((int) $personalAgenda['id']); - if (\count($subscriptionsInfo) > 0) { - $hasSubscriptions = true; - + if (\count($subscriptionsInfo) > 0 + && $personalAgenda['subscription_visibility'] !== 0 + ) { $invitationsOrSubscriptionsInfo = $subscriptionsInfo; } } - if ($hasSubscriptions) { + if ($invitationsOrSubscriptionsInfo) { $calendarEvent ->setInvitationType(CCalendarEvent::TYPE_SUBSCRIPTION) ->setSubscriptionVisibility($personalAgenda['subscription_visibility']) ->setSubscriptionItemId($personalAgenda['subscription_item_id']) + ->setMaxAttendees($invitationsOrSubscriptionsInfo[0]['max_attendees']) ; } else { - $calendarEvent->setInvitationType(CCalendarEvent::TYPE_INVITATION); + $invitationsInfo = $this->getInvitations($subscriptionsEnabled, (int) $personalAgenda['id']); + + if (\count($invitationsInfo) > 0) { + $calendarEvent + ->setCollective((bool) $personalAgenda['collective']) + ->setInvitationType(CCalendarEvent::TYPE_INVITATION) + ; - $invitationsOrSubscriptionsInfo = $this->getInvitations($subscriptionsEnabled, (int) $personalAgenda['id']); + $invitationsOrSubscriptionsInfo = $invitationsInfo; + } } foreach ($invitationsOrSubscriptionsInfo as $invitationOrSubscriptionInfo) { @@ -197,7 +202,7 @@ class Version20230904173400 extends AbstractMigrationChamilo private function getSubscriptions(int $personalAgendaId): array { - $sql = "SELECT i.id, i.creator_id, i.created_at, i.updated_at + $sql = "SELECT i.id, i.creator_id, i.created_at, i.updated_at, i.max_attendees FROM agenda_event_invitation i INNER JOIN personal_agenda pa ON i.id = pa.agenda_event_invitation_id WHERE pa.id = $personalAgendaId diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20230904173401.php b/src/CoreBundle/Migrations/Schema/V200/Version20230904173401.php new file mode 100644 index 0000000000..4c5893497f --- /dev/null +++ b/src/CoreBundle/Migrations/Schema/V200/Version20230904173401.php @@ -0,0 +1,34 @@ +hasTable('agenda_event_invitation')) { + + $this->addSql('ALTER TABLE personal_agenda DROP FOREIGN KEY FK_D8612460AF68C6B'); + $this->addSql("DROP INDEX UNIQ_D8612460AF68C6B ON personal_agenda"); + + $this->addSql("ALTER TABLE personal_agenda DROP agenda_event_invitation_id, DROP collective, DROP subscription_visibility, DROP subscription_item_id"); + + $this->addSql("ALTER TABLE agenda_event_invitation DROP FOREIGN KEY FK_52A2D5E161220EA6"); + $this->addSql("DROP TABLE agenda_event_invitation"); + + $this->addSql("ALTER TABLE agenda_event_invitee DROP FOREIGN KEY FK_4F5757FEA76ED395"); + $this->addSql("DROP TABLE agenda_event_invitee"); + } + } +} diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20240112131200.php b/src/CoreBundle/Migrations/Schema/V200/Version20240112131200.php index 127a065428..d4674506a1 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20240112131200.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20240112131200.php @@ -75,12 +75,6 @@ final class Version20240112131200 extends AbstractMigrationChamilo $this->addSql('ALTER TABLE resource_tag CHANGE id id INT AUTO_INCREMENT NOT NULL;'); } - if ($schema->hasTable('agenda_event_invitation')) { - error_log('Perform the changes in the agenda_event_invitation table'); - $this->addSql('ALTER TABLE personal_agenda DROP FOREIGN KEY FK_D8612460AF68C6B'); - $this->addSql('ALTER TABLE agenda_event_invitation CHANGE id id INT AUTO_INCREMENT NOT NULL;'); - } - if ($schema->hasTable('gradebook_comment')) { error_log('Perform the changes in the gradebook_comment table'); $this->addSql('ALTER TABLE gradebook_comment CHANGE id id INT AUTO_INCREMENT NOT NULL;'); From 1b615b3db67e835a17a7b2a251f464863e4e8ee0 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Wed, 20 Mar 2024 11:53:44 -0500 Subject: [PATCH 14/15] Minor: Format code --- .../EventListener/ExceptionListener.php | 2 +- .../Migrations/AbstractMigrationChamilo.php | 2 +- .../Schema/V200/Version20211005153900.php | 4 ++-- .../Schema/V200/Version20230904173400.php | 2 +- .../Schema/V200/Version20230904173401.php | 20 +++++++++---------- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/CoreBundle/EventListener/ExceptionListener.php b/src/CoreBundle/EventListener/ExceptionListener.php index 7425f486d1..18ad8942ca 100644 --- a/src/CoreBundle/EventListener/ExceptionListener.php +++ b/src/CoreBundle/EventListener/ExceptionListener.php @@ -6,7 +6,6 @@ declare(strict_types=1); namespace Chamilo\CoreBundle\EventListener; -use Chamilo\CoreBundle\Component\Utils\ChamiloApi; use Chamilo\CoreBundle\Exception\NotAllowedException; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Response; @@ -46,6 +45,7 @@ class ExceptionListener $loginUrl = $this->router->generate('login', ['redirect' => $redirectUrl], UrlGeneratorInterface::ABSOLUTE_URL); $event->setResponse(new RedirectResponse($loginUrl)); + return; } } diff --git a/src/CoreBundle/Migrations/AbstractMigrationChamilo.php b/src/CoreBundle/Migrations/AbstractMigrationChamilo.php index 443e848d10..4b46cf6113 100644 --- a/src/CoreBundle/Migrations/AbstractMigrationChamilo.php +++ b/src/CoreBundle/Migrations/AbstractMigrationChamilo.php @@ -266,7 +266,7 @@ abstract class AbstractMigrationChamilo extends AbstractMigration implements Con $groupId = $item['to_group_id'] ?? 0; if (empty($item['lastedit_date'])) { $lastUpdatedAt = new DateTime('now', new DateTimeZone('UTC')); - } else { + } else { $lastUpdatedAt = new DateTime($item['lastedit_date'], new DateTimeZone('UTC')); } $newVisibility = ResourceLink::VISIBILITY_DRAFT; diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20211005153900.php b/src/CoreBundle/Migrations/Schema/V200/Version20211005153900.php index 2952effba3..5f95b696f5 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20211005153900.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20211005153900.php @@ -47,11 +47,11 @@ class Version20211005153900 extends AbstractMigrationChamilo } if (!$table->hasColumn('exercise_id')) { - $this->addSql("ALTER TABLE ticket_ticket ADD exercise_id INT DEFAULT NULL"); + $this->addSql('ALTER TABLE ticket_ticket ADD exercise_id INT DEFAULT NULL'); } if (!$table->hasColumn('lp_id')) { - $this->addSql("ALTER TABLE ticket_ticket ADD lp_id INT DEFAULT NULL"); + $this->addSql('ALTER TABLE ticket_ticket ADD lp_id INT DEFAULT NULL'); } $table = $schema->getTable('ticket_assigned_log'); diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20230904173400.php b/src/CoreBundle/Migrations/Schema/V200/Version20230904173400.php index 59eafd68d3..e6688d98d2 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20230904173400.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20230904173400.php @@ -78,7 +78,7 @@ class Version20230904173400 extends AbstractMigrationChamilo $subscriptionsInfo = $this->getSubscriptions((int) $personalAgenda['id']); if (\count($subscriptionsInfo) > 0 - && $personalAgenda['subscription_visibility'] !== 0 + && 0 !== $personalAgenda['subscription_visibility'] ) { $invitationsOrSubscriptionsInfo = $subscriptionsInfo; } diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20230904173401.php b/src/CoreBundle/Migrations/Schema/V200/Version20230904173401.php index 4c5893497f..613612ad79 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20230904173401.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20230904173401.php @@ -1,5 +1,9 @@ hasTable('agenda_event_invitation')) { - $this->addSql('ALTER TABLE personal_agenda DROP FOREIGN KEY FK_D8612460AF68C6B'); - $this->addSql("DROP INDEX UNIQ_D8612460AF68C6B ON personal_agenda"); + $this->addSql('DROP INDEX UNIQ_D8612460AF68C6B ON personal_agenda'); - $this->addSql("ALTER TABLE personal_agenda DROP agenda_event_invitation_id, DROP collective, DROP subscription_visibility, DROP subscription_item_id"); + $this->addSql('ALTER TABLE personal_agenda DROP agenda_event_invitation_id, DROP collective, DROP subscription_visibility, DROP subscription_item_id'); - $this->addSql("ALTER TABLE agenda_event_invitation DROP FOREIGN KEY FK_52A2D5E161220EA6"); - $this->addSql("DROP TABLE agenda_event_invitation"); + $this->addSql('ALTER TABLE agenda_event_invitation DROP FOREIGN KEY FK_52A2D5E161220EA6'); + $this->addSql('DROP TABLE agenda_event_invitation'); - $this->addSql("ALTER TABLE agenda_event_invitee DROP FOREIGN KEY FK_4F5757FEA76ED395"); - $this->addSql("DROP TABLE agenda_event_invitee"); + $this->addSql('ALTER TABLE agenda_event_invitee DROP FOREIGN KEY FK_4F5757FEA76ED395'); + $this->addSql('DROP TABLE agenda_event_invitee'); } } } From 10a5f23ff432edc45bd0eb807be8d79436ac9694 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Wed, 20 Mar 2024 13:23:25 -0500 Subject: [PATCH 15/15] Internal: Set NotAllowedException as one HttpException to allow to return 403 status --- public/main/inc/lib/api.lib.php | 2 +- src/CoreBundle/Exception/NotAllowedException.php | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/public/main/inc/lib/api.lib.php b/public/main/inc/lib/api.lib.php index e9420646cc..475f410075 100644 --- a/public/main/inc/lib/api.lib.php +++ b/public/main/inc/lib/api.lib.php @@ -3517,7 +3517,7 @@ function api_not_allowed( $message = null, $responseCode = 0 ): never { - throw new NotAllowedException($message ?: 'You are not allowed', $responseCode); + throw new NotAllowedException($message ?: 'You are not allowed', null, $responseCode); } /** diff --git a/src/CoreBundle/Exception/NotAllowedException.php b/src/CoreBundle/Exception/NotAllowedException.php index fdac18d2f9..cc3cf58d15 100644 --- a/src/CoreBundle/Exception/NotAllowedException.php +++ b/src/CoreBundle/Exception/NotAllowedException.php @@ -6,12 +6,13 @@ declare(strict_types=1); namespace Chamilo\CoreBundle\Exception; -use Exception; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\HttpException; -class NotAllowedException extends Exception +class NotAllowedException extends HttpException { - public function __construct($message = 'Not allowed', $code = 0, ?Exception $previous = null) + public function __construct(?string $message = 'Not allowed', ?\Throwable $previous = null, int $code = 0, array $headers = []) { - parent::__construct($message, $code, $previous); + parent::__construct(Response::HTTP_FORBIDDEN, $message, $previous, $headers); } }