Merge branch 'master' of github.com:chamilo/chamilo-lms

pull/5209/head
Yannick Warnier 2 years ago
commit aaa5ed7558
  1. 12
      assets/css/scss/_exercise.scss
  2. 168
      assets/vue/components/Breadcrumb.vue
  3. 23
      assets/vue/components/layout/DashboardLayout.vue
  4. 29
      public/main/exercise/question_list_admin.inc.php
  5. 3
      public/main/inc/ajax/exercise.ajax.php
  6. 2
      public/main/inc/lib/display.lib.php
  7. 2
      public/main/inc/lib/template.lib.php
  8. 53
      src/CoreBundle/Component/Editor/CkEditor/Toolbar/Basic.php
  9. 20
      src/CoreBundle/Controller/SecurityController.php
  10. 1
      src/CoreBundle/Entity/Listener/SessionListener.php
  11. 1
      src/CoreBundle/EventListener/CourseAccessListener.php
  12. 1
      src/CoreBundle/EventListener/OnlineListener.php
  13. 1
      src/CoreBundle/Migrations/Schema/V200/Version20.php
  14. 2
      src/CoreBundle/Migrations/Schema/V200/Version20170625145000.php
  15. 1
      src/CoreBundle/Repository/SequenceRepository.php
  16. 6
      src/CoreBundle/Repository/TrackEDownloadsRepository.php
  17. 30
      var/vue_templates/components/layout/DashboardLayout.vue

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

@ -28,7 +28,7 @@
</router-link>
<a
v-else
:href="item.url"
:href="item.url !== '#' ? item.url : undefined"
v-bind="props.action"
>
<span>{{ item.label }}</span>
@ -46,24 +46,14 @@
</template>
<script setup>
import { computed } from "vue"
import { ref, watch } from "vue"
import { useRoute } from "vue-router"
import { useI18n } from "vue-i18n"
import Breadcrumb from "primevue/breadcrumb"
import { useCidReqStore } from "../store/cidReq"
import { storeToRefs } from "pinia"
// eslint-disable-next-line no-undef
const componentProps = defineProps({
layoutClass: {
type: String,
default: null,
},
legacy: {
type: Array,
default: () => [],
},
})
const legacyItems = ref(window.breadcrumb)
const cidReqStore = useCidReqStore()
const route = useRoute()
@ -71,84 +61,96 @@ const { t } = useI18n()
const { course, session } = storeToRefs(cidReqStore)
const itemList = computed(() => {
const list = [
"MyCourses",
"MySessions",
"MySessionsUpcoming",
"MySessionsPast",
"Home",
"MessageList",
"MessageNew",
"MessageShow",
"MessageCreate",
]
const items = []
if (route.name && route.name.includes("Page")) {
items.push({
label: t("Pages"),
to: "/resources/pages",
})
}
if (route.name && route.name.includes("Message")) {
items.push({
label: t("Messages"),
//disabled: route.path === path || lastItem.path === route.path,
to: "/resources/messages",
})
}
if (list.includes(route.name)) {
return items
}
if (course.value) {
if (session.value) {
items.push({
label: t("My sessions"),
route: { name: "MySessions" },
})
} else {
items.push({
label: t("My courses"),
route: { name: "MyCourses" },
const specialRouteNames = [
"MyCourses",
"MySessions",
"MySessionsUpcoming",
"MySessionsPast",
"Home",
"MessageList",
"MessageNew",
"MessageShow",
"MessageCreate",
]
const itemList = ref([])
watch(
route,
() => {
if ("/" === route.fullPath) {
return
}
itemList.value = []
if (route.name && route.name.includes("Page")) {
itemList.value.push({
label: t("Pages"),
to: "/resources/pages",
})
}
}
if (componentProps.legacy.length > 0) {
const mainUrl = window.location.href
const mainPath = mainUrl.indexOf("main/")
if (route.name && route.name.includes("Message")) {
itemList.value.push({
label: t("Messages"),
//disabled: route.path === path || lastItem.path === route.path,
to: "/resources/messages",
})
}
componentProps.legacy.forEach((item) => {
let url = item.url.toString()
let newUrl = url
if (specialRouteNames.includes(route.name)) {
return
}
if (url.indexOf("main/") > 0) {
newUrl = "/" + url.substring(mainPath, url.length)
if (course.value) {
if (session.value) {
itemList.value.push({
label: t("My sessions"),
route: { name: "MySessions" },
})
} else {
itemList.value.push({
label: t("My courses"),
route: { name: "MyCourses" },
})
}
}
if (newUrl === "/") {
newUrl = "#"
}
if (legacyItems.value.length > 0) {
const mainUrl = window.location.href
const mainPath = mainUrl.indexOf("main/")
items.push({
label: item["name"],
url: newUrl,
})
})
} else {
if (course.value) {
items.push({
label: course.value.title,
route: { name: "CourseHome", params: { id: course.value.id }, query: route.query },
legacyItems.value.forEach((item) => {
let url = item.url.toString()
let newUrl = url
if (url.indexOf("main/") > 0) {
newUrl = "/" + url.substring(mainPath, url.length)
}
if (newUrl === "/") {
newUrl = "#"
}
itemList.value.push({
label: item["name"],
url: newUrl,
})
})
}
}
return items
})
legacyItems.value = []
} else {
if (course.value) {
itemList.value.push({
label: course.value.title,
route: { name: "CourseHome", params: { id: course.value.id }, query: route.query },
})
}
}
},
{
immediate: true,
},
)
</script>

@ -5,19 +5,16 @@
class="app-main"
:class="{ 'app-main--no-sidebar': !securityStore.isAuthenticated }"
>
<Breadcrumb
v-if="showBreadcrumb"
:legacy="breadcrumb"
/>
<Breadcrumb v-if="showBreadcrumb" />
<slot />
<router-view />
</div>
</template>
<script setup>
import Breadcrumb from '../../components/Breadcrumb.vue';
import Topbar from '../../components/layout/Topbar.vue';
import Sidebar from '../../components/layout/Sidebar.vue';
import Breadcrumb from "../../components/Breadcrumb.vue"
import Topbar from "../../components/layout/Topbar.vue"
import Sidebar from "../../components/layout/Sidebar.vue"
import { useSecurityStore } from "../../store/securityStore"
// eslint-disable-next-line no-undef
@ -26,17 +23,7 @@ defineProps({
type: Boolean,
default: true,
},
});
})
const securityStore = useSecurityStore()
let breadcrumb = [];
try {
if (window.breadcrumb) {
breadcrumb = window.breadcrumb;
}
} catch (e) {
console.log(e.message);
}
</script>

@ -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=<?php echo $exerciseId; ?>";
$.post("<?php echo $ajax_url; ?>", 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();
});
});
</script>
<?php
@ -246,7 +261,7 @@ if (!$inATest) {
$title = strip_tags($title);
$move = '&nbsp;';
if ($allowQuestionOrdering) {
$move = Display::getMdiIcon('cursor-move');
$move = Display::getMdiIcon('cursor-move', 'moved');
}
// Question name

@ -347,9 +347,8 @@ switch ($action) {
$TBL_QUESTIONS,
['question_order' => $counter],
[
'question_id = ? AND c_id = ? AND quiz_id = ? ' => [
'question_id = ? AND quiz_id = ? ' => [
(int) $new_order_id,
$course_id,
$exercise_id,
],
]

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

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

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

@ -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

@ -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

@ -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.
*/

@ -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

@ -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

@ -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')) {

@ -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

@ -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
{

@ -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);
}
</script>
<template>
<Topbar />
<Sidebar
v-if="securityStore.isAuthenticated"
/>
<SidebarNotLoggedIn
v-else
/>
<Sidebar v-if="securityStore.isAuthenticated" />
<SidebarNotLoggedIn v-else />
<div
class="app-main"
:class="{ 'app-main--no-sidebar': !securityStore.isAuthenticated, 'app-main--no-loggedin': !securityStore.isAuthenticated }"
:class="{
'app-main--no-sidebar': !securityStore.isAuthenticated,
'app-main--no-loggedin': !securityStore.isAuthenticated,
}"
>
<Breadcrumb
v-if="showBreadcrumb"
:legacy="breadcrumb"
/>
<Breadcrumb v-if="showBreadcrumb" />
<slot />
<router-view />
</div>

Loading…
Cancel
Save