diff --git a/assets/css/scss/_exercise.scss b/assets/css/scss/_exercise.scss index 3366aceb41..afdbb12a01 100644 --- a/assets/css/scss/_exercise.scss +++ b/assets/css/scss/_exercise.scss @@ -62,6 +62,16 @@ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(102, 175, 233, 0.6); } +.ui-state-highlight { + height: 3.5em; + line-height: 2.2em; + background-color: #fafafa; + border: 1px dashed #ccc; + margin-top: 10px; + margin-bottom: 10px; + padding: 5px; +} + .question_menu { @apply p-4 flex flex-row gap-1; } @@ -232,4 +242,4 @@ @apply text-left; } } -} \ No newline at end of file +} diff --git a/assets/vue/components/Breadcrumb.vue b/assets/vue/components/Breadcrumb.vue index 0daac68320..ee478f9813 100644 --- a/assets/vue/components/Breadcrumb.vue +++ b/assets/vue/components/Breadcrumb.vue @@ -28,7 +28,7 @@ {{ item.label }} @@ -46,24 +46,14 @@ diff --git a/assets/vue/components/layout/DashboardLayout.vue b/assets/vue/components/layout/DashboardLayout.vue index a6fd5a2c34..433ce4d4db 100644 --- a/assets/vue/components/layout/DashboardLayout.vue +++ b/assets/vue/components/layout/DashboardLayout.vue @@ -5,19 +5,16 @@ class="app-main" :class="{ 'app-main--no-sidebar': !securityStore.isAuthenticated }" > - + diff --git a/public/main/exercise/question_list_admin.inc.php b/public/main/exercise/question_list_admin.inc.php index 4e1f8b174b..51eff76dc1 100644 --- a/public/main/exercise/question_list_admin.inc.php +++ b/public/main/exercise/question_list_admin.inc.php @@ -57,6 +57,7 @@ $ajax_url = api_get_path(WEB_AJAX_PATH).'exercise.ajax.php?'.api_get_cidreq().'& } }); + var isDragging = false; $("#question_list").accordion({ icons: null, heightStyle: "content", @@ -64,6 +65,10 @@ $ajax_url = api_get_path(WEB_AJAX_PATH).'exercise.ajax.php?'.api_get_cidreq().'& collapsible: true, header: ".header_operations", beforeActivate: function (e, ui) { + if (isDragging) { + e.preventDefault(); + isDragging = false; + } var data = ui.newHeader.data(); if (typeof data === 'undefined') { return; @@ -95,19 +100,29 @@ $ajax_url = api_get_path(WEB_AJAX_PATH).'exercise.ajax.php?'.api_get_cidreq().'& }) .sortable({ cursor: "move", // works? + axis: "y", + placeholder: "ui-state-highlight", //defines the yellow highlight + handle: ".moved", //only the class "moved" + start: function(event, ui) { + isDragging = true; + }, + stop: function(event, ui) { + stop = true; + setTimeout(function() { + isDragging = false; + }, 50); + }, update: function (event, ui) { var order = $(this).sortable("serialize") + "&a=update_question_order&exercise_id="; $.post("", order, function (result) { $("#message").html(result); }); - }, - axis: "y", - placeholder: "ui-state-highlight", //defines the yellow highlight - handle: ".moved", //only the class "moved" - stop: function () { - stop = true; } }); + + $(".moved").on('click', function(event) { + event.stopImmediatePropagation(); + }); }); $counter], [ - 'question_id = ? AND c_id = ? AND quiz_id = ? ' => [ + 'question_id = ? AND quiz_id = ? ' => [ (int) $new_order_id, - $course_id, $exercise_id, ], ] diff --git a/public/main/inc/lib/display.lib.php b/public/main/inc/lib/display.lib.php index e2cc8bb98d..39307f0a60 100644 --- a/public/main/inc/lib/display.lib.php +++ b/public/main/inc/lib/display.lib.php @@ -143,7 +143,7 @@ class Display } $params['legacy_javascript'] = $htmlHeadXtra; - $params['legacy_breadcrumb'] = json_encode($interbreadcrumb); + $params['legacy_breadcrumb'] = json_encode(array_values($interbreadcrumb)); Template::setVueParams($params); $content = Container::getTwig()->render($tpl, $params); diff --git a/public/main/inc/lib/template.lib.php b/public/main/inc/lib/template.lib.php index d2282f6afd..4aac738b1d 100644 --- a/public/main/inc/lib/template.lib.php +++ b/public/main/inc/lib/template.lib.php @@ -1062,7 +1062,7 @@ class Template } } - $this->params['legacy_breadcrumb'] = json_encode($interbreadcrumb); + $this->params['legacy_breadcrumb'] = json_encode(array_values($interbreadcrumb)); global $htmlHeadXtra; $this->params['legacy_javascript'] = $htmlHeadXtra; } diff --git a/src/CoreBundle/Component/Editor/CkEditor/Toolbar/Basic.php b/src/CoreBundle/Component/Editor/CkEditor/Toolbar/Basic.php index 5d5816249b..e6261cce21 100644 --- a/src/CoreBundle/Component/Editor/CkEditor/Toolbar/Basic.php +++ b/src/CoreBundle/Component/Editor/CkEditor/Toolbar/Basic.php @@ -191,13 +191,10 @@ class Basic extends Toolbar $config['file_picker_callback'] = '[browser]'; $iso = api_get_language_isocode(); - $url = api_get_path(WEB_PATH); + $languageConfig = $this->getLanguageConfig($iso); - // Language list: https://www.tiny.cloud/get-tiny/language-packages/ - if ('en_US' !== $iso) { - $config['language'] = $iso; - $config['language_url'] = "$url/libs/editor/langs/$iso.js"; - } + // Merge the language configuration + $config = array_merge($config, $languageConfig); /*if (isset($this->config)) { $this->config = array_merge($config, $this->config); @@ -301,4 +298,48 @@ class Basic extends Toolbar ['Toolbarswitch', 'Source'], ]; } + + /** + * Determines the appropriate language configuration for the editor. + * Tries to load a specific language file based on the ISO code. If not found, it attempts to load a general language file. + * Falls back to English if neither specific nor general language files are available. + */ + private function getLanguageConfig(string $iso): array + { + $url = api_get_path(WEB_PATH); + $sysUrl = api_get_path(SYS_PATH); + $defaultLang = 'en'; + $defaultLangFile = "libs/editor/langs/{$defaultLang}.js"; + $specificLangFile = "libs/editor/langs/{$iso}.js"; + $generalLangFile = null; + + // Default configuration set to English + $config = [ + 'language' => $defaultLang, + 'language_url' => $defaultLangFile, + ]; + + if ('en_US' !== $iso) { + // Check for a specific variant of the language (e.g., de_german2) + if (str_contains($iso, '_')) { + // Extract the general language code (e.g., de) + list($generalLangCode) = explode('_', $iso, 2); + $generalLangFile = "libs/editor/langs/{$generalLangCode}.js"; + } + + // Attempt to load the specific language file + if (file_exists($sysUrl.$specificLangFile)) { + $config['language'] = $iso; + $config['language_url'] = $url.$specificLangFile; + } + + // Fallback to the general language file if specific is not available + elseif (null !== $generalLangFile && file_exists($sysUrl.$generalLangFile)) { + $config['language'] = $generalLangCode; + $config['language_url'] = $url.$generalLangFile; + } + } + + return $config; + } } diff --git a/src/CoreBundle/Controller/SecurityController.php b/src/CoreBundle/Controller/SecurityController.php index 073111a761..6a515c0d59 100644 --- a/src/CoreBundle/Controller/SecurityController.php +++ b/src/CoreBundle/Controller/SecurityController.php @@ -24,24 +24,14 @@ use Symfony\Component\Serializer\SerializerInterface; class SecurityController extends AbstractController { - private $entityManager; - private $settingsManager; - private $tokenStorage; - private $authorizationChecker; - public function __construct( private SerializerInterface $serializer, private TrackELoginRecordRepository $trackELoginRecordRepository, - EntityManagerInterface $entityManager, - SettingsManager $settingsManager, - TokenStorageInterface $tokenStorage, - AuthorizationCheckerInterface $authorizationChecker - ) { - $this->entityManager = $entityManager; - $this->settingsManager = $settingsManager; - $this->tokenStorage = $tokenStorage; - $this->authorizationChecker = $authorizationChecker; - } + private EntityManagerInterface $entityManager, + private SettingsManager $settingsManager, + private TokenStorageInterface $tokenStorage, + private AuthorizationCheckerInterface $authorizationChecker + ) {} #[Route('/login_json', name: 'login_json', methods: ['POST'])] public function loginJson(Request $request, EntityManager $entityManager, SettingsManager $settingsManager, TokenStorageInterface $tokenStorage): Response diff --git a/src/CoreBundle/Entity/Listener/SessionListener.php b/src/CoreBundle/Entity/Listener/SessionListener.php index a5851c48b7..f2ffaf91c6 100644 --- a/src/CoreBundle/Entity/Listener/SessionListener.php +++ b/src/CoreBundle/Entity/Listener/SessionListener.php @@ -15,7 +15,6 @@ use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Security\Core\Security; /** - * Class SessionListener * Session entity listener, when a session is created/updated. */ class SessionListener diff --git a/src/CoreBundle/EventListener/CourseAccessListener.php b/src/CoreBundle/EventListener/CourseAccessListener.php index 34b5fcb278..b62dc0f36e 100644 --- a/src/CoreBundle/EventListener/CourseAccessListener.php +++ b/src/CoreBundle/EventListener/CourseAccessListener.php @@ -13,7 +13,6 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; /** - * Class CourseAccessListener * In and outs of a course * This listeners is always called when user enters the course home. */ diff --git a/src/CoreBundle/EventListener/OnlineListener.php b/src/CoreBundle/EventListener/OnlineListener.php index b0aa13a9eb..8cb630c65d 100644 --- a/src/CoreBundle/EventListener/OnlineListener.php +++ b/src/CoreBundle/EventListener/OnlineListener.php @@ -14,7 +14,6 @@ use Symfony\Component\HttpKernel\HttpKernel; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; /** - * Class OnlineListener * Adds objects into the session like the old global.inc. */ class OnlineListener diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20.php b/src/CoreBundle/Migrations/Schema/V200/Version20.php index 1e80efc13a..b46125d030 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20.php @@ -11,7 +11,6 @@ use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo; use Doctrine\DBAL\Schema\Schema; /** - * Class Version20 * Migrate file to updated to Chamilo 2.0. */ class Version20 extends AbstractMigrationChamilo diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20170625145000.php b/src/CoreBundle/Migrations/Schema/V200/Version20170625145000.php index c7f3d72bca..29a6c5e5da 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20170625145000.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20170625145000.php @@ -49,7 +49,7 @@ class Version20170625145000 extends AbstractMigrationChamilo } if (!$table->hasColumn('invitation_type')) { - $this->addSql("ALTER TABLE c_calendar_event ADD invitation_type VARCHAR(255) DEFAULT NULL"); + $this->addSql('ALTER TABLE c_calendar_event ADD invitation_type VARCHAR(255) DEFAULT NULL'); } if (!$table->hasColumn('collective')) { diff --git a/src/CoreBundle/Repository/SequenceRepository.php b/src/CoreBundle/Repository/SequenceRepository.php index cbb496ee4f..69e82432a1 100644 --- a/src/CoreBundle/Repository/SequenceRepository.php +++ b/src/CoreBundle/Repository/SequenceRepository.php @@ -17,7 +17,6 @@ use Doctrine\Persistence\ManagerRegistry; use SessionManager; /** - * Class SequenceRepository * The functions inside this class should return an instance of QueryBuilder. */ class SequenceRepository extends ServiceEntityRepository diff --git a/src/CoreBundle/Repository/TrackEDownloadsRepository.php b/src/CoreBundle/Repository/TrackEDownloadsRepository.php index 6e35067524..4ec8e7c156 100644 --- a/src/CoreBundle/Repository/TrackEDownloadsRepository.php +++ b/src/CoreBundle/Repository/TrackEDownloadsRepository.php @@ -20,11 +20,7 @@ class TrackEDownloadsRepository extends ServiceEntityRepository } /** - * Save record of a resource being downloaded in track_e_downloads - * @param int $userId - * @param int $resourceLinkId - * @param string $documentUrl - * @return int + * Save record of a resource being downloaded in track_e_downloads. */ public function saveDownload(int $userId, int $resourceLinkId, string $documentUrl): int { diff --git a/var/vue_templates/components/layout/DashboardLayout.vue b/var/vue_templates/components/layout/DashboardLayout.vue index 9f54ddbe24..13c01d1f4b 100644 --- a/var/vue_templates/components/layout/DashboardLayout.vue +++ b/var/vue_templates/components/layout/DashboardLayout.vue @@ -10,37 +10,23 @@ defineProps({ type: Boolean, default: true, }, -}); +}) const securityStore = useSecurityStore() - -let breadcrumb = []; - -try { - if (window.breadcrumb) { - breadcrumb = window.breadcrumb; - } -} catch (e) { - console.log(e.message); -}