From a3b28a27a78c08d80578adfb59e109f2db68f13c Mon Sep 17 00:00:00 2001 From: Christian Beeznest Date: Tue, 28 Jan 2025 11:31:46 -0500 Subject: [PATCH 1/5] User: Update last_login on user login --- src/CoreBundle/EventListener/LoginSuccessHandler.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/CoreBundle/EventListener/LoginSuccessHandler.php b/src/CoreBundle/EventListener/LoginSuccessHandler.php index 05f68bfb6ec..831b487b870 100644 --- a/src/CoreBundle/EventListener/LoginSuccessHandler.php +++ b/src/CoreBundle/EventListener/LoginSuccessHandler.php @@ -144,6 +144,10 @@ public function __invoke(InteractiveLoginEvent $event): ?RedirectResponse $trackELoginRepository->createLoginRecord($user, new DateTime(), $userIp); $trackEOnlineRepository->createOnlineSession($user, $userIp); + $user->setLastLogin(new DateTime()); + $this->entityManager->persist($user); + $this->entityManager->flush(); + // Log of connection attempts $trackELoginRecordRepository->addTrackLogin($user->getUsername(), $userIp, true); $this->loginAttemptLogger->logAttempt(true, $user->getUsername(), $userIp); From b340fab3bb71ff146a74ab6066b11539d0d9d534 Mon Sep 17 00:00:00 2001 From: Christian Beeznest Date: Tue, 28 Jan 2025 23:12:29 -0500 Subject: [PATCH 2/5] User: Fix settings update, lang filter, profile form --- .../Controller/AccountController.php | 7 ++++ src/CoreBundle/Entity/User.php | 13 +++++++- src/CoreBundle/Form/ProfileType.php | 32 +++++++++++-------- .../Repository/LanguageRepository.php | 10 ++++-- 4 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/CoreBundle/Controller/AccountController.php b/src/CoreBundle/Controller/AccountController.php index 820fe89b8a9..d4b2c126577 100644 --- a/src/CoreBundle/Controller/AccountController.php +++ b/src/CoreBundle/Controller/AccountController.php @@ -61,6 +61,13 @@ public function edit(Request $request, UserRepository $userRepository, Illustrat } } + if ($form->has('password')) { + $password = $form['password']->getData(); + if ($password) { + $user->setPlainPassword($password); + } + } + $showTermsIfProfileCompleted = ('true' === $settingsManager->getSetting('show_terms_if_profile_completed')); $user->setProfileCompleted($showTermsIfProfileCompleted); diff --git a/src/CoreBundle/Entity/User.php b/src/CoreBundle/Entity/User.php index 1e7f06c9a6d..60d899494aa 100644 --- a/src/CoreBundle/Entity/User.php +++ b/src/CoreBundle/Entity/User.php @@ -1220,7 +1220,7 @@ public function getPlainPassword(): ?string return $this->plainPassword; } - public function setPlainPassword(string $password): self + public function setPlainPassword(?string $password): self { $this->plainPassword = $password; // forces the object to look "dirty" to Doctrine. Avoids @@ -2210,6 +2210,17 @@ public function setSurveyInvitations(Collection $surveyInvitations): self return $this; } + public function getLogin(): string + { + return $this->username; + } + + public function setLogin(string $login): self + { + $this->username = $login; + return $this; + } + /** * @return Collection */ diff --git a/src/CoreBundle/Form/ProfileType.php b/src/CoreBundle/Form/ProfileType.php index 9af8669dba4..8d02affe336 100644 --- a/src/CoreBundle/Form/ProfileType.php +++ b/src/CoreBundle/Form/ProfileType.php @@ -11,8 +11,9 @@ use Chamilo\CoreBundle\Repository\LanguageRepository; use Chamilo\CoreBundle\Settings\SettingsManager; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\EmailType; -use Symfony\Component\Form\Extension\Core\Type\LocaleType; +use Symfony\Component\Form\Extension\Core\Type\PasswordType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TimezoneType; use Symfony\Component\Form\FormBuilderInterface; @@ -23,20 +24,16 @@ */ class ProfileType extends AbstractType { - private LanguageRepository $languageRepository; - public function __construct( - LanguageRepository $languageRepository, - private readonly SettingsManager $settingsManager - ) { - $this->languageRepository = $languageRepository; - } + private readonly LanguageRepository $languageRepository, + private readonly SettingsManager $settingsManager, + ) {} public function buildForm(FormBuilderInterface $builder, array $options): void { - $changeableOptions = $this->settingsManager->getSetting('profile.changeable_options') ?? []; - $visibleOptions = $this->settingsManager->getSetting('profile.visible_options') ?? []; - $languages = array_flip($this->languageRepository->getAllAvailableToArray()); + $changeableOptions = $this->settingsManager->getSetting('profile.changeable_options', true) ?? []; + $visibleOptions = $this->settingsManager->getSetting('profile.visible_options', true) ?? []; + $languages = array_flip($this->languageRepository->getAllAvailableToArray(true)); $fieldsMap = [ 'name' => ['field' => 'firstname', 'type' => TextType::class, 'label' => 'Firstname'], @@ -49,10 +46,16 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'mapped' => false, ], 'login' => ['field' => 'login', 'type' => TextType::class, 'label' => 'Login'], - 'password' => ['field' => 'password', 'type' => TextType::class, 'label' => 'Password'], + 'password' => [ + 'field' => 'password', + 'type' => PasswordType::class, + 'label' => 'Password', + 'mapped' => false, + 'required' => false, + ], 'language' => [ 'field' => 'locale', - 'type' => LocaleType::class, + 'type' => ChoiceType::class, 'label' => 'Language', 'choices' => $languages, ], @@ -69,7 +72,8 @@ public function buildForm(FormBuilderInterface $builder, array $options): void array_merge( [ 'label' => $fieldConfig['label'], - 'required' => false, + 'required' => $fieldConfig['required'] ?? false, + 'mapped' => $fieldConfig['mapped'] ?? true, 'attr' => !$isEditable ? ['readonly' => true] : [], ], isset($fieldConfig['choices']) ? ['choices' => $fieldConfig['choices']] : [] diff --git a/src/CoreBundle/Repository/LanguageRepository.php b/src/CoreBundle/Repository/LanguageRepository.php index 3850ef48b3a..17d223d90ca 100644 --- a/src/CoreBundle/Repository/LanguageRepository.php +++ b/src/CoreBundle/Repository/LanguageRepository.php @@ -44,9 +44,15 @@ public function getAllAvailable($excludeDefaultLocale = false): QueryBuilder return $qb; } - public function getAllAvailableToArray(): array + public function getAllAvailableToArray(bool $onlyActive = false): array { - $languages = $this->getAllAvailable()->getQuery()->getResult(); + $queryBuilder = $this->getAllAvailable(); + + if (!$onlyActive) { + $queryBuilder->resetDQLPart('where'); + } + + $languages = $queryBuilder->getQuery()->getResult(); $list = []; From fb2cf66e78d3ed73c94df0a89181c72745cbcc38 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com> Date: Thu, 30 Jan 2025 02:21:16 -0500 Subject: [PATCH 3/5] Display: Fix styles for icons, buttons and forms --- assets/css/scss/atoms/_buttons.scss | 29 ++++++++++++++----- assets/css/scss/molecules/_course_tool.scss | 4 --- assets/css/scss/molecules/_empty_state.scss | 6 +++- assets/css/scss/molecules/_toolbar.scss | 2 +- .../lib/formvalidator/FormValidator.class.php | 4 +-- 5 files changed, 28 insertions(+), 17 deletions(-) diff --git a/assets/css/scss/atoms/_buttons.scss b/assets/css/scss/atoms/_buttons.scss index fa2a74711c1..a212c7f3ee1 100644 --- a/assets/css/scss/atoms/_buttons.scss +++ b/assets/css/scss/atoms/_buttons.scss @@ -1,6 +1,9 @@ .btn { - @apply cursor-default font-semibold gap-2 inline-flex justify-center px-6 py-2 rounded-md transition flex-none; - font-size: 16px; + @apply cursor-default font-semibold gap-2 inline-flex justify-center px-6 py-2 rounded-md transition flex-none text-base; + + .mdi { + @apply text-base; + } &--primary { @apply bg-primary text-white; @@ -231,8 +234,6 @@ $border-color_12: #9333EA; @include filled-style('primary', 'support-4'); @apply cursor-pointer font-semibold gap-2 inline-flex justify-center items-center px-4 py-2 rounded-md transition; - font-size: 16px; - &:focus { @apply outline-none; } @@ -259,16 +260,28 @@ $border-color_12: #9333EA; &.p-button-sm { @apply px-2 py-1; - font-size: 13px; + + .p-button-icon, + .p-button-label { + font-size: 13px; + } } &.p-button-lg { @apply px-8 py-4; - font-size: 18px; + + .p-button-icon, + .p-button-label { + font-size: 18px; + } + } + + .p-button-icon { + @apply text-base; } .p-button-label { - @apply align-middle font-semibold; + @apply align-middle font-semibold text-base; } &.p-button-icon-only { @@ -289,7 +302,7 @@ $border-color_12: #9333EA; @apply bg-gray-10; .p-button-label { - text-decoration: underline; + @apply underline; } } } diff --git a/assets/css/scss/molecules/_course_tool.scss b/assets/css/scss/molecules/_course_tool.scss index 0a985c8d293..aa80ff4641a 100644 --- a/assets/css/scss/molecules/_course_tool.scss +++ b/assets/css/scss/molecules/_course_tool.scss @@ -13,13 +13,9 @@ &__icon { @apply text-transparent bg-clip-text bg-gradient-to-br from-primary to-primary-gradient leading-none; - &, &.mdi { font-size: 52px; } - - &::before { - } } &__title { diff --git a/assets/css/scss/molecules/_empty_state.scss b/assets/css/scss/molecules/_empty_state.scss index 8216f46c4ca..9f7b783bde4 100644 --- a/assets/css/scss/molecules/_empty_state.scss +++ b/assets/css/scss/molecules/_empty_state.scss @@ -7,7 +7,11 @@ } &__icon { - @apply mb-4 text-9xl text-transparent bg-clip-text bg-gradient-to-br from-primary to-primary-gradient w-32 h-32; + @apply mb-4 text-transparent bg-clip-text bg-gradient-to-br from-primary to-primary-gradient w-32 h-32; + + &.mdi { + font-size: 8rem; + } } &__summary { diff --git a/assets/css/scss/molecules/_toolbar.scss b/assets/css/scss/molecules/_toolbar.scss index 050c6fc215f..30831805afc 100644 --- a/assets/css/scss/molecules/_toolbar.scss +++ b/assets/css/scss/molecules/_toolbar.scss @@ -3,7 +3,7 @@ &-group-left, &-group-right { - @apply flex flex-row flex-wrap gap-2; + @apply flex flex-row flex-wrap gap-2 items-center; } &-separator { diff --git a/public/main/inc/lib/formvalidator/FormValidator.class.php b/public/main/inc/lib/formvalidator/FormValidator.class.php index e39b6642bee..85fe7c9bb64 100644 --- a/public/main/inc/lib/formvalidator/FormValidator.class.php +++ b/public/main/inc/lib/formvalidator/FormValidator.class.php @@ -50,10 +50,8 @@ public function __construct( switch ($layout) { case self::LAYOUT_BOX_SEARCH: - $attributes['class'] = 'form--search'; - break; case self::LAYOUT_INLINE: - $attributes['class'] = 'flex flex-row gap-3 '; + $attributes['class'] = 'flex flex-row gap-3 items-center '; break; case self::LAYOUT_BOX: $attributes['class'] = 'ch flex gap-1 '; From a83db96aec023141271f2b9dc3871e9d4ed4eef0 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com> Date: Thu, 30 Jan 2025 02:23:49 -0500 Subject: [PATCH 4/5] Use Display::toolbarButton instead of HTML code --- public/main/admin/course_list.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/public/main/admin/course_list.php b/public/main/admin/course_list.php index d4d20f0349f..3c98e9a760a 100644 --- a/public/main/admin/course_list.php +++ b/public/main/admin/course_list.php @@ -12,6 +12,7 @@ use Chamilo\CoreBundle\Component\Utils\ActionIcon; use Chamilo\CoreBundle\Component\Utils\StateIcon; use Chamilo\CoreBundle\Component\Utils\ToolIcon; +use Chamilo\CoreBundle\Framework\Container; $cidReset = true; @@ -416,9 +417,12 @@ function get_course_visibility_icon(int $visibility): string ['id' => 'course-search-keyword', 'aria-label' => get_lang('Search courses')] ); $form->addButtonSearch(get_lang('Search courses')); - $advanced = ' - '. - get_lang('Advanced search').''; + $advanced = Display::toolbarButton( + get_lang('Advanced search'), + Container::getRouter()->generate('legacy_main', ['name' => 'admin/course_list.php', 'search' => 'advanced']), + ActionIcon::SEARCH, + 'plain' + ); // Create a filter by session $sessionFilter = new FormValidator( From b508679ceb2e3d839c20a6de820eec6b70b72511 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com> Date: Thu, 30 Jan 2025 03:13:47 -0500 Subject: [PATCH 5/5] Exercise: Disable submit buttons while recorded audio is saving - refs BT#22293 --- public/main/inc/lib/javascript/record_audio/record_audio.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/main/inc/lib/javascript/record_audio/record_audio.js b/public/main/inc/lib/javascript/record_audio/record_audio.js index 1d3a6e95405..d271bbacdf9 100644 --- a/public/main/inc/lib/javascript/record_audio/record_audio.js +++ b/public/main/inc/lib/javascript/record_audio/record_audio.js @@ -87,6 +87,8 @@ window.RecordAudio = (function () { if (btnSave) { btnSave.prop('disabled', true).text(btnSave.data('loadingtext')); } + + $('.exercise_save_now_button button, .exercise_actions button').prop('disabled', true); } }).done(function (response) { $(response.message).insertAfter($(rtcInfo.blockId).find('.well')); @@ -97,6 +99,8 @@ window.RecordAudio = (function () { btnStop.prop('disabled', true).addClass('hidden'); btnPause.prop('disabled', true).addClass('hidden'); btnStart.prop('disabled', false).removeClass('hidden'); + + $('.exercise_save_now_button button, .exercise_actions button').prop('disabled', false); }); } @@ -127,7 +131,7 @@ window.RecordAudio = (function () { alert(error); } - if(!!(navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia)) { + if(navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia) { navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; navigator.getUserMedia(mediaConstraints, successCallback, errorCallback); return;