diff --git a/assets/locales/ar.json b/assets/locales/ar.json index 8be93a1875..4ff4a10ed1 100644 --- a/assets/locales/ar.json +++ b/assets/locales/ar.json @@ -18,7 +18,7 @@ "Surveys": "\u0627\u0644\u0627\u0633\u062a\u0628\u064a\u0627\u0646\u0627\u062a", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "\u0627\u0639\u062f\u0627\u062f\u0627\u062a \u0627\u0644\u0645\u0642\u0631\u0631", "Sign in": "\u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u062f\u062e\u0648\u0644", "My courses": "\u0645\u0642\u0631\u0631\u0627\u062a\u064a", "Start recording": "\u0628\u062f\u0623 \u0627\u0644\u062a\u0633\u062c\u064a\u0644", diff --git a/assets/locales/ast_ES.json b/assets/locales/ast_ES.json index e4a68ee7fc..31d1ac08e9 100644 --- a/assets/locales/ast_ES.json +++ b/assets/locales/ast_ES.json @@ -14,7 +14,7 @@ "Survey": "Encuesta", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Configuraci\u00f3n de la lleici\u00f3n", "My courses": "Los mios cursos", "Attach": "Attach", "Home": "P\u00e1xina d'entamu", diff --git a/assets/locales/bg.json b/assets/locales/bg.json index 32dd5bceed..df3a612555 100644 --- a/assets/locales/bg.json +++ b/assets/locales/bg.json @@ -18,7 +18,7 @@ "Surveys": "\u0410\u043d\u043a\u0435\u0442\u0438", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438", "My courses": "\u041c\u043e\u0438\u0442\u0435 \u043a\u0443\u0440\u0441\u043e\u0432\u0435", "Attach": "Attach", "Home": "\u041d\u0430\u0447\u0430\u043b\u043e", diff --git a/assets/locales/bn_BD.json b/assets/locales/bn_BD.json index 31f3756a60..7b2f9b317e 100644 --- a/assets/locales/bn_BD.json +++ b/assets/locales/bn_BD.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/bo_CN.json b/assets/locales/bo_CN.json index 31f3756a60..7b2f9b317e 100644 --- a/assets/locales/bo_CN.json +++ b/assets/locales/bo_CN.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/bs_BA.json b/assets/locales/bs_BA.json index 31f3756a60..7b2f9b317e 100644 --- a/assets/locales/bs_BA.json +++ b/assets/locales/bs_BA.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/ca_ES.json b/assets/locales/ca_ES.json index 1a4c98a432..bcdad8bb3b 100644 --- a/assets/locales/ca_ES.json +++ b/assets/locales/ca_ES.json @@ -18,7 +18,7 @@ "Surveys": "Enquestes", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Configuraci\u00f3 del curs", "Sign in": "Accedeix", "My courses": "La meva llista de cursos", "Attach": "Attach", diff --git a/assets/locales/cs_CZ.json b/assets/locales/cs_CZ.json index 4d53051d1a..d535a1ee21 100644 --- a/assets/locales/cs_CZ.json +++ b/assets/locales/cs_CZ.json @@ -6,7 +6,6 @@ "Member": "\u010clen", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Moje kurzy", "Attach": "Attach", "Home": "Dom\u016f", diff --git a/assets/locales/da.json b/assets/locales/da.json index 9b871000f5..adfa946de3 100644 --- a/assets/locales/da.json +++ b/assets/locales/da.json @@ -12,7 +12,6 @@ "Survey": "Evaluering", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Min kursusoversigt", "Attach": "Attach", "Home": "Hjem", diff --git a/assets/locales/de.json b/assets/locales/de.json index 8fa59d559b..bb313100b9 100644 --- a/assets/locales/de.json +++ b/assets/locales/de.json @@ -18,7 +18,7 @@ "Surveys": "Umfragen", "Tracking": "Tracking", "Course maintenance": "Kurspflege", - "Course setting": "Course setting", + "Course settings": "Kurs-Einstellungen", "Sign in": "Eintragen", "My courses": "Meine Kurse", "Start recording": "Aufnahme starten", diff --git a/assets/locales/el.json b/assets/locales/el.json index 562c06d398..dd2534fc8a 100644 --- a/assets/locales/el.json +++ b/assets/locales/el.json @@ -18,7 +18,7 @@ "Surveys": "\u0391\u03c0\u03bf\u03b3\u03c1\u03b1\u03c6\u03ad\u03c2", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03bc\u03b1\u03b8\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2", "Sign in": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5", "My courses": "\u03a4\u03b1 \u03bc\u03b1\u03b8\u03ae\u03bc\u03b1\u03c4\u03ac \u03bc\u03bf\u03c5", "Attach": "Attach", diff --git a/assets/locales/en.json b/assets/locales/en.json index e8143f33f9..09bfc6a696 100644 --- a/assets/locales/en.json +++ b/assets/locales/en.json @@ -18,7 +18,7 @@ "Surveys": "Surveys", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Course settings", "Sign in": "Sign in", "My courses": "My courses", "Start recording": "Start recording", diff --git a/assets/locales/eo.json b/assets/locales/eo.json index 46f1303e81..d555636748 100644 --- a/assets/locales/eo.json +++ b/assets/locales/eo.json @@ -11,7 +11,6 @@ "Member": "Uzanto", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "miajn kursojn", "Attach": "Attach", "Home": "Komenco", diff --git a/assets/locales/es.json b/assets/locales/es.json index bbdd191fd6..05fe61ad31 100644 --- a/assets/locales/es.json +++ b/assets/locales/es.json @@ -18,7 +18,7 @@ "Surveys": "Encuestas", "Tracking": "Tracking", "Course maintenance": "Mantenimiento del curso", - "Course setting": "Course setting", + "Course settings": "Configuraci\u00f3n del curso", "Sign in": "Ingresar", "My courses": "Mis cursos", "Start recording": "Lanzar la grabaci\u00f3n", @@ -447,7 +447,7 @@ "Cancel": "Cancelar", "Event": "Event", "Are you sure you want to delete this event?": "Are you sure you want to delete this event?", - "Edit introduction": "Edit introduction", + "Edit introduction": "Editar la introduccion", "Show all": "Mostrar todos", "Hide all": "Ocultar todo", "Sort": "Ordenar", diff --git a/assets/locales/eu_ES.json b/assets/locales/eu_ES.json index 0c29132f12..7e1f5503dc 100644 --- a/assets/locales/eu_ES.json +++ b/assets/locales/eu_ES.json @@ -18,7 +18,7 @@ "Surveys": "Inkestak", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Ikastaroaren ezarpenak", "My courses": "Nire ikastaroak", "Attach": "Attach", "Home": "Hasierakoa", diff --git a/assets/locales/fa_AF.json b/assets/locales/fa_AF.json index 31f3756a60..7b2f9b317e 100644 --- a/assets/locales/fa_AF.json +++ b/assets/locales/fa_AF.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/fa_IR.json b/assets/locales/fa_IR.json index 0aa72424cc..9514cf24cd 100644 --- a/assets/locales/fa_IR.json +++ b/assets/locales/fa_IR.json @@ -18,7 +18,7 @@ "Surveys": "\u0628\u0631\u0631\u0633\u06cc", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "\u062a\u0646\u0638\u06cc\u0645\u0627\u062a \u062f\u0631\u0633", "My courses": "\u0641\u0647\u0631\u0633\u062a \u062f\u0631\u0648\u0633 \u0645\u0646", "Attach": "Attach", "Home": "\u0635\u0641\u062d\u0647 \u0627\u0635\u0644\u06cc", diff --git a/assets/locales/fi_FI.json b/assets/locales/fi_FI.json index e3f4a6fa7e..7e9a95e0a4 100644 --- a/assets/locales/fi_FI.json +++ b/assets/locales/fi_FI.json @@ -15,7 +15,7 @@ "Surveys": "Kyselyt", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Kurssin asetukset", "My courses": "Kurssini", "Attach": "Attach", "Home": "Projektin kotisivu", diff --git a/assets/locales/fo_FO.json b/assets/locales/fo_FO.json index 3274a623ca..6e9839ae61 100644 --- a/assets/locales/fo_FO.json +++ b/assets/locales/fo_FO.json @@ -3,7 +3,6 @@ "Links": "Leinkjur", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "M\u00edni skei\u00f0", "Attach": "Attach", "Home": "Heim", diff --git a/assets/locales/fr_FR.json b/assets/locales/fr_FR.json index d5b332facf..749690322a 100644 --- a/assets/locales/fr_FR.json +++ b/assets/locales/fr_FR.json @@ -18,7 +18,7 @@ "Surveys": "Enqu\u00eates", "Tracking": "Tracking", "Course maintenance": "Maintenance du cours", - "Course setting": "Course setting", + "Course settings": "Param\u00e8tres du cours", "Sign in": "Connectez-vous", "My courses": "Mes cours", "Start recording": "Lancer l'enregistrement", @@ -447,7 +447,7 @@ "Cancel": "Annuler", "Event": "Event", "Are you sure you want to delete this event?": "Are you sure you want to delete this event?", - "Edit introduction": "Edit introduction", + "Edit introduction": "\u00c9diter l'introduction", "Show all": "Afficher tout", "Hide all": "Masquer tout", "Sort": "Trier", diff --git a/assets/locales/fur.json b/assets/locales/fur.json index a45a620fc1..ac56f66c00 100644 --- a/assets/locales/fur.json +++ b/assets/locales/fur.json @@ -18,7 +18,7 @@ "Surveys": "Surveys", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Course settings", "Sign in": "Sign in", "My courses": "My courses", "Start recording": "Start recording", diff --git a/assets/locales/gl.json b/assets/locales/gl.json index a45a620fc1..ac56f66c00 100644 --- a/assets/locales/gl.json +++ b/assets/locales/gl.json @@ -18,7 +18,7 @@ "Surveys": "Surveys", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Course settings", "Sign in": "Sign in", "My courses": "My courses", "Start recording": "Start recording", diff --git a/assets/locales/he_IL.json b/assets/locales/he_IL.json index dd4c1e79b5..4cda5dbe9b 100644 --- a/assets/locales/he_IL.json +++ b/assets/locales/he_IL.json @@ -18,7 +18,7 @@ "Surveys": "\u05e1\u05e7\u05e8\u05d9\u05dd", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05e7\u05d5\u05e8\u05e1", "Sign in": "\u05db\u05e0\u05d9\u05e1\u05d4", "My courses": "\u05d4\u05e7\u05d5\u05e8\u05e1\u05d9\u05dd \u05e9\u05dc\u05d9", "Start recording": "\u05d4\u05ea\u05d7\u05dc \u05dc\u05d4\u05e7\u05dc\u05d9\u05d8", diff --git a/assets/locales/hi.json b/assets/locales/hi.json index 31f3756a60..7b2f9b317e 100644 --- a/assets/locales/hi.json +++ b/assets/locales/hi.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/hr_HR.json b/assets/locales/hr_HR.json index d1cbb434aa..bcd040994a 100644 --- a/assets/locales/hr_HR.json +++ b/assets/locales/hr_HR.json @@ -12,7 +12,6 @@ "Survey": "Anketa", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Moji predmeti", "Attach": "Attach", "Home": "Po\u010detna stranica", diff --git a/assets/locales/hu_HU.json b/assets/locales/hu_HU.json index 480cf68064..7b422811f2 100644 --- a/assets/locales/hu_HU.json +++ b/assets/locales/hu_HU.json @@ -12,7 +12,6 @@ "Survey": "K\u00e9rd\u0151\u00edv", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Kurzusaim", "Attach": "Attach", "Home": "Kezd\u0151lap", diff --git a/assets/locales/id_ID.json b/assets/locales/id_ID.json index fbcdadc933..47f8642b2b 100644 --- a/assets/locales/id_ID.json +++ b/assets/locales/id_ID.json @@ -11,7 +11,6 @@ "Member": "Anggota", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Daftar Mata Kuliah Ku", "Attach": "Attach", "Home": "Home", diff --git a/assets/locales/it.json b/assets/locales/it.json index 1ed7421bfa..c7b460f4f7 100644 --- a/assets/locales/it.json +++ b/assets/locales/it.json @@ -18,7 +18,7 @@ "Surveys": "Questionari", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Configurazione del modulo", "Sign in": "Accedi", "My courses": "Corsi", "Start recording": "Inizia la registrazione", diff --git a/assets/locales/ja.json b/assets/locales/ja.json index 75d3925e97..f29eaad5d6 100644 --- a/assets/locales/ja.json +++ b/assets/locales/ja.json @@ -13,7 +13,7 @@ "Surveys": "\u610f\u898b\u8abf\u67fb", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "\u30b3\u30fc\u30b9\u306e\u8a2d\u5b9a", "My courses": "\u30b3\u30fc\u30b9", "Attach": "Attach", "Home": "\u30db\u30fc\u30e0", diff --git a/assets/locales/ka_GE.json b/assets/locales/ka_GE.json index 31f3756a60..7b2f9b317e 100644 --- a/assets/locales/ka_GE.json +++ b/assets/locales/ka_GE.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/ko_KR.json b/assets/locales/ko_KR.json index 65f9197a44..9419630a56 100644 --- a/assets/locales/ko_KR.json +++ b/assets/locales/ko_KR.json @@ -8,7 +8,6 @@ "Groups": "\uadf8\ub8f9", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "\ub0b4 \uacfc\uc815", "Attach": "Attach", "Home": "\ud648", diff --git a/assets/locales/lt_LT.json b/assets/locales/lt_LT.json index 6ed9d3410e..a33c6b59c1 100644 --- a/assets/locales/lt_LT.json +++ b/assets/locales/lt_LT.json @@ -12,7 +12,6 @@ "Survey": "Apklausa", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Mano kursai", "Attach": "Attach", "Home": "Pagrindinis puslapis", diff --git a/assets/locales/lv_LV.json b/assets/locales/lv_LV.json index abbec4c39f..8b3044d5b5 100644 --- a/assets/locales/lv_LV.json +++ b/assets/locales/lv_LV.json @@ -18,7 +18,7 @@ "Surveys": "Aptaujas", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Kursa iestat\u012bjumi", "My courses": "Mani kursi", "Attach": "Attach", "Home": "Mana profila lapas s\u0101kums", diff --git a/assets/locales/mk_MK.json b/assets/locales/mk_MK.json index 21631d1c45..87b580b6ff 100644 --- a/assets/locales/mk_MK.json +++ b/assets/locales/mk_MK.json @@ -12,7 +12,6 @@ "Survey": "\u0410\u043d\u043a\u0435\u0442\u0430", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "\u041c\u043e\u0438 \u043a\u0443\u0440\u0441\u0435\u0432\u0438", "Attach": "Attach", "Home": "\u041f\u043e\u0447\u0435\u0442\u043e\u043a", diff --git a/assets/locales/ms_MY.json b/assets/locales/ms_MY.json index a02a99f359..e1b1e341d3 100644 --- a/assets/locales/ms_MY.json +++ b/assets/locales/ms_MY.json @@ -6,7 +6,6 @@ "Groups": "Kumpulan", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Senarai Subjek", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", diff --git a/assets/locales/nl.json b/assets/locales/nl.json index 5e853a14e0..2cc6ab89cf 100644 --- a/assets/locales/nl.json +++ b/assets/locales/nl.json @@ -18,7 +18,7 @@ "Surveys": "Enquete", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Cursus instellingen", "Sign in": "Meld aan", "My courses": "Mijn cursussen", "Attach": "Attach", diff --git a/assets/locales/nn_NO.json b/assets/locales/nn_NO.json index 5f7be614d1..482b88b5e7 100644 --- a/assets/locales/nn_NO.json +++ b/assets/locales/nn_NO.json @@ -5,7 +5,6 @@ "Link": "Lenke", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Mine kurs", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", diff --git a/assets/locales/oc.json b/assets/locales/oc.json index a45a620fc1..ac56f66c00 100644 --- a/assets/locales/oc.json +++ b/assets/locales/oc.json @@ -18,7 +18,7 @@ "Surveys": "Surveys", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Course settings", "Sign in": "Sign in", "My courses": "My courses", "Start recording": "Start recording", diff --git a/assets/locales/pl_PL.json b/assets/locales/pl_PL.json index 12d2e87cee..7465e0b681 100644 --- a/assets/locales/pl_PL.json +++ b/assets/locales/pl_PL.json @@ -18,7 +18,7 @@ "Surveys": "Ankiety", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Ustawienia kursu", "My courses": "Lista moich kurs\u00f3w", "Attach": "Attach", "Home": "Strona g\u0142\u00f3wna", diff --git a/assets/locales/ps.json b/assets/locales/ps.json index 31f3756a60..7b2f9b317e 100644 --- a/assets/locales/ps.json +++ b/assets/locales/ps.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/pt_PT.json b/assets/locales/pt_PT.json index d73e979d0e..a55ffa182a 100644 --- a/assets/locales/pt_PT.json +++ b/assets/locales/pt_PT.json @@ -18,7 +18,7 @@ "Surveys": "Pesquisas", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Configura\u00e7\u00f5es do curso", "Sign in": "Entrar", "My courses": "Cursos", "Attach": "Attach", diff --git a/assets/locales/qu_PE.json b/assets/locales/qu_PE.json index 63f2c12d9b..2f9b329e97 100644 --- a/assets/locales/qu_PE.json +++ b/assets/locales/qu_PE.json @@ -13,7 +13,7 @@ "Survey": "Tapukuy", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Configuraci\u00f3n de la lecci\u00f3n", "My courses": "Noqapaq yachachiykuna", "Attach": "Attach", "Home": "Qallariq", diff --git a/assets/locales/ro_RO.json b/assets/locales/ro_RO.json index 1f6a8fa0a5..2a3ab59f77 100644 --- a/assets/locales/ro_RO.json +++ b/assets/locales/ro_RO.json @@ -17,7 +17,7 @@ "Surveys": "Sondaje", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Setarile cursului", "Sign in": "Conectare", "My courses": "Cursurile mele", "Attach": "Attach", diff --git a/assets/locales/ru_RU.json b/assets/locales/ru_RU.json index 0c0184ab1a..2c85be730f 100644 --- a/assets/locales/ru_RU.json +++ b/assets/locales/ru_RU.json @@ -14,7 +14,6 @@ "Surveys": "\u041e\u043f\u0440\u043e\u0441\u043d\u0438\u043a\u0438", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "\u041c\u043e\u0438 \u043a\u0443\u0440\u0441\u044b", "Attach": "Attach", "Home": "\u041d\u0430\u0447\u0430\u043b\u043e (\u0434\u043e\u043c\u043e\u0439)", diff --git a/assets/locales/sk_SK.json b/assets/locales/sk_SK.json index bff3466c1c..43dabac65b 100644 --- a/assets/locales/sk_SK.json +++ b/assets/locales/sk_SK.json @@ -18,7 +18,7 @@ "Surveys": "Prieskum", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Nastavenie kurzu", "Sign in": "Prihl\u00e1si\u0165 sa", "My courses": "Moje kurzy", "Attach": "Attach", diff --git a/assets/locales/sl_SI.json b/assets/locales/sl_SI.json index 65012f7819..caa698a414 100644 --- a/assets/locales/sl_SI.json +++ b/assets/locales/sl_SI.json @@ -18,7 +18,7 @@ "Surveys": "Evalvacijski vpra\u0161alniki", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Nastavitve te\u010daja", "Sign in": "Vpi\u0161i", "My courses": "Moji te\u010daji", "Start recording": "Pri\u010dni snemanje", diff --git a/assets/locales/so_SO.json b/assets/locales/so_SO.json index 31f3756a60..7b2f9b317e 100644 --- a/assets/locales/so_SO.json +++ b/assets/locales/so_SO.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/sr_RS.json b/assets/locales/sr_RS.json index abff76ac73..437df1ce68 100644 --- a/assets/locales/sr_RS.json +++ b/assets/locales/sr_RS.json @@ -9,7 +9,7 @@ "Groups": "Grupe", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Pode\u0161avanje kursa", "My courses": "Moja lista kurseva", "Attach": "Attach", "Home": "Po\u010detak", diff --git a/assets/locales/sv_SE.json b/assets/locales/sv_SE.json index e85777726f..57167b27c5 100644 --- a/assets/locales/sv_SE.json +++ b/assets/locales/sv_SE.json @@ -14,7 +14,7 @@ "Surveys": "Unders\u00f6kning", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Inst\u00e4llningar", "My courses": "Mina kurser", "Attach": "Attach", "Home": "Hem", diff --git a/assets/locales/sw_KE.json b/assets/locales/sw_KE.json index 31f3756a60..7b2f9b317e 100644 --- a/assets/locales/sw_KE.json +++ b/assets/locales/sw_KE.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/th.json b/assets/locales/th.json index a0d2519d9c..c0ff753880 100644 --- a/assets/locales/th.json +++ b/assets/locales/th.json @@ -10,7 +10,6 @@ "Groups": "\u0e01\u0e25\u0e38\u0e48\u0e21", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "\u0e23\u0e32\u0e22\u0e27\u0e34\u0e0a\u0e32\u0e02\u0e2d\u0e07\u0e40\u0e23\u0e32", "Attach": "Attach", "Home": "\u0e2b\u0e19\u0e49\u0e32\u0e41\u0e23\u0e01", diff --git a/assets/locales/tl_PH.json b/assets/locales/tl_PH.json index 34b95cd4c1..14e628f6e5 100644 --- a/assets/locales/tl_PH.json +++ b/assets/locales/tl_PH.json @@ -13,7 +13,7 @@ "Surveys": "Mga Sarbey", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Mga setting ng kurso", "My courses": "Aking Mga Kurso", "Attach": "Attach", "Home": "Home", diff --git a/assets/locales/tr.json b/assets/locales/tr.json index 73a5a25d1f..d406487b35 100644 --- a/assets/locales/tr.json +++ b/assets/locales/tr.json @@ -12,7 +12,6 @@ "Member": "\u00dcye", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Derslerim", "Attach": "Attach", "Home": "Ana sayfa", diff --git a/assets/locales/uk_UA.json b/assets/locales/uk_UA.json index 22eac68f18..6291a80916 100644 --- a/assets/locales/uk_UA.json +++ b/assets/locales/uk_UA.json @@ -11,7 +11,6 @@ "Survey": "\u041e\u043f\u0438\u0442\u0443\u0432\u0430\u043d\u043d\u044f", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "\u041c\u043e\u0457 \u043a\u0443\u0440\u0441\u0438", "Attach": "Attach", "Home": "\u0414\u043e\u0434\u043e\u043c\u0443", diff --git a/assets/locales/vi_VN.json b/assets/locales/vi_VN.json index e149e3ddba..72fa9a27ff 100644 --- a/assets/locales/vi_VN.json +++ b/assets/locales/vi_VN.json @@ -6,7 +6,6 @@ "Groups": "S\u1eeda Nh\u00f3m", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Danh m\u1ee5c kho\u00e1 h\u1ecdc", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", diff --git a/assets/locales/xh_ZA.json b/assets/locales/xh_ZA.json index 31f3756a60..7b2f9b317e 100644 --- a/assets/locales/xh_ZA.json +++ b/assets/locales/xh_ZA.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/yo_NG.json b/assets/locales/yo_NG.json index 31f3756a60..7b2f9b317e 100644 --- a/assets/locales/yo_NG.json +++ b/assets/locales/yo_NG.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/zh_CN.json b/assets/locales/zh_CN.json index 29c823b0f0..2b1daaa072 100644 --- a/assets/locales/zh_CN.json +++ b/assets/locales/zh_CN.json @@ -17,7 +17,7 @@ "Surveys": "\u8c03\u67e5", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "\u8bfe\u7a0b\u8bbe\u7f6e", "My courses": "\u6211\u7684\u8bfe\u7a0b", "Attach": "Attach", "Home": "\u4e3b\u9875", diff --git a/assets/locales/zh_TW.json b/assets/locales/zh_TW.json index b3e2ae9bb3..54eca79525 100644 --- a/assets/locales/zh_TW.json +++ b/assets/locales/zh_TW.json @@ -12,7 +12,6 @@ "Survey": "\u7d71\u8a08\u8abf\u67e5", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "\u6211\u7684\u8ab2\u7a0b", "Attach": "Attach", "Home": "\u9996\u9801", diff --git a/assets/vue/router/index.js b/assets/vue/router/index.js index aa70794570..edc7527b34 100644 --- a/assets/vue/router/index.js +++ b/assets/vue/router/index.js @@ -116,7 +116,7 @@ const router = createRouter({ return false } - const course = await courseService.getCourseDetails(courseId) + const course = await courseService.findById(courseId, { sid: sessionId }) if (!course) { return false } diff --git a/assets/vue/services/courseService.js b/assets/vue/services/courseService.js index 60e65ec3ca..1f6e10737b 100644 --- a/assets/vue/services/courseService.js +++ b/assets/vue/services/courseService.js @@ -4,6 +4,13 @@ import baseService from "./baseService" export default { find: baseService.get, + /** + * @param {number} cid + * @param {object} params + * @returns {Promise} + */ + findById: async (cid, params) => baseService.get(`/api/courses/${cid}`, params), + /** * @param {number} courseId * @param {number=} sessionId @@ -107,22 +114,6 @@ export default { })) }, - /** - * Fetches course details by course ID. - * - * @param {number} courseId - The ID of the course. - * @returns {Promise} - The course details or null if an error occurs. - */ - getCourseDetails: async (courseId) => { - try { - const response = await api.get(`/api/courses/${courseId}`) - return response.data - } catch (error) { - console.error("Error fetching course details:", error) - return null - } - }, - /** * Retrieves the ID of the auto-launchable exercise in a course, if configured. * @@ -136,16 +127,16 @@ export default { params: { sid: sessionId, }, - }); + }) if (data && data.exerciseId) { - return data.exerciseId; + return data.exerciseId } - return null; + return null } catch (error) { - console.error("Error fetching auto-launch exercise ID:", error); - return null; + console.error("Error fetching auto-launch exercise ID:", error) + return null } }, /** @@ -161,16 +152,16 @@ export default { params: { sid: sessionId, }, - }); + }) if (data && data.lpId) { - return data.lpId; + return data.lpId } - return null; + return null } catch (error) { - console.error("Error fetching auto-launch LP ID:", error); - return null; + console.error("Error fetching auto-launch LP ID:", error) + return null } }, } diff --git a/public/img/copy.png b/public/img/copy.png new file mode 100755 index 0000000000..2cc17b79a8 Binary files /dev/null and b/public/img/copy.png differ diff --git a/public/img/icons/32/attendance_list.png b/public/img/icons/32/attendance_list.png new file mode 100755 index 0000000000..98bafe29b2 Binary files /dev/null and b/public/img/icons/32/attendance_list.png differ diff --git a/public/img/icons/32/scorms.png b/public/img/icons/32/scorms.png new file mode 100755 index 0000000000..d88202c033 Binary files /dev/null and b/public/img/icons/32/scorms.png differ diff --git a/public/img/icons/32/scorms_na.png b/public/img/icons/32/scorms_na.png new file mode 100755 index 0000000000..38e90a12ed Binary files /dev/null and b/public/img/icons/32/scorms_na.png differ diff --git a/public/img/icons/32/security.png b/public/img/icons/32/security.png new file mode 100755 index 0000000000..80e060dbf6 Binary files /dev/null and b/public/img/icons/32/security.png differ diff --git a/public/img/icons/32/security_na.png b/public/img/icons/32/security_na.png new file mode 100644 index 0000000000..3a63ede910 Binary files /dev/null and b/public/img/icons/32/security_na.png differ diff --git a/public/img/icons/32/tools.png b/public/img/icons/32/tools.png new file mode 100755 index 0000000000..cf8e01b2ba Binary files /dev/null and b/public/img/icons/32/tools.png differ diff --git a/public/img/icons/32/tools_na.png b/public/img/icons/32/tools_na.png new file mode 100755 index 0000000000..ee95e934f7 Binary files /dev/null and b/public/img/icons/32/tools_na.png differ diff --git a/public/img/icons/32/user.png b/public/img/icons/32/user.png new file mode 100755 index 0000000000..4249eddc7c Binary files /dev/null and b/public/img/icons/32/user.png differ diff --git a/public/img/icons/32/user_na.png b/public/img/icons/32/user_na.png new file mode 100755 index 0000000000..8e569a12e2 Binary files /dev/null and b/public/img/icons/32/user_na.png differ diff --git a/public/img/save_pack.png b/public/img/save_pack.png new file mode 100755 index 0000000000..85e2258541 Binary files /dev/null and b/public/img/save_pack.png differ diff --git a/public/main/auth/inscription.php b/public/main/auth/inscription.php index 15c713b32a..b97a55ff63 100644 --- a/public/main/auth/inscription.php +++ b/public/main/auth/inscription.php @@ -2,6 +2,7 @@ /* For licensing terms, see /license.txt */ use Chamilo\CoreBundle\Entity\User; +use Chamilo\CoreBundle\Framework\Container; use Chamilo\CoreBundle\ServiceHelper\ContainerHelper; use ChamiloSession as Session; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; @@ -1159,6 +1160,7 @@ if ($form->validate()) { $token = new UsernamePasswordToken($userEntity, $providerKey, $roles); $container->get(ContainerHelper::class)->getTokenStorage()->setToken($token); + $request = $container->get('request_stack')->getMainRequest(); $sessionHandler = $container->get('request_stack')->getSession(); $sessionHandler->set('_security_' . $providerKey, serialize($token)); $userData = [ @@ -1174,9 +1176,11 @@ if ($form->validate()) { $is_allowedCreateCourse = isset($values['status']) && 1 == $values['status']; $sessionHandler->set('is_allowedCreateCourse', $is_allowedCreateCourse); - // Stats - //Event::eventLogin($user_id); + Container::getTrackELoginRepository() + ->createLoginRecord($userEntity, new DateTime(), $request->getClientIp()) + ; + // @todo implement Auto-subscribe according to STATUS_autosubscribe setting // last user login date is now $user_last_login_datetime = 0; // used as a unix timestamp it will correspond to : 1 1 1970 diff --git a/public/main/course_info/infocours.php b/public/main/course_info/infocours.php index e5ccbe46fb..3efd1f27d0 100644 --- a/public/main/course_info/infocours.php +++ b/public/main/course_info/infocours.php @@ -311,7 +311,7 @@ $group[] = $form->createElement( 'radio', 'email_alert_to_teacher_on_new_user_in_course', null, - get_lang('To teachar and tutor'), + get_lang('To teacher and tutor'), 2 ); $group[] = $form->createElement( diff --git a/public/main/inc/lib/TrackingCourseLog.php b/public/main/inc/lib/TrackingCourseLog.php new file mode 100644 index 0000000000..e458ae55b6 --- /dev/null +++ b/public/main/inc/lib/TrackingCourseLog.php @@ -0,0 +1,1318 @@ +total_number_of_items; + } + + /** + * Retrieves item resources data with pagination and sorting options. + */ + public static function getItemResourcesData($from, $numberOfItems, $column, $direction): array + { + $sessionId = api_get_session_id(); + $courseId = api_get_course_int_id(); + + $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY); + $tableUser = Database::get_main_table(TABLE_MAIN_USER); + $tableSession = Database::get_main_table(TABLE_MAIN_SESSION); + $column = (int) $column; + $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction; + + $sql = "SELECT + tool as col0, + lastedit_type as col1, + ref as ref, + user.username as col3, + insert_date as col6, + visibility as col7, + user.user_id as user_id + FROM $tableItemProperty track_resource, $tableUser user + WHERE + track_resource.c_id = $courseId AND + track_resource.insert_user_id = user.user_id AND + session_id ".(empty($sessionId) ? ' IS NULL ' : " = $sessionId "); + + if (isset($_GET['keyword'])) { + $keyword = Database::escape_string(trim($_GET['keyword'])); + $sql .= " AND ( + user.username LIKE '%".$keyword."%' OR + lastedit_type LIKE '%".$keyword."%' OR + tool LIKE '%".$keyword."%' + ) "; + } + + $sql .= " AND tool IN ( + 'document', + 'learnpath', + 'quiz', + 'glossary', + 'link', + 'course_description', + 'announcement', + 'thematic', + 'thematic_advance', + 'thematic_plan' + )"; + + if (0 == $column) { + $column = '0'; + } + if ('' != $column && '' != $direction) { + if (2 != $column && 4 != $column) { + $sql .= " ORDER BY col$column $direction"; + } + } else { + $sql .= " ORDER BY col6 DESC "; + } + + $from = intval($from); + if ($from) { + $numberOfItems = intval($numberOfItems); + $sql .= " LIMIT $from, $numberOfItems "; + } + + $res = Database::query($sql); + $resources = []; + $thematicTools = ['thematic', 'thematic_advance', 'thematic_plan']; + while ($row = Database::fetch_array($res)) { + $ref = $row['ref']; + $tableName = self::getToolNameTable($row['col0']); + $tableTool = Database::get_course_table($tableName['table_name']); + + $id = $tableName['id_tool']; + $recorset = false; + + if (in_array($row['col0'], ['thematic_plan', 'thematic_advance'])) { + $tblThematic = Database::get_course_table(TABLE_THEMATIC); + $sql = "SELECT thematic_id FROM $tableTool + WHERE c_id = $courseId AND id = $ref"; + $rsThematic = Database::query($sql); + if (Database::num_rows($rsThematic)) { + $rowThematic = Database::fetch_array($rsThematic); + $thematicId = $rowThematic['thematic_id']; + + $sql = "SELECT session.id, session.name, user.username + FROM $tblThematic t, $tableSession session, $tableUser user + WHERE + t.c_id = $courseId AND + t.session_id = session.id AND + session.id_coach = user.user_id AND + t.id = $thematicId"; + $recorset = Database::query($sql); + } + } else { + $sql = "SELECT session.id, session.name, user.username + FROM $tableTool tool, $tableSession session, $tableUser user + WHERE + tool.c_id = $courseId AND + tool.session_id = session.id AND + session.id_coach = user.user_id AND + tool.$id = $ref"; + $recorset = Database::query($sql); + } + + if (!empty($recorset)) { + $obj = Database::fetch_object($recorset); + + $nameSession = ''; + $coachName = ''; + if (!empty($obj)) { + $nameSession = $obj->name; + $coachName = $obj->username; + } + + $urlTool = api_get_path(WEB_CODE_PATH).$tableName['link_tool']; + + if ($row['col6'] != 2) { + if (in_array($row['col0'], $thematicTools)) { + $expThematicTool = explode('_', $row['col0']); + $thematicTooltitle = ''; + if (is_array($expThematicTool)) { + foreach ($expThematicTool as $exp) { + $thematicTooltitle .= api_ucfirst($exp); + } + } else { + $thematicTooltitle = api_ucfirst($row['col0']); + } + + $row[0] = ''.get_lang( + $thematicTooltitle + ).''; + } else { + $row[0] = ''.get_lang( + 'Tool'.api_ucfirst($row['col0']) + ).''; + } + } else { + $row[0] = api_ucfirst($row['col0']); + } + $row[1] = get_lang($row[1]); + $row[6] = api_convert_and_format_date($row['col6'], null, date_default_timezone_get()); + $row[5] = ''; + //@todo Improve this code please + switch ($tableName['table_name']) { + case 'document': + $sql = "SELECT tool.title as title FROM $tableTool tool + WHERE c_id = $courseId AND id = $ref"; + $rsDocument = Database::query($sql); + $objDocument = Database::fetch_object($rsDocument); + if ($objDocument) { + $row[5] = $objDocument->title; + } + break; + case 'quiz': + case 'course_description': + case 'announcement': + $sql = "SELECT title FROM $tableTool + WHERE c_id = $courseId AND id = $ref"; + $rsDocument = Database::query($sql); + $objDocument = Database::fetch_object($rsDocument); + if ($objDocument) { + $row[5] = $objDocument->title; + } + break; + case 'glossary': + $sql = "SELECT name FROM $tableTool + WHERE c_id = $courseId AND glossary_id = $ref"; + $rsDocument = Database::query($sql); + $objDocument = Database::fetch_object($rsDocument); + if ($objDocument) { + $row[5] = $objDocument->name; + } + break; + case 'lp': + $sql = "SELECT name + FROM $tableTool WHERE c_id = $courseId AND id = $ref"; + $rsDocument = Database::query($sql); + $objDocument = Database::fetch_object($rsDocument); + $row[5] = $objDocument->name; + break; + case 'thematic_plan': + case 'thematic': + $rs = Database::query("SELECT title FROM $tableTool WHERE c_id = $courseId AND id = $ref"); + if (Database::num_rows($rs) > 0) { + $obj = Database::fetch_object($rs); + if ($obj) { + $row[5] = $obj->title; + } + } + break; + case 'thematic_advance': + $rs = Database::query("SELECT content FROM $tableTool WHERE c_id = $courseId AND id = $ref"); + if (Database::num_rows($rs) > 0) { + $obj = Database::fetch_object($rs); + if ($obj) { + $row[5] = $obj->content; + } + } + break; + case 'thematic_plan': + $rs = Database::query("SELECT title FROM $tableTool WHERE c_id = $courseId AND id = $ref"); + if (Database::num_rows($rs) > 0) { + $obj = Database::fetch_object($rs); + if ($obj) { + $row[5] = $obj->title; + } + } + break; + default: + break; + } + + $row2 = $nameSession; + if (!empty($coachName)) { + $row2 .= '
'.get_lang('Coach').': '.$coachName; + } + $row[2] = $row2; + if (!empty($row['col3'])) { + $userInfo = api_get_user_info($row['user_id']); + $row['col3'] = Display::url( + $row['col3'], + $userInfo['profile_url'] + ); + $row[3] = $row['col3']; + + $ip = Tracking::get_ip_from_user_event( + $row['user_id'], + $row['col6'], + true + ); + if (empty($ip)) { + $ip = get_lang('Unknown'); + } + $row[4] = $ip; + } + + $resources[] = $row; + } + } + + return $resources; + } + + /** + * Retrieves the name and associated table for a given tool. + */ + public static function getToolNameTable(string $tool): array + { + $linkTool = ''; + $idTool = ''; + + switch ($tool) { + case 'document': + $tableName = TABLE_DOCUMENT; + $linkTool = 'document/document.php'; + $idTool = 'id'; + break; + case 'learnpath': + $tableName = TABLE_LP_MAIN; + $linkTool = 'lp/lp_controller.php'; + $idTool = 'id'; + break; + case 'quiz': + $tableName = TABLE_QUIZ_TEST; + $linkTool = 'exercise/exercise.php'; + $idTool = 'iid'; + break; + case 'glossary': + $tableName = TABLE_GLOSSARY; + $linkTool = 'glossary/index.php'; + $idTool = 'glossary_id'; + break; + case 'link': + $tableName = TABLE_LINK; + $linkTool = 'link/link.php'; + $idTool = 'id'; + break; + case 'course_description': + $tableName = TABLE_COURSE_DESCRIPTION; + $linkTool = 'course_description/'; + $idTool = 'id'; + break; + case 'announcement': + $tableName = TABLE_ANNOUNCEMENT; + $linkTool = 'announcements/announcements.php'; + $idTool = 'id'; + break; + case 'thematic': + $tableName = TABLE_THEMATIC; + $linkTool = 'course_progress/index.php'; + $idTool = 'id'; + break; + case 'thematic_advance': + $tableName = TABLE_THEMATIC_ADVANCE; + $linkTool = 'course_progress/index.php'; + $idTool = 'id'; + break; + case 'thematic_plan': + $tableName = TABLE_THEMATIC_PLAN; + $linkTool = 'course_progress/index.php'; + $idTool = 'id'; + break; + default: + $tableName = $tool; + break; + } + + return [ + 'table_name' => $tableName, + 'link_tool' => $linkTool, + 'id_tool' => $idTool, + ]; + } + + /** + * Displays additional profile fields, excluding specific fields if provided. + */ + public static function displayAdditionalProfileFields(array $exclude = [], $formAction = null): string + { + $formAction = $formAction ?: 'courseLog.php'; + + // getting all the extra profile fields that are defined by the platform administrator + $extraFields = UserManager::get_extra_fields(0, 50); + + // creating the form + $return = '
'; + // the select field with the additional user profile fields, this is where we select the field of which we want to see + // the information the users have entered or selected. + $return .= '
'; + $return .= ''; + $return .= '
'; + + // the form elements for the $_GET parameters (because the form is passed through GET + foreach ($_GET as $key => $value) { + if ($key != 'additional_profile_field') { + $return .= ''; + } + } + // the submit button + $return .= '
'; + $return .= ''; + $return .= '
'; + $return .= '
'; + + return $extraFieldsToShow > 0 ? $return : ''; + } + + /** + * This function gets all the information of a certrain ($field_id) + * additional profile field for a specific list of users is more efficent + * than get_addtional_profile_information_of_field() function + * It gets the information of all the users so that it can be displayed + * in the sortable table or in the csv or xls export. + * + * @param int $fieldId field id + * @param array $users list of user ids + * + * @author Julio Montoya + * + * @since Nov 2009 + * + * @version 1.8.6.2 + */ + public static function getAdditionalProfileInformationOfFieldByUser($fieldId, $users): array + { + // Database table definition + $tableUser = Database::get_main_table(TABLE_MAIN_USER); + $tableUserFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES); + $extraField = Database::get_main_table(TABLE_EXTRA_FIELD); + $resultExtraField = UserManager::get_extra_field_information($fieldId); + $return = []; + if (!empty($users)) { + if ($resultExtraField['field_type'] == UserManager::USER_FIELD_TYPE_TAG) { + foreach ($users as $user_id) { + $userResult = UserManager::get_user_tags($user_id, $fieldId); + $tagList = []; + foreach ($userResult as $item) { + $tagList[] = $item['tag']; + } + $return[$user_id][] = implode(', ', $tagList); + } + } else { + $newUserArray = []; + foreach ($users as $user_id) { + $newUserArray[] = "'".$user_id."'"; + } + $users = implode(',', $newUserArray); + $extraFieldType = EntityExtraField::USER_FIELD_TYPE; + // Selecting only the necessary information NOT ALL the user list + $sql = "SELECT user.user_id, v.value + FROM $tableUser user + INNER JOIN $tableUserFieldValues v + ON (user.user_id = v.item_id) + INNER JOIN $extraField f + ON (f.id = v.field_id) + WHERE + f.extra_field_type = $extraFieldType AND + v.field_id=".intval($fieldId)." AND + user.user_id IN ($users)"; + + $result = Database::query($sql); + while ($row = Database::fetch_array($result)) { + // get option value for field type double select by id + if (!empty($row['value'])) { + if ($resultExtraField['field_type'] == + ExtraField::FIELD_TYPE_DOUBLE_SELECT + ) { + $idDoubleSelect = explode(';', $row['value']); + if (is_array($idDoubleSelect)) { + $value1 = $resultExtraField['options'][$idDoubleSelect[0]]['option_value']; + $value2 = $resultExtraField['options'][$idDoubleSelect[1]]['option_value']; + $row['value'] = ($value1.';'.$value2); + } + } + + if ($resultExtraField['field_type'] == ExtraField::FIELD_TYPE_SELECT_WITH_TEXT_FIELD) { + $parsedValue = explode('::', $row['value']); + + if ($parsedValue) { + $value1 = $resultExtraField['options'][$parsedValue[0]]['display_text']; + $value2 = $parsedValue[1]; + + $row['value'] = "$value1: $value2"; + } + } + + if ($resultExtraField['field_type'] == ExtraField::FIELD_TYPE_TRIPLE_SELECT) { + [$level1, $level2, $level3] = explode(';', $row['value']); + + $row['value'] = $resultExtraField['options'][$level1]['display_text'].' / '; + $row['value'] .= $resultExtraField['options'][$level2]['display_text'].' / '; + $row['value'] .= $resultExtraField['options'][$level3]['display_text']; + } + } + // get other value from extra field + $return[$row['user_id']][] = $row['value']; + } + } + } + + return $return; + } + + /** + * Get number of users for sortable with pagination. + */ + public static function getNumberOfUsers(array $conditions): int + { + $conditions['get_count'] = true; + + return self::getUserData(0, 0, 0, '', $conditions); + } + + /** + * Get data for users list in sortable with pagination. + */ + public static function getUserData( + $from, + $numberOfItems, + $column, + $direction, + array $conditions = [], + bool $exerciseToCheckConfig = true, + bool $displaySessionInfo = false, + ?string $courseCode = null, + ?int $sessionId = null, + bool $exportCsv = false, + array $userIds = [] + ) { + $includeInvitedUsers = $conditions['include_invited_users'] ?? false; + $getCount = $conditions['get_count'] ?? false; + + $csvContent = []; + $tblUser = Database::get_main_table(TABLE_MAIN_USER); + $tblUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER); + $accessUrlId = api_get_current_access_url_id(); + + if (!empty($userIds) && is_array($userIds)) { + $userIds = array_map('intval', $userIds); + $conditionUser = " WHERE user.id IN (".implode(',', $userIds).") "; + } else { + $conditionUser = " WHERE user.id = " . (int) $userIds; + } + + if (!empty($_GET['user_keyword'])) { + $keyword = trim(Database::escape_string($_GET['user_keyword'])); + $conditionUser .= " AND ( + user.firstname LIKE '%".$keyword."%' OR + user.lastname LIKE '%".$keyword."%' OR + user.username LIKE '%".$keyword."%' OR + user.email LIKE '%".$keyword."%' + ) "; + } + + $urlTable = ''; + $urlCondition = ''; + if (api_is_multiple_url_enabled()) { + $urlTable = " INNER JOIN $tblUrlRelUser as url_users ON (user.id = url_users.user_id)"; + $urlCondition = " AND access_url_id = '$accessUrlId'"; + } + + $invitedUsersCondition = ''; + if (!$includeInvitedUsers) { + $invitedUsersCondition = " AND user.status != ".INVITEE; + } + + $select = ' + SELECT user.id as user_id, + user.official_code as col0, + user.lastname as col1, + user.firstname as col2, + user.username as col3, + user.email as col4'; + + if ($getCount) { + $select = ' SELECT COUNT(distinct(user.id)) as count '; + } + + $sqlInjectJoins = ''; + $where = 'AND 1 = 1 '; + $sqlInjectWhere = ''; + if (!empty($conditions)) { + if (isset($conditions['inject_joins'])) { + $sqlInjectJoins = $conditions['inject_joins']; + } + if (isset($conditions['where'])) { + $where = $conditions['where']; + } + if (isset($conditions['inject_where'])) { + $sqlInjectWhere = $conditions['inject_where']; + } + $injectExtraFields = !empty($conditions['inject_extra_fields']) ? $conditions['inject_extra_fields'] : 1; + $injectExtraFields = rtrim($injectExtraFields, ', '); + if (false === $getCount) { + $select .= " , $injectExtraFields"; + } + } + + $sql = "$select + FROM $tblUser as user + $urlTable + $sqlInjectJoins + $conditionUser + $urlCondition + $invitedUsersCondition + $where + $sqlInjectWhere + "; + + if (!in_array($direction, ['ASC', 'DESC'])) { + $direction = 'ASC'; + } + + $column = $column <= 2 ? (int) $column : 0; + $from = (int) $from; + $numberOfItems = (int) $numberOfItems; + + if ($getCount) { + $res = Database::query($sql); + $row = Database::fetch_array($res); + + return $row['count']; + } + + $sortByFirstName = api_sort_by_first_name(); + + if ($sortByFirstName) { + if ($column == 1) { + $column = 2; + } elseif ($column == 2) { + $column = 1; + } + } + + $sql .= " ORDER BY col$column $direction "; + $sql .= " LIMIT $from, $numberOfItems"; + + $res = Database::query($sql); + $users = []; + + $courseInfo = api_get_course_info($courseCode); + $courseId = $courseInfo['real_id']; + + $totalSurveys = 0; + $totalExercises = ExerciseLib::get_all_exercises( + $courseInfo, + $sessionId + ); + + if (empty($sessionId)) { + $surveyUserList = []; + $surveyList = SurveyManager::get_surveys($courseCode); + if ($surveyList) { + $totalSurveys = count($surveyList); + foreach ($surveyList as $survey) { + $userList = SurveyManager::get_people_who_filled_survey( + $survey['survey_id'], + false, + $courseId + ); + + foreach ($userList as $user_id) { + isset($surveyUserList[$user_id]) ? $surveyUserList[$user_id]++ : $surveyUserList[$user_id] = 1; + } + } + } + } + + $urlBase = api_get_path(WEB_CODE_PATH).'my_space/myStudents.php?details=true&cid='.$courseId. + '&course='.$courseCode.'&origin=tracking_course&sid='.$sessionId; + + Session::write('user_id_list', []); + $userIdList = []; + + if ($exerciseToCheckConfig) { + $addExerciseOption = api_get_setting('exercise.add_exercise_best_attempt_in_report', true); + $exerciseResultsToCheck = []; + if (!empty($addExerciseOption) && isset($addExerciseOption['courses']) && + isset($addExerciseOption['courses'][$courseCode]) + ) { + foreach ($addExerciseOption['courses'][$courseCode] as $exerciseId) { + $exercise = new Exercise(); + $exercise->read($exerciseId); + if ($exercise->iid) { + $exerciseResultsToCheck[] = $exercise; + } + } + } + } + + $lpShowMaxProgress = 'true' === api_get_setting('lp.lp_show_max_progress_instead_of_average'); + if ('true' === api_get_setting('lp.lp_show_max_progress_or_average_enable_course_level_redefinition')) { + $lpShowProgressCourseSetting = api_get_course_setting('lp_show_max_or_average_progress', $courseInfo, true); + if (in_array($lpShowProgressCourseSetting, ['max', 'average'])) { + $lpShowMaxProgress = ('max' === $lpShowProgressCourseSetting); + } + } + + while ($user = Database::fetch_array($res, 'ASSOC')) { + $userIdList[] = $user['user_id']; + $user['official_code'] = $user['col0']; + $user['username'] = $user['col3']; + $user['time'] = api_time_to_hms( + Tracking::get_time_spent_on_the_course( + $user['user_id'], + $courseId, + $sessionId + ) + ); + + $avgStudentScore = Tracking::get_avg_student_score( + $user['user_id'], + api_get_course_entity($courseId), + [], + api_get_session_entity($sessionId) + ); + + $averageBestScore = Tracking::get_avg_student_score( + $user['user_id'], + api_get_course_entity($courseId), + [], + api_get_session_entity($sessionId), + false, + false, + true + ); + + $avgStudentProgress = Tracking::get_avg_student_progress( + $user['user_id'], + api_get_course_entity($courseId), + [], + api_get_session_entity($sessionId) + ); + + if (empty($avgStudentProgress)) { + $avgStudentProgress = 0; + } + $user['average_progress'] = $avgStudentProgress.'%'; + + $totalUserExercise = Tracking::get_exercise_student_progress( + $totalExercises, + $user['user_id'], + $courseId, + $sessionId + ); + + $user['exercise_progress'] = $totalUserExercise; + + $totalUserExercise = Tracking::get_exercise_student_average_best_attempt( + $totalExercises, + $user['user_id'], + $courseId, + $sessionId + ); + + $user['exercise_average_best_attempt'] = $totalUserExercise; + + if (is_numeric($avgStudentScore)) { + $user['student_score'] = $avgStudentScore.'%'; + } else { + $user['student_score'] = $avgStudentScore; + } + + if (is_numeric($averageBestScore)) { + $user['student_score_best'] = $averageBestScore.'%'; + } else { + $user['student_score_best'] = $averageBestScore; + } + + $exerciseResults = []; + if (!empty($exerciseResultsToCheck)) { + foreach ($exerciseResultsToCheck as $exercise) { + $bestExerciseResult = Event::get_best_attempt_exercise_results_per_user( + $user['user_id'], + $exercise->iid, + $courseId, + $sessionId, + false + ); + + $best = null; + if ($bestExerciseResult) { + $best = $bestExerciseResult['exe_result'] / $bestExerciseResult['exe_weighting']; + $best = round($best, 2) * 100; + $best .= '%'; + } + $exerciseResults['exercise_'.$exercise->iid] = $best; + } + } + + $user['first_connection'] = Tracking::get_first_connection_date_on_the_course( + $user['user_id'], + $courseId, + $sessionId, + !$exportCsv + ); + + $user['last_connection'] = Tracking::get_last_connection_date_on_the_course( + $user['user_id'], + $courseInfo, + $sessionId, + !$exportCsv + ); + + + $user['count_assignments'] = Tracking::countStudentPublications( + $courseId, + $sessionId + ); + + $user['count_messages'] = Tracking::countStudentMessages( + $courseId, + $sessionId + ); + + + $user['lp_finalization_date'] = Tracking::getCourseLpFinalizationDate( + $user['user_id'], + $courseId, + $sessionId, + !$exportCsv + ); + + $user['quiz_finalization_date'] = Tracking::getCourseQuizLastFinalizationDate( + $user['user_id'], + $courseId, + $sessionId, + !$exportCsv + ); + + if ($exportCsv) { + if (!empty($user['first_connection'])) { + $user['first_connection'] = api_get_local_time($user['first_connection']); + } else { + $user['first_connection'] = '-'; + } + if (!empty($user['last_connection'])) { + $user['last_connection'] = api_get_local_time($user['last_connection']); + } else { + $user['last_connection'] = '-'; + } + if (!empty($user['lp_finalization_date'])) { + $user['lp_finalization_date'] = api_get_local_time($user['lp_finalization_date']); + } else { + $user['lp_finalization_date'] = '-'; + } + if (!empty($user['quiz_finalization_date'])) { + $user['quiz_finalization_date'] = api_get_local_time($user['quiz_finalization_date']); + } else { + $user['quiz_finalization_date'] = '-'; + } + } + + if (empty($sessionId)) { + $user['survey'] = ($surveyUserList[$user['user_id']] ?? 0).' / '.$totalSurveys; + } + + $url = $urlBase.'&student='.$user['user_id']; + + $user['link'] = ' + '.Display::return_icon('2rightarrow.png', get_lang('Details')).' + '; + + // store columns in array $users + $userRow = []; + if ($displaySessionInfo && !empty($sessionId)) { + $sessionInfo = api_get_session_info($sessionId); + $userRow['session_name'] = $sessionInfo['name']; + $userRow['session_startdate'] = $sessionInfo['access_start_date']; + $userRow['session_enddate'] = $sessionInfo['access_end_date']; + $userRow['course_name'] = $courseInfo['name']; + } + $userRow['official_code'] = $user['official_code']; + if ($sortByFirstName) { + $userRow['firstname'] = $user['col2']; + $userRow['lastname'] = $user['col1']; + } else { + $userRow['lastname'] = $user['col1']; + $userRow['firstname'] = $user['col2']; + } + $userRow['username'] = $user['username']; + $userRow['time'] = $user['time']; + $userRow['average_progress'] = $user['average_progress']; + $userRow['exercise_progress'] = $user['exercise_progress']; + $userRow['exercise_average_best_attempt'] = $user['exercise_average_best_attempt']; + $userRow['student_score'] = $user['student_score']; + $userRow['student_score_best'] = $user['student_score_best']; + if (!empty($exerciseResults)) { + foreach ($exerciseResults as $exerciseId => $bestResult) { + $userRow[$exerciseId] = $bestResult; + } + } + + $userRow['count_assignments'] = $user['count_assignments']; + $userRow['count_messages'] = $user['count_messages']; + + $userGroupManager = new UserGroupModel(); + if ($exportCsv) { + $userRow['classes'] = implode( + ',', + $userGroupManager->getNameListByUser($user['user_id'], UserGroupModel::NORMAL_CLASS) + ); + } else { + $userRow['classes'] = $userGroupManager->getLabelsFromNameList( + $user['user_id'], + UserGroupModel::NORMAL_CLASS + ); + } + + if (empty($sessionId)) { + $userRow['survey'] = $user['survey']; + } else { + $userSession = SessionManager::getUserSession($user['user_id'], $sessionId); + $userRow['registered_at'] = ''; + if ($userSession) { + $userRow['registered_at'] = api_get_local_time($userSession['registered_at']); + } + } + + $userRow['first_connection'] = $user['first_connection']; + $userRow['last_connection'] = $user['last_connection']; + + $userRow['lp_finalization_date'] = $user['lp_finalization_date']; + $userRow['quiz_finalization_date'] = $user['quiz_finalization_date']; + + // we need to display an additional profile field + if (isset($_GET['additional_profile_field'])) { + $data = Session::read('additional_user_profile_info'); + + $extraFieldInfo = Session::read('extra_field_info'); + foreach ($_GET['additional_profile_field'] as $fieldId) { + if (isset($data[$fieldId]) && isset($data[$fieldId][$user['user_id']])) { + if (is_array($data[$fieldId][$user['user_id']])) { + $userRow[$extraFieldInfo[$fieldId]['variable']] = implode( + ', ', + $data[$fieldId][$user['user_id']] + ); + } else { + $userRow[$extraFieldInfo[$fieldId]['variable']] = $data[$fieldId][$user['user_id']]; + } + } else { + $userRow[$extraFieldInfo[$fieldId]['variable']] = ''; + } + } + } + + $data = Session::read('default_additional_user_profile_info'); + $defaultExtraFieldInfo = Session::read('default_extra_field_info'); + if (isset($defaultExtraFieldInfo) && isset($data)) { + foreach ($data as $key => $val) { + if (isset($val[$user['user_id']])) { + if (is_array($val[$user['user_id']])) { + $userRow[$defaultExtraFieldInfo[$key]['variable']] = implode( + ', ', + $val[$user['user_id']] + ); + } else { + $userRow[$defaultExtraFieldInfo[$key]['variable']] = $val[$user['user_id']]; + } + } else { + $userRow[$defaultExtraFieldInfo[$key]['variable']] = ''; + } + } + } + + if (api_get_setting('show_email_addresses') === 'true') { + $userRow['email'] = $user['col4']; + } + + $userRow['link'] = $user['link']; + + if ($exportCsv) { + unset($userRow['link']); + $csvContent[] = $userRow; + } + $users[] = array_values($userRow); + } + + if ($exportCsv) { + Session::write('csv_content', $csvContent); + } + + Session::erase('additional_user_profile_info'); + Session::erase('extra_field_info'); + Session::erase('default_additional_user_profile_info'); + Session::erase('default_extra_field_info'); + Session::write('user_id_list', $userIdList); + + return $users; + } + + /** + * Get data for users list in sortable with pagination. + */ + public static function getTotalTimeReport( + $from, + $numberOfItems, + $column, + $direction, + bool $includeInvitedUsers = false + ): array { + global $user_ids, $course_code, $export_csv, $session_id; + + $course_code = Database::escape_string($course_code); + $tblUser = Database::get_main_table(TABLE_MAIN_USER); + $tblUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER); + $accessUrlId = api_get_current_access_url_id(); + + // get all users data from a course for sortable with limit + if (is_array($user_ids)) { + $user_ids = array_map('intval', $user_ids); + $conditionUser = " WHERE user.user_id IN (".implode(',', $user_ids).") "; + } else { + $user_ids = intval($user_ids); + $conditionUser = " WHERE user.user_id = $user_ids "; + } + + $urlTable = null; + $urlCondition = null; + if (api_is_multiple_url_enabled()) { + $urlTable = ", ".$tblUrlRelUser." as url_users"; + $urlCondition = " AND user.user_id = url_users.user_id AND access_url_id='$accessUrlId'"; + } + + $invitedUsersCondition = ''; + if (!$includeInvitedUsers) { + $invitedUsersCondition = " AND user.status != ".INVITEE; + } + + $sql = "SELECT user.user_id as user_id, + user.official_code as col0, + user.lastname as col1, + user.firstname as col2, + user.username as col3 + FROM $tblUser as user $urlTable + $conditionUser $urlCondition $invitedUsersCondition"; + + if (!in_array($direction, ['ASC', 'DESC'])) { + $direction = 'ASC'; + } + + $column = (int) $column; + $from = (int) $from; + $numberOfItems = (int) $numberOfItems; + + $sql .= " ORDER BY col$column $direction "; + $sql .= " LIMIT $from,$numberOfItems"; + + $res = Database::query($sql); + $users = []; + + $sortByFirstName = api_sort_by_first_name(); + $courseInfo = api_get_course_info($course_code); + $courseId = $courseInfo['real_id']; + + while ($user = Database::fetch_array($res, 'ASSOC')) { + $user['official_code'] = $user['col0']; + $user['lastname'] = $user['col1']; + $user['firstname'] = $user['col2']; + $user['username'] = $user['col3']; + + $totalCourseTime = Tracking::get_time_spent_on_the_course( + $user['user_id'], + $courseId, + $session_id + ); + + $user['time'] = api_time_to_hms($totalCourseTime); + $totalLpTime = Tracking::get_time_spent_in_lp( + $user['user_id'], + api_get_course_entity($courseId), + [], + $session_id + ); + + $warning = ''; + if ($totalLpTime > $totalCourseTime) { + $warning = ' '.Display::label(get_lang('TimeDifference'), 'danger'); + } + + $user['total_lp_time'] = api_time_to_hms($totalLpTime).$warning; + + $user['first_connection'] = Tracking::get_first_connection_date_on_the_course( + $user['user_id'], + $courseId, + $session_id + ); + $user['last_connection'] = Tracking::get_last_connection_date_on_the_course( + $user['user_id'], + $courseInfo, + $session_id, + $export_csv === false + ); + + $user['link'] = '
+ + '.Display::return_icon('2rightarrow.png', get_lang('Details')).' + +
'; + + // store columns in array $users + $userRow = []; + $userRow['official_code'] = $user['official_code']; //0 + if ($sortByFirstName) { + $userRow['firstname'] = $user['firstname']; + $userRow['lastname'] = $user['lastname']; + } else { + $userRow['lastname'] = $user['lastname']; + $userRow['firstname'] = $user['firstname']; + } + $userRow['username'] = $user['username']; + $userRow['time'] = $user['time']; + $userRow['total_lp_time'] = $user['total_lp_time']; + $userRow['first_connection'] = $user['first_connection']; + $userRow['last_connection'] = $user['last_connection']; + + $userRow['link'] = $user['link']; + $users[] = array_values($userRow); + } + + return $users; + } + + /** + * Determines the remaining actions for a session and returns a string with the results. + */ + public static function actionsLeft($current, $sessionId = 0): string + { + $usersLink = Display::url( + Display::return_icon('user.png', get_lang('StudentsTracking'), [], ICON_SIZE_MEDIUM), + 'courseLog.php?'.api_get_cidreq(true, false) + ); + + $groupsLink = Display::url( + Display::return_icon('group.png', get_lang('GroupReporting'), [], ICON_SIZE_MEDIUM), + 'course_log_groups.php?'.api_get_cidreq() + ); + + $resourcesLink = Display::url( + Display::return_icon('tools.png', get_lang('ResourcesTracking'), [], ICON_SIZE_MEDIUM), + 'course_log_resources.php?'.api_get_cidreq(true, false) + ); + + $courseLink = Display::url( + Display::return_icon('course.png', get_lang('CourseTracking'), [], ICON_SIZE_MEDIUM), + 'course_log_tools.php?'.api_get_cidreq(true, false) + ); + + $examLink = Display::url( + Display::return_icon('quiz.png', get_lang('ExamTracking'), [], ICON_SIZE_MEDIUM), + api_get_path(WEB_CODE_PATH).'tracking/exams.php?'.api_get_cidreq() + ); + + $eventsLink = Display::url( + Display::return_icon('security.png', get_lang('EventsReport'), [], ICON_SIZE_MEDIUM), + api_get_path(WEB_CODE_PATH).'tracking/course_log_events.php?'.api_get_cidreq() + ); + + $lpLink = Display::url( + Display::return_icon('scorms.png', get_lang('CourseLearningPathsGenericStats'), [], ICON_SIZE_MEDIUM), + api_get_path(WEB_CODE_PATH).'tracking/lp_report.php?'.api_get_cidreq() + ); + + $attendanceLink = ''; + if (!empty($sessionId)) { + $attendanceLink = Display::url( + Display::return_icon('attendance_list.png', get_lang('Logins'), [], ICON_SIZE_MEDIUM), + api_get_path(WEB_CODE_PATH).'attendance/index.php?'.api_get_cidreq().'&action=calendar_logins' + ); + } + + switch ($current) { + case 'users': + $usersLink = Display::url( + Display::return_icon( + 'user_na.png', + get_lang('StudentsTracking'), + [], + ICON_SIZE_MEDIUM + ), + '#' + ); + break; + case 'groups': + $groupsLink = Display::url( + Display::return_icon('group_na.png', get_lang('GroupReporting'), [], ICON_SIZE_MEDIUM), + '#' + ); + break; + case 'courses': + $courseLink = Display::url( + Display::return_icon('course_na.png', get_lang('CourseTracking'), [], ICON_SIZE_MEDIUM), + '#' + ); + break; + case 'resources': + $resourcesLink = Display::url( + Display::return_icon( + 'tools_na.png', + get_lang('ResourcesTracking'), + [], + ICON_SIZE_MEDIUM + ), + '#' + ); + break; + case 'exams': + $examLink = Display::url( + Display::return_icon('quiz_na.png', get_lang('ExamTracking'), [], ICON_SIZE_MEDIUM), + '#' + ); + break; + case 'logs': + $eventsLink = Display::url( + Display::return_icon('security_na.png', get_lang('EventsReport'), [], ICON_SIZE_MEDIUM), + '#' + ); + break; + case 'attendance': + if (!empty($sessionId)) { + $attendanceLink = Display::url( + Display::return_icon('attendance_list.png', get_lang('Logins'), [], ICON_SIZE_MEDIUM), + '#' + ); + } + break; + case 'lp': + $lpLink = Display::url( + Display::return_icon( + 'scorms_na.png', + get_lang('CourseLearningPathsGenericStats'), + [], + ICON_SIZE_MEDIUM + ), + '#' + ); + break; + } + + $items = [ + $usersLink, + $groupsLink, + $courseLink, + $resourcesLink, + $examLink, + $eventsLink, + $lpLink, + $attendanceLink, + ]; + + return implode('', $items).' '; + } + + public static function calcBestScoreAverageNotInLP( + array $exerciseList, + array $usersInGroup, + int $cId, + int $sessionId = 0, + bool $returnFormatted = false + ) { + if (empty($exerciseList) || empty($usersInGroup)) { + return 0; + } + + $bestScoreAverageNotInLP = 0; + + foreach ($exerciseList as $exerciseData) { + foreach ($usersInGroup as $userId) { + $results = Event::get_best_exercise_results_by_user( + $exerciseData['iid'], + $cId, + $sessionId, + $userId + ); + + $scores = array_map( + function (array $result) { + return empty($result['exe_weighting']) ? 0 : $result['exe_result'] / $result['exe_weighting']; + }, + $results + ); + + $bestScoreAverageNotInLP += $scores ? max($scores) : 0; + } + } + + $rounded = round( + $bestScoreAverageNotInLP / count($exerciseList) * 100 / count($usersInGroup), + 2 + ); + + if ($returnFormatted) { + return sprintf(get_lang('XPercent'), $rounded); + } + + return $rounded; + } +} diff --git a/public/main/inc/lib/exercise.lib.php b/public/main/inc/lib/exercise.lib.php index bdf533f402..c363f6cef2 100644 --- a/public/main/inc/lib/exercise.lib.php +++ b/public/main/inc/lib/exercise.lib.php @@ -3000,47 +3000,9 @@ EOT; $repo = Container::getQuizRepository(); - return $repo->findAllByCourse($course, $session, (string) $search, $active); - - // Show courses by active status - /*if (true == $search_all_sessions) { - $conditions = [ - 'where' => [ - $active_sql.' c_id = ? '.$needle_where.$timeConditions => [ - $course_id, - $needle, - ], - ], - 'order' => 'title', - ]; - } else { - if (empty($session_id)) { - $conditions = [ - 'where' => [ - $active_sql.' (session_id = 0 OR session_id IS NULL) AND c_id = ? '.$needle_where.$timeConditions => [ - $course_id, - $needle, - ], - ], - 'order' => 'title', - ]; - } else { - $conditions = [ - 'where' => [ - $active_sql.' (session_id = 0 OR session_id IS NULL OR session_id = ? ) AND c_id = ? '.$needle_where.$timeConditions => [ - $session_id, - $course_id, - $needle, - ], - ], - 'order' => 'title', - ]; - } - } - - $table = Database::get_course_table(TABLE_QUIZ_TEST); - - return Database::select('*', $table, $conditions);*/ + return $repo->findAllByCourse($course, $session, (string) $search, $active) + ->getQuery() + ->getResult(); } /** diff --git a/public/main/inc/lib/export.lib.inc.php b/public/main/inc/lib/export.lib.inc.php index 07178e8ba8..3396dede15 100644 --- a/public/main/inc/lib/export.lib.inc.php +++ b/public/main/inc/lib/export.lib.inc.php @@ -26,14 +26,9 @@ class Export /** * Export tabular data to CSV-file. * - * @param array $data - * @param string $filename - * @param bool $writeOnly Whether to only write on disk or also send for download - * @param string $enclosure - * * @return mixed csv raw data | false if no data to export | string file path if success in $writeOnly mode */ - public static function arrayToCsv($data, $filename = 'export', $writeOnly = false, $enclosure = '"') + public static function arrayToCsv(array $data, string $filename = 'export', bool $writeOnly = false, string $enclosure = '"') { if (empty($data)) { return false; @@ -55,6 +50,44 @@ class Export return $filePath; } + /** + * Converts an array of data into a CSV file and optionally sends it for download. + * + * @return string|void Returns the file path if $writeOnly is true, otherwise sends the file for download and exits. + */ + public static function arrayToCsvSimple(array $data, string $filename = 'export', bool $writeOnly = false) + { + $file = api_get_path(SYS_ARCHIVE_PATH) . uniqid('') . '.csv'; + + $handle = fopen($file, 'w'); + + if ($handle === false) { + throw new \RuntimeException("Unable to create or open the file: $file"); + } + + if (is_array($data)) { + foreach ($data as $row) { + $line = ''; + if (is_array($row)) { + foreach ($row as $value) { + $line .= '"' . str_replace('"', '""', (string)$value) . '";'; + } + } + fwrite($handle, rtrim($line, ';') . "\n"); + } + } + + fclose($handle); + + if (!$writeOnly) { + DocumentManager::file_send_for_download($file, true, $filename . '.csv'); + unlink($file); + exit; + } + + return $file; + } + /** * Export tabular data to XLS-file. */ diff --git a/public/main/inc/lib/extra_field_value.lib.php b/public/main/inc/lib/extra_field_value.lib.php index 8999fbfb81..633f20e7cf 100644 --- a/public/main/inc/lib/extra_field_value.lib.php +++ b/public/main/inc/lib/extra_field_value.lib.php @@ -220,6 +220,10 @@ class ExtraFieldValue extends Model $tags = []; foreach ($tagValues as $tagValue) { + if (is_array($tagValue)) { + $tagValue = reset($tagValue); + } + if (empty($tagValue)) { continue; } @@ -338,16 +342,26 @@ class ExtraFieldValue extends Model break; case ExtraField::FIELD_TYPE_DATE: - $d = DateTime::createFromFormat('Y-m-d', $value); - $valid = $d && $d->format('Y-m-d') === $value; - if ($valid) { - $newParams = [ - 'item_id' => $params['item_id'], - 'field_id' => $extraFieldInfo['id'], - 'field_value' => $value, - 'comment' => $comment, - ]; - $this->save($newParams, $showQuery); + if (is_array($value)) { + if (empty($value)) { + break; + } + $value = reset($value); + } + + if (is_string($value) && !empty($value)) { + $d = DateTime::createFromFormat('Y-m-d', $value); + $valid = $d && $d->format('Y-m-d') === $value; + + if ($valid) { + $newParams = [ + 'item_id' => $params['item_id'], + 'field_id' => $extraFieldInfo['id'], + 'field_value' => $value, + 'comment' => $comment, + ]; + $this->save($newParams, $showQuery); + } } break; case ExtraField::FIELD_TYPE_DATETIME: diff --git a/public/main/inc/lib/sessionmanager.lib.php b/public/main/inc/lib/sessionmanager.lib.php index 766a555bc1..cfa56348df 100644 --- a/public/main/inc/lib/sessionmanager.lib.php +++ b/public/main/inc/lib/sessionmanager.lib.php @@ -232,9 +232,9 @@ class SessionManager ->setSendSubscriptionNotification((bool) $sendSubscriptionNotification) ->setNotifyBoss((bool) $notifyBoss) ->setParentId($parentId) - ->setDaysToReinscription($daysBeforeFinishingForReinscription) + ->setDaysToReinscription((int) $daysBeforeFinishingForReinscription) ->setLastRepetition($lastRepetition) - ->setDaysToNewRepetition($daysBeforeFinishingToCreateNewRepetition); + ->setDaysToNewRepetition((int) $daysBeforeFinishingToCreateNewRepetition); foreach ($coachesId as $coachId) { $session->addGeneralCoach(api_get_user_entity($coachId)); @@ -1877,9 +1877,9 @@ class SessionManager ->setSendSubscriptionNotification((bool) $sendSubscriptionNotification) ->setNotifyBoss((bool) $notifyBoss) ->setParentId($parentId) - ->setDaysToReinscription($daysBeforeFinishingForReinscription) + ->setDaysToReinscription((int) $daysBeforeFinishingForReinscription) ->setLastRepetition($lastRepetition) - ->setDaysToNewRepetition($daysBeforeFinishingToCreateNewRepetition) + ->setDaysToNewRepetition((int) $daysBeforeFinishingToCreateNewRepetition) ->setAccessStartDate(null) ->setAccessStartDate(null) ->setDisplayStartDate(null) @@ -8619,7 +8619,7 @@ class SessionManager [ 'name' => 'title', 'index' => 's.title', - 'width' => '160', + 'width' => '300', 'align' => 'left', 'search' => 'true', 'searchoptions' => ['sopt' => $operators], @@ -8635,6 +8635,7 @@ class SessionManager 'name' => 'display_start_date', 'index' => 'display_start_date', 'align' => 'left', + 'width' => '200', 'search' => 'true', 'searchoptions' => [ 'dataInit' => 'date_pick_today', @@ -8645,6 +8646,7 @@ class SessionManager 'name' => 'display_end_date', 'index' => 'display_end_date', 'align' => 'left', + 'width' => '200', 'search' => 'true', 'searchoptions' => [ 'dataInit' => 'date_pick_one_month', @@ -8665,6 +8667,7 @@ class SessionManager 'name' => 'users', 'index' => 'users', 'align' => 'left', + 'width' => '100', 'search' => 'false', ]; @@ -8689,6 +8692,7 @@ class SessionManager 'name' => 'status', 'index' => 'status', 'align' => 'left', + 'width' => '120', 'search' => 'true', 'stype' => 'select', // for the bottom bar @@ -8719,7 +8723,7 @@ class SessionManager [ 'name' => 'title', 'index' => 's.title', - 'width' => '160', + 'width' => '300', 'align' => 'left', 'search' => 'true', 'searchoptions' => ['sopt' => $operators], @@ -8735,6 +8739,7 @@ class SessionManager 'name' => 'display_start_date', 'index' => 'display_start_date', 'align' => 'left', + 'width' => '200', 'search' => 'true', 'searchoptions' => [ 'dataInit' => 'date_pick_today', @@ -8745,6 +8750,7 @@ class SessionManager 'name' => 'display_end_date', 'index' => 'display_end_date', 'align' => 'left', + 'width' => '200', 'search' => 'true', 'searchoptions' => [ 'dataInit' => 'date_pick_one_month', @@ -8765,6 +8771,7 @@ class SessionManager 'name' => 'users', 'index' => 'users', 'align' => 'left', + 'width' => '100', 'search' => 'false', ]; @@ -8808,7 +8815,7 @@ class SessionManager [ 'name' => 'title', 'index' => 's.title', - 'width' => '200', + 'width' => '300', 'align' => 'left', 'search' => 'true', 'searchoptions' => ['sopt' => $operators], @@ -8817,12 +8824,14 @@ class SessionManager 'name' => 'display_start_date', 'index' => 'display_start_date', 'align' => 'left', + 'width' => '200', 'search' => 'true', 'searchoptions' => ['dataInit' => 'date_pick_today', 'sopt' => $date_operators], ], [ 'name' => 'display_end_date', 'index' => 'display_end_date', + 'width' => '200', 'align' => 'left', 'search' => 'true', 'searchoptions' => ['dataInit' => 'date_pick_one_month', 'sopt' => $date_operators], @@ -8888,6 +8897,7 @@ class SessionManager 'name' => 'title', 'index' => 's.title', 'width' => '260px', + 'width' => '300', 'align' => 'left', 'search' => 'true', 'searchoptions' => ['sopt' => $operators], @@ -8903,6 +8913,7 @@ class SessionManager 'name' => 'display_start_date', 'index' => 'display_start_date', 'align' => 'left', + 'width' => '200', 'search' => 'true', 'searchoptions' => [ 'dataInit' => 'date_pick_today', @@ -8913,6 +8924,7 @@ class SessionManager 'name' => 'display_end_date', 'index' => 'display_end_date', 'align' => 'left', + 'width' => '200', 'search' => 'true', 'searchoptions' => [ 'dataInit' => 'date_pick_one_month', @@ -8933,6 +8945,7 @@ class SessionManager 'name' => 'users', 'index' => 'users', 'align' => 'left', + 'width' => '100', 'search' => 'false', ]; @@ -8957,6 +8970,7 @@ class SessionManager 'name' => 'status', 'index' => 'status', 'align' => 'left', + 'width' => '120', 'search' => 'true', 'stype' => 'select', // for the bottom bar @@ -10319,4 +10333,192 @@ class SessionManager return $sessions; } + + + /** + * Method to export sessions data as CSV + */ + public static function exportSessionsAsCSV(array $selectedSessions): void + { + $csvData = []; + $headersGenerated = false; + $csvHeaders = []; + + foreach ($selectedSessions as $sessionId) { + $courses = SessionManager::get_course_list_by_session_id($sessionId); + + if (!empty($courses)) { + foreach ($courses as $course) { + $courseCode = $course['course_code']; + $courseId = $course['id']; + $studentList = CourseManager::get_student_list_from_course_code( + $courseCode, + true, + $sessionId + ); + + $userIds = array_keys($studentList); + + [$generatedHeaders, $csvContent] = self::generateSessionCourseReportData($sessionId, $courseId, $userIds); + + if (!$headersGenerated) { + $csvHeaders = $generatedHeaders; + $headersGenerated = true; + } + + foreach ($csvContent as $row) { + $csvData[] = $row; + } + } + } + } + + if (!empty($csvData)) { + array_unshift($csvData, $csvHeaders); + $filename = 'export_session_courses_reports_complete_' . date('Y-m-d_H-i-s') . '.csv'; + Export::arrayToCsvSimple($csvData, $filename); + exit; + } + } + + /** + * Exports session data as a ZIP file with CSVs and sends it for download. + */ + public static function exportSessionsAsZip(array $sessionList): void + { + $tempZipFile = api_get_path(SYS_ARCHIVE_PATH) . api_get_unique_id() . '.zip'; + $tempDir = dirname($tempZipFile); + + if (!is_dir($tempDir) || !is_writable($tempDir)) { + exit("The directory for creating the ZIP file does not exist or lacks write permissions: $tempDir"); + } + + $zip = new \ZipArchive(); + if ($zip->open($tempZipFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) { + exit("Unable to open the ZIP file for writing: $tempZipFile"); + } + + foreach ($sessionList as $sessionItemId) { + $courses = SessionManager::get_course_list_by_session_id($sessionItemId); + + if (!empty($courses)) { + foreach ($courses as $course) { + $courseCode = $course['course_code']; + $courseId = $course['id']; + $studentList = CourseManager::get_student_list_from_course_code($courseCode, true, $sessionItemId); + $userIds = array_keys($studentList); + + [$csvHeaders, $csvContent] = self::generateSessionCourseReportData($sessionItemId, $courseId, $userIds); + array_unshift($csvContent, $csvHeaders); + + $sessionInfo = api_get_session_info($sessionItemId); + $courseInfo = api_get_course_info_by_id($courseId); + $csvFileName = $sessionInfo['name'] . '_' . $courseInfo['name'] . '.csv'; + + $csvFilePath = Export::arrayToCsvSimple($csvContent, $csvFileName, true); + + if ($csvFilePath && file_exists($csvFilePath)) { + $zip->addFile($csvFilePath, $csvFileName); + } + } + } + } + + if (!$zip->close()) { + exit("Could not close the ZIP file correctly."); + } + + if (file_exists($tempZipFile)) { + DocumentManager::file_send_for_download($tempZipFile, true); + unlink($tempZipFile); + } else { + exit("The ZIP file was not created correctly."); + } + } + + private static function generateSessionCourseReportData($sessionId, $courseId, $userIds): array + { + $em = Database::getManager(); + $sessionRepository = $em->getRepository(Session::class); + $session = $sessionRepository->find($sessionId); + + if (!$session instanceof Session) { + throw new \InvalidArgumentException("Invalid session object for session ID $sessionId"); + } + + $courseInfo = api_get_course_info_by_id($courseId); + $courseCode = $courseInfo['code']; + + $csvHeaders = [ + get_lang('Session name'), + get_lang('Session access dates'), + get_lang('Session display dates'), + get_lang('Course name'), + get_lang('Official code'), + get_lang('First name'), + get_lang('Last name'), + get_lang('Login'), + get_lang('Training time'), + get_lang('Course progress'), + get_lang('Exercise progress'), + get_lang('Exercise average'), + get_lang('Score'), + get_lang('Score') . ' - ' . get_lang('Best attempt'), + get_lang('Student_publication'), + get_lang('Messages'), + get_lang('Classes'), + get_lang('Registration date'), + get_lang('First login in course'), + get_lang('Latest login in course'), + ]; + + $csvData = TrackingCourseLog::getUserData( + null, + count($userIds), + null, + null, + [], + true, + true, + $courseCode, + $sessionId, + true, + $userIds + ); + + $rawCsvContent = ChamiloSession::read('csv_content'); + + if (empty($rawCsvContent)) { + throw new \RuntimeException("No CSV content found in session for course $courseCode and session $sessionId."); + } + + $csvContent = []; + foreach ($rawCsvContent as $row) { + $alignedRow = [ + $row['session_name'] ?? '', + $row['session_startdate'] ?? '', + $row['session_enddate'] ?? '', + $row['course_name'] ?? '', + $row['official_code'] ?? '', + $row['firstname'] ?? '', + $row['lastname'] ?? '', + $row['username'] ?? '', + $row['time'] ?? '', + $row['average_progress'] ?? '', + $row['exercise_progress'] ?? '', + $row['exercise_average'] ?? '', + $row['student_score'] ?? '', + $row['student_score_best'] ?? '', + $row['count_assignments'] ?? '', + $row['count_messages'] ?? '', + $row['classes'] ?? '', + $row['registered_at'] ?? '', + $row['first_connection'] ?? '', + $row['last_connection'] ?? '', + ]; + $csvContent[] = $alignedRow; + } + + return [$csvHeaders, $csvContent]; + } } diff --git a/public/main/inc/lib/tracking.lib.php b/public/main/inc/lib/tracking.lib.php index 2fae9bbc3e..8373faa687 100644 --- a/public/main/inc/lib/tracking.lib.php +++ b/public/main/inc/lib/tracking.lib.php @@ -2640,22 +2640,26 @@ class Tracking $sessionId ) { $result = 0; - if (!empty($exercise_list)) { + if (!empty($exercise_list) && (is_array($exercise_list) || $exercise_list instanceof \Countable)) { foreach ($exercise_list as $exercise_data) { $exercise_id = $exercise_data->getIid(); $best_attempt = Event::get_best_attempt_exercise_results_per_user( $user_id, $exercise_id, $courseId, - $sessionId + $sessionId, + false ); if (!empty($best_attempt) && !empty($best_attempt['max_score'])) { $result += $best_attempt['score'] / $best_attempt['max_score']; } } - $result = $result / count($exercise_list); - $result = round($result, 2) * 100; + + if (count($exercise_list) > 0) { + $result = $result / count($exercise_list); + $result = round($result, 2) * 100; + } } return $result.'%'; @@ -2891,25 +2895,6 @@ class Tracking } $sessionCondition = api_get_session_condition($sessionId); - //$sessionId = (int) $sessionId; - /*if (count($lp_ids) > 0) { - $condition_session = " AND session_id = $sessionId "; - } else { - $condition_session = " WHERE session_id = $sessionId "; - } - - // Check the real number of LPs corresponding to the filter in the - // database (and if no list was given, get them all) - if (empty($sessionId)) { - $sql = "SELECT DISTINCT(iid), use_max_score - FROM $lp_table - WHERE - c_id = $courseId AND - (session_id = 0 OR session_id IS NULL) $condition_lp "; - } else { - - }*/ - $lp_list = $use_max_score = []; if (empty($condition_lp)) { $repo = Container::getLpRepository(); @@ -2951,6 +2936,7 @@ class Tracking WHERE lp_id IN (".implode(',', $lp_list).") $condition_user1 + $sessionCondition GROUP BY lp_id, user_id"; //AND session_id = $sessionId @@ -8021,1274 +8007,131 @@ class Tracking ['class' => 'table-responsive'] ); } -} -/** - * @todo move into a proper file - */ -class TrackingCourseLog -{ /** - * @return mixed - */ - public static function count_item_resources() - { - $session_id = api_get_session_id(); - $course_id = api_get_course_int_id(); - - $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY); - $table_user = Database::get_main_table(TABLE_MAIN_USER); - - $sql = "SELECT count(tool) AS total_number_of_items - FROM $table_item_property track_resource, $table_user user - WHERE - track_resource.c_id = $course_id AND - track_resource.insert_user_id = user.id user_id AND - session_id ".(empty($session_id) ? ' IS NULL ' : " = $session_id "); - - if (isset($_GET['keyword'])) { - $keyword = Database::escape_string(trim($_GET['keyword'])); - $sql .= " AND ( - user.username LIKE '%".$keyword."%' OR - lastedit_type LIKE '%".$keyword."%' OR - tool LIKE '%".$keyword."%' - )"; - } - - $sql .= " AND tool IN ( - 'document', - 'learnpath', - 'quiz', - 'glossary', - 'link', - 'course_description', - 'announcement', - 'thematic', - 'thematic_advance', - 'thematic_plan' - )"; - $res = Database::query($sql); - $obj = Database::fetch_object($res); - - return $obj->total_number_of_items; - } - - /** - * @param $from - * @param $number_of_items - * @param $column - * @param $direction + * Counts the number of student publications for a given course, session, and group. * - * @return array - */ - public static function get_item_resources_data($from, $number_of_items, $column, $direction) - { - $session_id = api_get_session_id(); - $course_id = api_get_course_int_id(); - - $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY); - $table_user = Database::get_main_table(TABLE_MAIN_USER); - $table_session = Database::get_main_table(TABLE_MAIN_SESSION); - $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER); - $column = (int) $column; - $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction; - - $sql = "SELECT - tool as col0, - lastedit_type as col1, - ref as ref, - user.username as col3, - insert_date as col6, - visibility as col7, - user.user_id as user_id - FROM $table_item_property track_resource, $table_user user - WHERE - track_resource.c_id = $course_id AND - track_resource.insert_user_id = user.user_id AND - session_id ".(empty($session_id) ? ' IS NULL ' : " = $session_id "); - - if (isset($_GET['keyword'])) { - $keyword = Database::escape_string(trim($_GET['keyword'])); - $sql .= " AND ( - user.username LIKE '%".$keyword."%' OR - lastedit_type LIKE '%".$keyword."%' OR - tool LIKE '%".$keyword."%' - ) "; - } - - $sql .= " AND tool IN ( - 'document', - 'learnpath', - 'quiz', - 'glossary', - 'link', - 'course_description', - 'announcement', - 'thematic', - 'thematic_advance', - 'thematic_plan' - )"; - - if (0 == $column) { - $column = '0'; - } - if ('' != $column && '' != $direction) { - if (2 != $column && 4 != $column) { - $sql .= " ORDER BY col$column $direction"; - } - } else { - $sql .= " ORDER BY col6 DESC "; - } - - $from = intval($from); - if ($from) { - $number_of_items = intval($number_of_items); - $sql .= " LIMIT $from, $number_of_items "; - } - - $res = Database::query($sql); - $resources = []; - $thematic_tools = ['thematic', 'thematic_advance', 'thematic_plan']; - while ($row = Database::fetch_array($res)) { - $ref = $row['ref']; - $table_name = self::get_tool_name_table($row['col0']); - $table_tool = Database::get_course_table($table_name['table_name']); - - $id = $table_name['id_tool']; - $recorset = false; - - if (in_array($row['col0'], ['thematic_plan', 'thematic_advance'])) { - $tbl_thematic = Database::get_course_table(TABLE_THEMATIC); - $sql = "SELECT thematic_id FROM $table_tool - WHERE c_id = $course_id AND iid = $ref"; - $rs_thematic = Database::query($sql); - if (Database::num_rows($rs_thematic)) { - $row_thematic = Database::fetch_array($rs_thematic); - $thematic_id = $row_thematic['thematic_id']; - - $sql = "SELECT s.id, s.title, u.username - FROM $tbl_thematic t - INNER JOIN $tblSessionRelUser sru - ON t.session_id = sru.session_id - INNER JOIN $table_session s - ON sru.session_id = s.id - INNER JOIN $table_user u - ON sru.user_id = u.id - WHERE - t.c_id = $course_id AND - t.id = $thematic_id AND - sru.relation_type = ".SessionEntity::GENERAL_COACH; - $recorset = Database::query($sql); - } - } else { - $sql = "SELECT s.id, s.title u.username - FROM c_tool t, session s, user u, $tblSessionRelUser sru - WHERE - t.c_id = $course_id AND - t.session_id = s.id AND - sru.session_id = s.id AND - sru.user_id = u.id AND - t.$id = $ref"; - $recorset = Database::query($sql); - } - - if (!empty($recorset)) { - $obj = Database::fetch_object($recorset); - - $name_session = ''; - $coach_name = ''; - if (!empty($obj)) { - $name_session = $obj->title; - $coach_name = $obj->username; - } - - $url_tool = api_get_path(WEB_CODE_PATH).$table_name['link_tool']; - $row[0] = ''; - if (2 != $row['col6']) { - if (in_array($row['col0'], $thematic_tools)) { - $exp_thematic_tool = explode('_', $row['col0']); - $thematic_tool_title = ''; - if (is_array($exp_thematic_tool)) { - foreach ($exp_thematic_tool as $exp) { - $thematic_tool_title .= api_ucfirst($exp); - } - } else { - $thematic_tool_title = api_ucfirst($row['col0']); - } - - $row[0] = ''.get_lang($thematic_tool_title).''; - } else { - $row[0] = ''.get_lang('Tool'.api_ucfirst($row['col0'])).''; - } - } else { - $row[0] = api_ucfirst($row['col0']); - } - $row[1] = get_lang($row[1]); - $row[6] = api_convert_and_format_date($row['col6'], null, date_default_timezone_get()); - $row[5] = ''; - //@todo Improve this code please - switch ($table_name['table_name']) { - case 'document': - $sql = "SELECT tool.title as title FROM $table_tool tool - WHERE c_id = $course_id AND iid = $ref"; - $rs_document = Database::query($sql); - $obj_document = Database::fetch_object($rs_document); - if ($obj_document) { - $row[5] = $obj_document->title; - } - break; - case 'announcement': - $sql = "SELECT title FROM $table_tool - WHERE c_id = $course_id AND id = $ref"; - $rs_document = Database::query($sql); - $obj_document = Database::fetch_object($rs_document); - if ($obj_document) { - $row[5] = $obj_document->title; - } - break; - case 'glossary': - $sql = "SELECT title FROM $table_tool - WHERE c_id = $course_id AND glossary_id = $ref"; - $rs_document = Database::query($sql); - $obj_document = Database::fetch_object($rs_document); - if ($obj_document) { - $row[5] = $obj_document->title; - } - break; - case 'lp': - $sql = "SELECT title - FROM $table_tool WHERE c_id = $course_id AND id = $ref"; - $rs_document = Database::query($sql); - $obj_document = Database::fetch_object($rs_document); - $row[5] = $obj_document->title; - break; - case 'quiz': - $sql = "SELECT title FROM $table_tool - WHERE c_id = $course_id AND id = $ref"; - $rs_document = Database::query($sql); - $obj_document = Database::fetch_object($rs_document); - if ($obj_document) { - $row[5] = $obj_document->title; - } - break; - case 'course_description': - $sql = "SELECT title FROM $table_tool - WHERE c_id = $course_id AND id = $ref"; - $rs_document = Database::query($sql); - $obj_document = Database::fetch_object($rs_document); - if ($obj_document) { - $row[5] = $obj_document->title; - } - break; - case 'thematic': - $rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref"); - if (Database::num_rows($rs) > 0) { - $obj = Database::fetch_object($rs); - if ($obj) { - $row[5] = $obj->title; - } - } - break; - case 'thematic_advance': - $rs = Database::query("SELECT content FROM $table_tool WHERE c_id = $course_id AND id = $ref"); - if (Database::num_rows($rs) > 0) { - $obj = Database::fetch_object($rs); - if ($obj) { - $row[5] = $obj->content; - } - } - break; - case 'thematic_plan': - $rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref"); - if (Database::num_rows($rs) > 0) { - $obj = Database::fetch_object($rs); - if ($obj) { - $row[5] = $obj->title; - } - } - break; - default: - break; - } - - $row2 = $name_session; - if (!empty($coach_name)) { - $row2 .= '
'.get_lang('Coach').': '.$coach_name; - } - $row[2] = $row2; - if (!empty($row['col3'])) { - $userInfo = api_get_user_info($row['user_id']); - $row['col3'] = Display::url( - $row['col3'], - $userInfo['profile_url'] - ); - $row[3] = $row['col3']; - - $ip = Tracking::get_ip_from_user_event( - $row['user_id'], - $row['col6'], - true - ); - if (empty($ip)) { - $ip = get_lang('Unknown'); - } - $row[4] = $ip; - } - - $resources[] = $row; - } - } - - return $resources; - } - - /** - * @param string $tool + * @param int $courseId + * @param int|null $sessionId + * @param int|null $groupId * - * @return array + * @return int The number of student publications. */ - public static function get_tool_name_table($tool) + public static function countStudentPublications(int $courseId, ?int $sessionId = null, ?int $groupId = null): int { - switch ($tool) { - case 'document': - $table_name = TABLE_DOCUMENT; - $link_tool = 'document/document.php'; - $id_tool = 'id'; - break; - case 'learnpath': - $table_name = TABLE_LP_MAIN; - $link_tool = 'lp/lp_controller.php'; - $id_tool = 'id'; - break; - case 'quiz': - $table_name = TABLE_QUIZ_TEST; - $link_tool = 'exercise/exercise.php'; - $id_tool = 'id'; - break; - case 'glossary': - $table_name = TABLE_GLOSSARY; - $link_tool = 'glossary/index.php'; - $id_tool = 'glossary_id'; - break; - case 'link': - $table_name = TABLE_LINK; - $link_tool = 'link/link.php'; - $id_tool = 'id'; - break; - case 'course_description': - $table_name = TABLE_COURSE_DESCRIPTION; - $link_tool = 'course_description/'; - $id_tool = 'id'; - break; - case 'announcement': - $table_name = TABLE_ANNOUNCEMENT; - $link_tool = 'announcements/announcements.php'; - $id_tool = 'id'; - break; - case 'thematic': - $table_name = TABLE_THEMATIC; - $link_tool = 'course_progress/index.php'; - $id_tool = 'id'; - break; - case 'thematic_advance': - $table_name = TABLE_THEMATIC_ADVANCE; - $link_tool = 'course_progress/index.php'; - $id_tool = 'id'; - break; - case 'thematic_plan': - $table_name = TABLE_THEMATIC_PLAN; - $link_tool = 'course_progress/index.php'; - $id_tool = 'id'; - break; - default: - $table_name = $tool; - break; - } + $repo = Container::getStudentPublicationRepository(); - return [ - 'table_name' => $table_name, - 'link_tool' => $link_tool, - 'id_tool' => $id_tool, - ]; - } + $course = api_get_course_entity($courseId); + $session = api_get_session_entity($sessionId); + $group = api_get_group_entity($groupId); - /** - * @return string - */ - public static function display_additional_profile_fields() - { - // getting all the extra profile fields that are defined by the platform administrator - $extra_fields = UserManager::get_extra_fields(0, 50, 5, 'ASC'); - - // creating the form - $return = '
'; - - // the select field with the additional user profile fields (= this is where we select the field of which we want to see - // the information the users have entered or selected. - $return .= ''; + $qb = $repo->getResourcesByCourse($course, $session, $group); + $qb->select('COUNT(resource.iid)'); - // the form elements for the $_GET parameters (because the form is passed through GET - foreach ($_GET as $key => $value) { - if ('additional_profile_field' != $key) { - $return .= ''; - } - } - // the submit button - $return .= ''; - $return .= '
'; - if ($extra_fields_to_show > 0) { - return $return; - } else { - return ''; - } + return (int) $qb->getQuery()->getSingleScalarResult(); } /** - * This function gets all the information of a certrain ($field_id) - * additional profile field for a specific list of users is more efficent - * than get_addtional_profile_information_of_field() function - * It gets the information of all the users so that it can be displayed - * in the sortable table or in the csv or xls export. - * - * @author Julio Montoya - * - * @param int field id - * @param array list of user ids + * Counts the number of forum posts for a given course, session, and group. * - * @return array - * - * @since Nov 2009 + * @param int $courseId + * @param int|null $sessionId + * @param int|null $groupId * - * @version 1.8.6.2 - */ - public static function getAdditionalProfileInformationOfFieldByUser($field_id, $users) - { - // Database table definition - $table_user = Database::get_main_table(TABLE_MAIN_USER); - $table_user_field_values = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES); - $extraField = Database::get_main_table(TABLE_EXTRA_FIELD); - $result_extra_field = UserManager::get_extra_field_information($field_id); - $return = []; - if (!empty($users)) { - if (UserManager::USER_FIELD_TYPE_TAG == $result_extra_field['value_type']) { - foreach ($users as $user_id) { - $user_result = UserManager::get_user_tags($user_id, $field_id); - $tag_list = []; - foreach ($user_result as $item) { - $tag_list[] = $item['tag']; - } - $return[$user_id][] = implode(', ', $tag_list); - } - } else { - $new_user_array = []; - foreach ($users as $user_id) { - $new_user_array[] = "'".$user_id."'"; - } - $users = implode(',', $new_user_array); - $extraFieldType = EntityExtraField::USER_FIELD_TYPE; - // Selecting only the necessary information NOT ALL the user list - $sql = "SELECT user.id as user_id, v.value - FROM $table_user user - INNER JOIN $table_user_field_values v - ON (user.id = v.item_id) - INNER JOIN $extraField f - ON (f.id = v.field_id) - WHERE - f.item_type = $extraFieldType AND - v.field_id=".intval($field_id)." AND - user.id IN ($users)"; - - $result = Database::query($sql); - while ($row = Database::fetch_array($result)) { - // get option value for field type double select by id - if (!empty($row['value'])) { - if (ExtraField::FIELD_TYPE_DOUBLE_SELECT == - $result_extra_field['value_type'] - ) { - $id_double_select = explode(';', $row['value']); - if (is_array($id_double_select)) { - $value1 = $result_extra_field['options'][$id_double_select[0]]['option_value']; - $value2 = $result_extra_field['options'][$id_double_select[1]]['option_value']; - $row['value'] = ($value1.';'.$value2); - } - } - - if (ExtraField::FIELD_TYPE_SELECT_WITH_TEXT_FIELD == $result_extra_field['value_type']) { - $parsedValue = explode('::', $row['value']); - - if ($parsedValue) { - $value1 = $result_extra_field['options'][$parsedValue[0]]['display_text']; - $value2 = $parsedValue[1]; - - $row['value'] = "$value1: $value2"; - } - } - - if (ExtraField::FIELD_TYPE_TRIPLE_SELECT == $result_extra_field['value_type']) { - [$level1, $level2, $level3] = explode(';', $row['value']); - - $row['value'] = $result_extra_field['options'][$level1]['display_text'].' / '; - $row['value'] .= $result_extra_field['options'][$level2]['display_text'].' / '; - $row['value'] .= $result_extra_field['options'][$level3]['display_text']; - } - } - // get other value from extra field - $return[$row['user_id']][] = $row['value']; - } - } - } - - return $return; - } - - /** - * count the number of students in this course (used for SortableTable) - * Deprecated. + * @return int The number of forum posts. */ - public function count_student_in_course() - { - global $nbStudents; - - return $nbStudents; - } - - public function sort_users($a, $b) - { - $tracking = Session::read('tracking_column'); - - return strcmp( - trim(api_strtolower($a[$tracking])), - trim(api_strtolower($b[$tracking])) - ); - } - - public function sort_users_desc($a, $b) + public static function countStudentMessages(int $courseId, ?int $sessionId = null, ?int $groupId = null): int { - $tracking = Session::read('tracking_column'); + $repo = Container::getForumPostRepository(); - return strcmp( - trim(api_strtolower($b[$tracking])), - trim(api_strtolower($a[$tracking])) - ); - } + $course = api_get_course_entity($courseId); + $session = api_get_session_entity($sessionId); + $group = api_get_group_entity($groupId); - /** - * Get number of users for sortable with pagination. - * - * @return int - */ - public static function get_number_of_users($conditions) - { - $conditions['get_count'] = true; + $qb = $repo->getResourcesByCourse($course, $session, $group); + $qb->select('COUNT(resource.iid)'); - return self::get_user_data(null, null, null, null, $conditions); + return (int) $qb->getQuery()->getSingleScalarResult(); } /** - * Get data for users list in sortable with pagination. - * - * @param int $from - * @param int $number_of_items - * @param $column - * @param $direction - * @param $conditions + * It gets the last finalization date of learnpaths in a course. * - * @return array + * @return string finalization date formatted or false if it is empty. */ - public static function get_user_data( - $from, - $number_of_items, - $column, - $direction, - $conditions = [], - $options = [] + public static function getCourseLpFinalizationDate( + int $userId, + int $courseId, + int $sessionId, + bool $convertDate = true ) { - global $user_ids, $export_csv, $sessionId; - $includeInvitedUsers = $conditions['include_invited_users']; // include the invited users - $getCount = isset($conditions['get_count']) ? $conditions['get_count'] : false; - - $course = api_get_course_entity($conditions['course_id']); - $courseId = $course->getId(); - $courseCode = $course->getCode(); - - $csv_content = []; - $tbl_user = Database::get_main_table(TABLE_MAIN_USER); - $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER); - $access_url_id = api_get_current_access_url_id(); - - // get all users data from a course for sortable with limit - if (is_array($user_ids)) { - $user_ids = array_map('intval', $user_ids); - $condition_user = " WHERE user.id IN (".implode(',', $user_ids).") "; - } else { - $user_ids = (int) $user_ids; - $condition_user = " WHERE user.id = $user_ids "; - } - - if (!empty($_GET['user_keyword'])) { - $keyword = trim(Database::escape_string($_GET['user_keyword'])); - $condition_user .= " AND ( - user.firstname LIKE '%".$keyword."%' OR - user.lastname LIKE '%".$keyword."%' OR - user.username LIKE '%".$keyword."%' OR - user.email LIKE '%".$keyword."%' - ) "; - } - - $url_table = ''; - $url_condition = ''; - if (api_is_multiple_url_enabled()) { - $url_table = " INNER JOIN $tbl_url_rel_user as url_users ON (user.id = url_users.user_id)"; - $url_condition = " AND access_url_id = '$access_url_id'"; - } - - $invitedUsersCondition = ''; - if (!$includeInvitedUsers) { - $invitedUsersCondition = " AND user.status != ".INVITEE; - } - - $select = ' - SELECT user.id as user_id, - user.official_code as col0, - user.lastname as col1, - user.firstname as col2, - user.username as col3, - user.email as col4'; - if ($getCount) { - $select = ' SELECT COUNT(distinct(user.id)) as count '; - } - - $sqlInjectJoins = ''; - $where = 'AND 1 = 1 '; - $sqlInjectWhere = ''; - if (!empty($conditions)) { - if (isset($conditions['inject_joins'])) { - $sqlInjectJoins = $conditions['inject_joins']; - } - if (isset($conditions['where'])) { - $where = $conditions['where']; - } - if (isset($conditions['inject_where'])) { - $sqlInjectWhere = $conditions['inject_where']; - } - $injectExtraFields = !empty($conditions['inject_extra_fields']) ? $conditions['inject_extra_fields'] : 1; - $injectExtraFields = rtrim($injectExtraFields, ', '); - if (false === $getCount) { - $select .= " , $injectExtraFields"; - } - } - - $sql = "$select - FROM $tbl_user as user - $url_table - $sqlInjectJoins - $condition_user - $url_condition - $invitedUsersCondition - $where - $sqlInjectWhere - "; - - if (!in_array($direction, ['ASC', 'DESC'])) { - $direction = 'ASC'; - } - - $column = (int) $column; - $from = (int) $from; - $number_of_items = (int) $number_of_items; - - if ($getCount) { - $res = Database::query($sql); - $row = Database::fetch_array($res); - - return $row['count']; - } - - $sql .= " ORDER BY col$column $direction "; - $sql .= " LIMIT $from, $number_of_items"; - - $res = Database::query($sql); - $users = []; - - $total_surveys = 0; - /*$total_exercises = ExerciseLib::get_all_exercises( - $courseInfo, - $session_id, - false, - null, - false, - 3 - );*/ - $session = api_get_session_entity($sessionId); - $repo = Container::getQuizRepository(); - $qb = $repo->findAllByCourse($course, $session, null, 2); - $exercises = $qb->getQuery()->getResult(); - - if (empty($sessionId)) { - $survey_user_list = []; - // @todo - //$surveyList = SurveyManager::get_surveys($courseCode, $session_id); - $surveyList = []; - if ($surveyList) { - $total_surveys = count($surveyList); - foreach ($surveyList as $survey) { - $user_list = SurveyManager::get_people_who_filled_survey( - $survey['survey_id'], - false, - $courseId - ); - - foreach ($user_list as $user_id) { - isset($survey_user_list[$user_id]) ? $survey_user_list[$user_id]++ : $survey_user_list[$user_id] = 1; - } - } - } - } - - $urlBase = api_get_path(WEB_CODE_PATH).'my_space/myStudents.php?details=true&cid='.$courseId. - '&origin=tracking_course&sid='.$sessionId; - - $sortByFirstName = api_sort_by_first_name(); - Session::write('user_id_list', []); - $userIdList = []; - $addExerciseOption = api_get_setting('exercise.add_exercise_best_attempt_in_report', true); - $exerciseResultsToCheck = []; - if (!empty($addExerciseOption) && isset($addExerciseOption['courses']) && - isset($addExerciseOption['courses'][$courseCode]) - ) { - foreach ($addExerciseOption['courses'][$courseCode] as $exerciseId) { - $exercise = new Exercise(); - $exercise->read($exerciseId); - if ($exercise->iId) { - $exerciseResultsToCheck[] = $exercise; - } - } - } - while ($user = Database::fetch_assoc($res)) { - $userId = $user['user_id']; - $userIdList[] = $userId; - $userEntity = api_get_user_entity($userId); - $user['official_code'] = $user['col0']; - $user['username'] = $user['col3']; - $user['time'] = api_time_to_hms( - Tracking::get_time_spent_on_the_course( - $user['user_id'], - $courseId, - $sessionId - ) - ); - - $avg_student_score = Tracking::get_avg_student_score( - $userId, - $course, - [], - $session - ); - - $averageBestScore = Tracking::get_avg_student_score( - $user['user_id'], - $course, - [], - $session, - false, - false, - true - ); - - $avg_student_progress = Tracking::get_avg_student_progress( - $user['user_id'], - $course, - [], - $session - ); - - if (empty($avg_student_progress)) { - $avg_student_progress = 0; - } - $user['average_progress'] = $avg_student_progress.'%'; - - $total_user_exercise = Tracking::get_exercise_student_progress( - $exercises, - $user['user_id'], - $courseId, - $sessionId - ); - - $user['exercise_progress'] = $total_user_exercise; - - $total_user_exercise = Tracking::get_exercise_student_average_best_attempt( - $exercises, - $user['user_id'], - $courseId, - $sessionId - ); - - $user['exercise_average_best_attempt'] = $total_user_exercise; - - if (is_numeric($avg_student_score)) { - $user['student_score'] = $avg_student_score.'%'; - } else { - $user['student_score'] = $avg_student_score; - } - - if (is_numeric($averageBestScore)) { - $user['student_score_best'] = $averageBestScore.'%'; - } else { - $user['student_score_best'] = $averageBestScore; - } - - $exerciseResults = []; - if (!empty($exerciseResultsToCheck)) { - foreach ($exerciseResultsToCheck as $exercise) { - $bestExerciseResult = Event::get_best_attempt_exercise_results_per_user( - $user['user_id'], - $exercise->iId, - $courseId, - $sessionId, - false - ); - - $best = null; - if ($bestExerciseResult) { - $best = $bestExerciseResult['score'] / $bestExerciseResult['max_score']; - $best = round($best, 2) * 100; - $best .= '%'; - } - $exerciseResults['exercise_'.$exercise->iId] = $best; - } - } - $user['count_assignments'] = Container::getStudentPublicationRepository()->countUserPublications( - $userEntity, - $course, - $session - ); - $user['count_messages'] = Container::getForumPostRepository()->countUserForumPosts( - $userEntity, - $course, - $session - ); - $user['first_connection'] = Tracking::get_first_connection_date_on_the_course( - $user['user_id'], - $courseId, - $sessionId, - false === $export_csv - ); - - $user['last_connection'] = Tracking::get_last_connection_date_on_the_course( - $user['user_id'], - ['real_id' => $course->getId()], - $sessionId, - false === $export_csv - ); - - if ($export_csv) { - if (!empty($user['first_connection'])) { - $user['first_connection'] = api_get_local_time($user['first_connection']); - } else { - $user['first_connection'] = '-'; - } - if (!empty($user['last_connection'])) { - $user['last_connection'] = api_get_local_time($user['last_connection']); - } else { - $user['last_connection'] = '-'; - } - } - - if (empty($sessionId)) { - $user['survey'] = (isset($survey_user_list[$user['user_id']]) ? $survey_user_list[$user['user_id']] : 0).' / '.$total_surveys; - } - - $url = $urlBase.'&student='.$user['user_id']; - - $user['link'] = '
- '.Display::getMdiIcon( - 'fast-forward-outline', - 'ch-tool-icon', - null, - ICON_SIZE_MEDIUM, - get_lang('Details') - ).'
'; - - // store columns in array $users - $user_row = []; - $user_row['official_code'] = $user['official_code']; //0 - if ($sortByFirstName) { - $user_row['firstname'] = $user['col2']; - $user_row['lastname'] = $user['col1']; - } else { - $user_row['lastname'] = $user['col1']; - $user_row['firstname'] = $user['col2']; - } - $user_row['username'] = $user['username']; - $user_row['time'] = $user['time']; - $user_row['average_progress'] = $user['average_progress']; - $user_row['exercise_progress'] = $user['exercise_progress']; - $user_row['exercise_average_best_attempt'] = $user['exercise_average_best_attempt']; - $user_row['student_score'] = $user['student_score']; - $user_row['student_score_best'] = $user['student_score_best']; - if (!empty($exerciseResults)) { - foreach ($exerciseResults as $exerciseId => $bestResult) { - $user_row[$exerciseId] = $bestResult; - } - } - $user_row['count_assignments'] = $user['count_assignments']; - $user_row['count_messages'] = $user['count_messages']; - - $userGroupManager = new UserGroupModel(); - $user_row['classes'] = $userGroupManager->getLabelsFromNameList($user['user_id'], Usergroup::NORMAL_CLASS); - - if (empty($sessionId)) { - $user_row['survey'] = $user['survey']; - } else { - $userSession = SessionManager::getUserSession($user['user_id'], $sessionId); - $user_row['registered_at'] = ''; - if ($userSession) { - $user_row['registered_at'] = api_get_local_time($userSession['registered_at']); - } - } - - $user_row['first_connection'] = $user['first_connection']; - $user_row['last_connection'] = $user['last_connection']; - - // we need to display an additional profile field - if (isset($_GET['additional_profile_field'])) { - $data = Session::read('additional_user_profile_info'); - - $extraFieldInfo = Session::read('extra_field_info'); - foreach ($_GET['additional_profile_field'] as $fieldId) { - if (isset($data[$fieldId]) && isset($data[$fieldId][$user['user_id']])) { - if (is_array($data[$fieldId][$user['user_id']])) { - $user_row[$extraFieldInfo[$fieldId]['variable']] = implode( - ', ', - $data[$fieldId][$user['user_id']] - ); - } else { - $user_row[$extraFieldInfo[$fieldId]['variable']] = $data[$fieldId][$user['user_id']]; - } - } else { - $user_row[$extraFieldInfo[$fieldId]['variable']] = ''; - } - } - } - - if ('true' === api_get_setting('show_email_addresses')) { - $user_row['email'] = $user['col4']; - } - - $user_row['link'] = $user['link']; + $tblLpView = Database::get_course_table(TABLE_LP_VIEW); + $tblLpItem = Database::get_course_table(TABLE_LP_ITEM); + $tblLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW); + + $sql = "SELECT FROM_UNIXTIME(liv.start_time) as start_date + FROM $tblLpItemView liv + INNER JOIN + $tblLpView lv ON lv.iid = liv.lp_view_id + INNER JOIN + $tblLpItem li ON li.iid = liv.lp_item_id + WHERE + lv.user_id = $userId AND + lv.c_id = $courseId AND + lv.session_id = $sessionId AND + li.item_type = '".TOOL_LP_FINAL_ITEM."' AND + liv.status = 'completed' + ORDER BY start_date DESC + LIMIT 1"; - if ($export_csv) { - if (empty($sessionId)) { - unset($user_row['classes']); - unset($user_row['link']); - } else { - unset($user_row['classes']); - unset($user_row['link']); - } + $rs = Database::query($sql); + $lpFinalDate = Database::result($rs, 0, 0); - $csv_content[] = $user_row; - } - $users[] = array_values($user_row); + if (empty($lpFinalDate)) { + return false; } - if ($export_csv) { - Session::write('csv_content', $csv_content); + if ($convertDate) { + return api_convert_and_format_date($lpFinalDate, DATE_FORMAT_SHORT); } - Session::erase('additional_user_profile_info'); - Session::erase('extra_field_info'); - Session::write('user_id_list', $userIdList); - - return $users; + return $lpFinalDate; } /** - * Get data for users list in sortable with pagination. - * - * @param $from - * @param $number_of_items - * @param $column - * @param $direction - * @param $includeInvitedUsers boolean Whether include the invited users + * It gets the last finalization date of exercises in a course. * - * @return array + * @return string finalization date formatted or false if it is empty. */ - public static function getTotalTimeReport( - $from, - $number_of_items, - $column, - $direction, - $params = [] + public static function getCourseQuizLastFinalizationDate( + int $userId, + int $courseId, + int $sessionId, + bool $convertDate = true ) { - global $user_ids, $course_code, $export_csv, $sessionId; - $includeInvitedUsers = false; - $courseId = $params['cid']; - $sessionId = $params['sid']; - - $course_code = Database::escape_string($course_code); - $tbl_user = Database::get_main_table(TABLE_MAIN_USER); - $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER); - $access_url_id = api_get_current_access_url_id(); - - // get all users data from a course for sortable with limit - if (is_array($user_ids)) { - $user_ids = array_map('intval', $user_ids); - $condition_user = " WHERE user.user_id IN (".implode(',', $user_ids).") "; - } else { - $user_ids = (int) $user_ids; - $condition_user = " WHERE user.user_id = $user_ids "; - } - - $url_table = null; - $url_condition = null; - if (api_is_multiple_url_enabled()) { - $url_table = ", ".$tbl_url_rel_user." as url_users"; - $url_condition = " AND user.user_id = url_users.user_id AND access_url_id='$access_url_id'"; - } - - $invitedUsersCondition = ''; - if (!$includeInvitedUsers) { - $invitedUsersCondition = " AND user.status != ".INVITEE; - } + $tblTrackExercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); - $sql = "SELECT - user.user_id as user_id, - user.official_code as col0, - user.lastname as col1, - user.firstname as col2, - user.username as col3 - FROM $tbl_user as user $url_table - $condition_user $url_condition $invitedUsersCondition"; + $sql = "SELECT ex.exe_date + FROM $tblTrackExercise AS ex + WHERE + ex.c_id = $courseId AND + ex.session_id = $sessionId AND + ex.exe_user_id = $userId AND + ex.status = '' + ORDER BY ex.exe_date DESC + LIMIT 1"; + $rs = Database::query($sql); + $exeDate = Database::result($rs, 0, 0); - if (!in_array($direction, ['ASC', 'DESC'])) { - $direction = 'ASC'; + if (empty($exeDate)) { + return false; } - $column = (int) $column; - $from = (int) $from; - $number_of_items = (int) $number_of_items; - - $sql .= " ORDER BY col$column $direction "; - $sql .= " LIMIT $from,$number_of_items"; - - $res = Database::query($sql); - $users = []; - - $sortByFirstName = api_sort_by_first_name(); - $course = api_get_course_entity($courseId); - - while ($user = Database::fetch_assoc($res)) { - $user['official_code'] = $user['col0']; - $user['lastname'] = $user['col1']; - $user['firstname'] = $user['col2']; - $user['username'] = $user['col3']; - - $totalCourseTime = Tracking::get_time_spent_on_the_course( - $user['user_id'], - $courseId, - $sessionId - ); - - $user['time'] = api_time_to_hms($totalCourseTime); - $totalLpTime = Tracking::get_time_spent_in_lp( - $user['user_id'], - $course, - [], - $sessionId - ); - - $user['total_lp_time'] = $totalLpTime; - $warning = ''; - if ($totalLpTime > $totalCourseTime) { - $warning = ' '.Display::label(get_lang('Time difference'), 'danger'); - } - - $user['total_lp_time'] = api_time_to_hms($totalLpTime).$warning; - $user['first_connection'] = Tracking::get_first_connection_date_on_the_course( - $user['user_id'], - $courseId, - $sessionId - ); - $user['last_connection'] = Tracking::get_last_connection_date_on_the_course( - $user['user_id'], - $courseInfo, - $sessionId, - false === $export_csv - ); - - $user['link'] = ' -
- - '.Display::getMdiIcon( - 'fast-forward-outline', - 'ch-tool-icon', - null, - ICON_SIZE_SMALL, - get_lang('Details') - ).' -
'; - - // store columns in array $users - $user_row = []; - $user_row['official_code'] = $user['official_code']; //0 - if ($sortByFirstName) { - $user_row['firstname'] = $user['firstname']; - $user_row['lastname'] = $user['lastname']; - } else { - $user_row['lastname'] = $user['lastname']; - $user_row['firstname'] = $user['firstname']; - } - $user_row['username'] = $user['username']; - $user_row['time'] = $user['time']; - $user_row['total_lp_time'] = $user['total_lp_time']; - $user_row['first_connection'] = $user['first_connection']; - $user_row['last_connection'] = $user['last_connection']; - $user_row['link'] = $user['link']; - $users[] = array_values($user_row); + if ($convertDate) { + return api_convert_and_format_date($exeDate, DATE_FORMAT_SHORT); } - return $users; + return $exeDate; } - /** - * @param string $current - */ - public static function actionsLeft($current, $sessionId = 0, $addWrapper = true) - { - $usersLink = Display::url( - Display::getMdiIcon('account', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Report on learners')), - 'courseLog.php?'.api_get_cidreq(true, false) - ); - - $groupsLink = Display::url( - Display::getMdiIcon('account-group', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Group reporting')), - 'course_log_groups.php?'.api_get_cidreq() - ); - $resourcesLink = ''; - /*$resourcesLink = Display::url( - Display::getMdiIcon('chart-box', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Report on resource')), - 'course_log_resources.php?'.api_get_cidreq(true, false) - );*/ - - $courseLink = Display::url( - Display::getMdiIcon('book-open-page-variant ', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Course report')), - 'course_log_tools.php?'.api_get_cidreq(true, false) - ); - - $examLink = Display::url( - Display::getMdiIcon('chart-box', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Exam tracking')), - api_get_path(WEB_CODE_PATH).'tracking/exams.php?'.api_get_cidreq() - ); - - $eventsLink = Display::url( - Display::getMdiIcon('security', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Audit report')), - api_get_path(WEB_CODE_PATH).'tracking/course_log_events.php?'.api_get_cidreq() - ); - - $lpLink = Display::url( - Display::getMdiIcon('map-marker-path', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('CourseLPsGenericStats')), - api_get_path(WEB_CODE_PATH).'tracking/lp_report.php?'.api_get_cidreq() - ); - - $attendanceLink = ''; - if (!empty($sessionId)) { - $sessionInfo = api_get_session_info($sessionId); - $startDate = $sessionInfo['access_start_date']; - $endDate = $sessionInfo['access_end_date']; - $attendance = new Attendance(); - $checkExport = $attendance->getAttendanceLogin($startDate, $endDate); - if (false !== $checkExport) { - $attendanceLink = Display::url( - Display::getMdiIcon('av-timer', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Logins')), - api_get_path(WEB_CODE_PATH).'attendance/index.php?'.api_get_cidreq().'&action=calendar_logins' - ); - } - } - - switch ($current) { - case 'users': - $usersLink = Display::url( - Display::getMdiIcon('account', 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Report on learners')), - '#' - ); - break; - case 'groups': - $groupsLink = Display::url( - Display::getMdiIcon('account-group', 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Group reporting')), - '#' - ); - break; - case 'courses': - $courseLink = Display::url( - Display::getMdiIcon('book-open-page-variant', 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Course report')), - '#' - ); - break; - case 'resources': - $resourcesLink = Display::url( - Display::getMdiIcon('package-variant-closed', 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Report on resource')), - '#' - ); - break; - case 'exams': - $examLink = Display::url( - Display::getMdiIcon('order-bool-ascending-variant', 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Exam tracking')), - '#' - ); - break; - case 'logs': - $eventsLink = Display::url( - Display::getMdiIcon('security', 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Audit report')), - '#' - ); - break; - case 'attendance': - if (!empty($sessionId)) { - $attendanceLink = Display::url( - Display::getMdiIcon('av-timer', 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Logins')), - '#' - ); - } - break; - case 'lp': - $lpLink = Display::url( - Display::getMdiIcon('map-marker-path', 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('CourseLPsGenericStats')), - '#' - ); - break; - } - - $links = - $usersLink. - $groupsLink. - $courseLink. - $resourcesLink. - $examLink. - $eventsLink. - $lpLink. - $attendanceLink - ; - - if ($addWrapper) { - return Display::toolbarAction('tracking', [$links]); - } - - return $links; - } } diff --git a/public/main/inc/lib/usergroup.lib.php b/public/main/inc/lib/usergroup.lib.php index b4a4405a35..0055d602cc 100644 --- a/public/main/inc/lib/usergroup.lib.php +++ b/public/main/inc/lib/usergroup.lib.php @@ -18,6 +18,8 @@ use Chamilo\CoreBundle\Component\Utils\ToolIcon; */ class UserGroupModel extends Model { + public const SOCIAL_CLASS = 1; + public const NORMAL_CLASS = 0; public $columns = [ 'id', 'title', diff --git a/public/main/my_space/myStudents.php b/public/main/my_space/myStudents.php index cffe3e1da3..69fca2ae0e 100644 --- a/public/main/my_space/myStudents.php +++ b/public/main/my_space/myStudents.php @@ -350,7 +350,7 @@ switch ($action) { $studentId, $course, [], - $sId, + api_get_session_entity($sId), false, false, true diff --git a/public/main/session/session_list.php b/public/main/session/session_list.php index aec7e2f8f6..19312887af 100644 --- a/public/main/session/session_list.php +++ b/public/main/session/session_list.php @@ -18,9 +18,21 @@ $htmlHeadXtra[] = api_get_jqgrid_js(); $action = $_REQUEST['action'] ?? null; $idChecked = $_REQUEST['idChecked'] ?? null; +$idMultiple = $_REQUEST['id'] ?? null; $listType = isset($_REQUEST['list_type']) ? Security::remove_XSS($_REQUEST['list_type']) : SessionManager::getDefaultSessionTab(); +$copySessionContent = isset($_REQUEST['copy_session_content']) ? true : false; switch ($action) { + case 'delete_multiple': + $sessionList = explode(',', $idMultiple); + foreach ($sessionList as $id) { + $sessionInfo = api_get_session_info($id); + if ($sessionInfo) { + $response = SessionManager::delete($id); + } + } + echo 1; + exit; case 'delete': $sessionInfo = api_get_session_info($idChecked); if ($sessionInfo) { @@ -37,7 +49,6 @@ switch ($action) { } header('Location: '.$url); exit(); - break; case 'copy': $result = SessionManager::copy($idChecked); if ($result) { @@ -50,6 +61,32 @@ switch ($action) { $url = 'session_list.php?list_type='.$listType; } header('Location: '.$url); + exit; + case 'copy_multiple': + $sessionList = explode(',', $idMultiple); + foreach ($sessionList as $id) { + $sessionIdCopied = SessionManager::copy($id); + if ($sessionIdCopied) { + $sessionInfo = api_get_session_info($sessionIdCopied); + Display::addFlash(Display::return_message(get_lang('ItemCopied').' - '.$sessionInfo['name'])); + } else { + Display::addFlash(Display::return_message(get_lang('ThereWasAnError'), 'error')); + } + } + $url = 'session_list.php'; + if ('custom' !== $listType) { + $url = 'session_list.php?list_type='.$listType; + } + header('Location: '.$url); + exit; + case 'export_csv': + $selectedSessions = explode(',', $idMultiple); + SessionManager::exportSessionsAsCSV($selectedSessions); + break; + + case 'export_multiple': + $sessionList = explode(',', $idMultiple); + SessionManager::exportSessionsAsZip($sessionList); break; } @@ -169,6 +206,11 @@ $action_links = 'function action_formatter(cellvalue, options, rowObject) { $urlAjaxExtraField = api_get_path(WEB_AJAX_PATH).'extra_field.ajax.php?1=1'; $orderUrl = api_get_path(WEB_AJAX_PATH).'session.ajax.php?a=order'; +$deleteUrl = api_get_self().'?list_type='.$listType.'&action=delete_multiple'; +$copyUrl = api_get_self().'?list_type='.$listType.'&action=copy_multiple'; +$exportUrl = api_get_self().'?list_type='.$listType.'&action=export_multiple'; +$exportCsvUrl = api_get_self().'?list_type='.$listType.'&action=export_csv'; +$extra_params['multiselect'] = true; ?> "; $htmlHeadXtra[] = $js; @@ -141,6 +175,7 @@ $TABLECOURSE = Database::get_main_table(TABLE_MAIN_COURSE); $table_user = Database::get_main_table(TABLE_MAIN_USER); $TABLEQUIZ = Database::get_course_table(TABLE_QUIZ_TEST); +$userEditionExtraFieldToCheck = 'true' === api_get_setting('platform.user_edition_extra_field_to_check'); // Breadcrumbs. if ('resume_session' === $origin) { $interbreadcrumb[] = [ @@ -195,6 +230,31 @@ if (isset($_GET['additional_profile_field'])) { Session::write('additional_user_profile_info', $userProfileInfo); Session::write('extra_field_info', $extra_info); +$defaultExtraFields = []; +$defaultExtraFieldsFromSettings = []; +$defaultExtraFieldsFromSettings = api_get_setting('course.course_log_default_extra_fields', true); +if (!empty($defaultExtraFieldsFromSettings) && isset($defaultExtraFieldsFromSettings['extra_fields'])) { + $defaultExtraFields = $defaultExtraFieldsFromSettings['extra_fields']; + $defaultExtraInfo = []; + $defaultUserProfileInfo = []; + + foreach ($defaultExtraFields as $fieldName) { + $extraFieldInfo = UserManager::get_extra_field_information_by_name($fieldName); + + if (!empty($extraFieldInfo)) { + // Fetching only the user that are loaded NOT ALL user in the portal. + $defaultUserProfileInfo[$extraFieldInfo['id']] = TrackingCourseLog::getAdditionalProfileInformationOfFieldByUser( + $extraFieldInfo['id'], + $user_ids + ); + $defaultExtraInfo[$extraFieldInfo['id']] = $extraFieldInfo; + } + } + + Session::write('default_additional_user_profile_info', $defaultUserProfileInfo); + Session::write('default_extra_field_info', $defaultExtraInfo); +} + Display::display_header($nameTools, 'Tracking'); $actionsLeft = TrackingCourseLog::actionsLeft('users', $sessionId, false); @@ -202,13 +262,6 @@ $actionsLeft = TrackingCourseLog::actionsLeft('users', $sessionId, false); $actionsRight = ''. Display::getMdiIcon(ActionIcon::PRINT, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Print')).''; -$additionalParams = ''; -if (isset($_GET['additional_profile_field'])) { - foreach ($_GET['additional_profile_field'] as $fieldId) { - $additionalParams .= '&additional_profile_field[]='.(int) $fieldId; - } -} - $users_tracking_per_page = ''; if (isset($_GET['users_tracking_per_page'])) { $users_tracking_per_page = '&users_tracking_per_page='.intval($_GET['users_tracking_per_page']); @@ -326,8 +379,10 @@ $class = new UserGroupModel(); //$classes = $class->getUserGroupInCourse($options); $classes = $class->get_all(); +$bestScoreLabel = get_lang('Score').' - '.get_lang('BestAttempt'); + // Show the charts part only if there are students subscribed to this course/session -if ($nbStudents > 0) { +if ($nbStudents > 0 || isset($parameters['user_active'])) { // Classes $formClass = new FormValidator( 'classes', @@ -456,12 +511,18 @@ if ($nbStudents > 0) { if (false === $hideReports) { $conditions['course_id'] = $courseId; $conditions['include_invited_users'] = false; - $usersTracking = TrackingCourseLog::get_user_data( + $usersTracking = TrackingCourseLog::getUserData( 0, $nbStudents, $trackingColumn, $trackingDirection, - $conditions + $conditions, + true, + false, + null, + (int) $sessionId, + $export_csv, + $user_ids ); $userRepo = Container::getUserRepository(); foreach ($usersTracking as $userTracking) { @@ -474,7 +535,7 @@ if ($nbStudents > 0) { $numberStudentsCompletedLP++; } $averageStudentTestScore = substr($userTracking[7], 0, -1); - $averageStudentsTestScore += $averageStudentTestScore; + $averageStudentsTestScore .= $averageStudentTestScore; if ('100' === $averageStudentTestScore) { $reducedAverage = 9; @@ -574,11 +635,11 @@ if ($nbStudents > 0) { $el->setSelected(7); $form->addElement('hidden', 'action', 'add'); $form->addElement('hidden', 'remindallinactives', 'true'); - $form->addElement('hidden', 'cidReq', $course->getCode()); - $form->addElement('hidden', 'id_session', api_get_session_id()); + $form->addElement('hidden', 'cid', api_get_course_int_id()); + $form->addElement('hidden', 'sid', api_get_session_id()); $form->addButtonSend(get_lang('Notify')); - $extraFieldSelect = TrackingCourseLog::display_additional_profile_fields(); + $extraFieldSelect = TrackingCourseLog::displayAdditionalProfileFields(); if (!empty($extraFieldSelect)) { $html .= $extraFieldSelect; } @@ -612,7 +673,7 @@ if ($nbStudents > 0) { $table->setDataFunctionParams($conditions); } - $parameters['cidReq'] = isset($_GET['cidReq']) ? Security::remove_XSS($_GET['cidReq']) : ''; + $parameters['cid'] = isset($_GET['cid']) ? Security::remove_XSS($_GET['cid']) : ''; $parameters['sid'] = $sessionId; $parameters['from'] = isset($_GET['myspace']) ? Security::remove_XSS($_GET['myspace']) : null; @@ -718,6 +779,11 @@ if ($nbStudents > 0) { $headers['first_login'] = get_lang('First access to course'); $table->set_header($headerCounter++, get_lang('Latest access in course'), false); $headers['latest_login'] = get_lang('Latest access in course'); + $table->set_header($headerCounter++, get_lang('Lp Finalization Date'), false); + $headers['lp_finalization_date'] = get_lang('Lp Finalization Date'); + $table->set_header($headerCounter++, get_lang('Quiz Finalization Date'), false); + $headers['quiz_finalization_date'] = get_lang('Quiz Finalization Date'); + $counter = $headerCounter; if ('true' === api_get_setting('show_email_addresses')) { $table->set_header($counter, get_lang('Email'), false); @@ -732,6 +798,15 @@ if ($nbStudents > 0) { $parameters['additional_profile_field'] = $fieldId; } } + if (isset($defaultExtraFields)) { + if (!empty($defaultExtraInfo)) { + foreach ($defaultExtraInfo as $field) { + $table->set_header($counter, $field['display_text'], false); + $headers[$field['variable']] = $field['display_text']; + $counter++; + } + } + } $table->set_header($counter, get_lang('Details'), false); $headers['Details'] = get_lang('Details'); @@ -742,7 +817,7 @@ if ($nbStudents > 0) { $parameters[$key] = $value; } } - $parameters['cidReq'] = $courseCode; + $parameters['cid'] = api_get_course_int_id(); $parameters['sid'] = $sessionId; $table->set_additional_parameters($parameters); // display buttons to un hide hidden columns @@ -783,15 +858,7 @@ $groupTable->setHeaderContents(0, $column++, get_lang('Average time in the cours $groupTable->setHeaderContents(0, $column++, get_lang('Course progress')); $groupTable->setHeaderContents(0, $column++, get_lang('Exercise average')); -/*$exerciseList = ExerciseLib::get_all_exercises( - $courseInfo, - $sessionId, - false, - null, - false, - 3 -);*/ - +$exerciseList = []; $session = api_get_session_entity($sessionId); $qb = Container::getQuizRepository()->findAllByCourse($course, $session, null, 2, false); /** @var CQuiz[] $exercises */ @@ -946,7 +1013,8 @@ if (!empty($groupList)) { $totalBestScoreAverageNotInLP = 0; $bestScoreAverageNotInLP = 0; if (!empty($exercises)) { - foreach ($exercises as $exerciseData) { + foreach ($exercises as $i => $exerciseData) { + $exerciseList[$i]['iid'] = $exerciseData->getIid(); foreach ($studentIdList as $userId) { $results = Event::get_best_exercise_results_by_user( $exerciseData->getIid(), @@ -975,6 +1043,13 @@ if (!empty($groupList)) { 2 ).' %'; } + $bestScoreAverageNotInLP = (string) TrackingCourseLog::calcBestScoreAverageNotInLP( + $exerciseList, + $studentIdList, + (int) $courseInfo['real_id'], + $sessionId, + true + ); } $row = 1; @@ -1022,6 +1097,8 @@ if ($export_csv) { $csv_headers[] = get_lang('First access to course'); $csv_headers[] = get_lang('Latest access in course'); + $csv_headers[] = get_lang('Lp Finalization Date'); + $csv_headers[] = get_lang('Quiz Finalization Date'); if (isset($_GET['additional_profile_field'])) { foreach ($_GET['additional_profile_field'] as $fieldId) { @@ -1030,7 +1107,7 @@ if ($export_csv) { } ob_end_clean(); - $csvContentInSession = Session::read('csv_content'); + $csvContentInSession = Session::read('csv_content', []); // Adding headers before the content. array_unshift($csvContentInSession, $csv_headers); diff --git a/public/main/tracking/course_log_resources.php b/public/main/tracking/course_log_resources.php index 77b0c35da5..674b2eb3dd 100644 --- a/public/main/tracking/course_log_resources.php +++ b/public/main/tracking/course_log_resources.php @@ -28,7 +28,7 @@ if (!$is_allowedToTrack) { } if ($export_csv || $exportXls) { - $csvData = TrackingCourseLog::get_item_resources_data(0, 0, '', ''); + $csvData = TrackingCourseLog::getItemResourcesData(0, 0, '', ''); array_walk( $csvData, function (&$item) { diff --git a/public/phpinfo.php b/public/phpinfo.php new file mode 100755 index 0000000000..147cebcdd4 --- /dev/null +++ b/public/phpinfo.php @@ -0,0 +1 @@ + diff --git a/src/CoreBundle/Framework/Container.php b/src/CoreBundle/Framework/Container.php index 821d7472aa..a15e42fe2e 100644 --- a/src/CoreBundle/Framework/Container.php +++ b/src/CoreBundle/Framework/Container.php @@ -39,6 +39,7 @@ use Chamilo\CoreBundle\Repository\TagRepository; use Chamilo\CoreBundle\Repository\TrackEDownloadsRepository; use Chamilo\CoreBundle\Repository\TrackEExerciseRepository; use Chamilo\CoreBundle\Repository\TrackELoginRecordRepository; +use Chamilo\CoreBundle\Repository\TrackELoginRepository; use Chamilo\CoreBundle\Serializer\UserToJsonNormalizer; use Chamilo\CoreBundle\ServiceHelper\ContainerHelper; use Chamilo\CoreBundle\ServiceHelper\ThemeHelper; @@ -641,6 +642,11 @@ class Container return self::$container->get(SocialPostRepository::class); } + public static function getTrackELoginRepository(): TrackELoginRepository + { + return self::$container->get(TrackELoginRepository::class); + } + public static function getTrackELoginRecordRepository(): TrackELoginRecordRepository { return self::$container->get(TrackELoginRecordRepository::class); diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20230913162700.php b/src/CoreBundle/Migrations/Schema/V200/Version20230913162700.php index 3e65d58aaf..d7b28e907c 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20230913162700.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20230913162700.php @@ -32,7 +32,7 @@ final class Version20230913162700 extends AbstractMigrationChamilo $resourceNodeRepo = $this->container->get(ResourceNodeRepository::class); $q = $this->entityManager->createQuery('SELECT c FROM Chamilo\CoreBundle\Entity\Course c'); - $updateConfigurations = [ + /*$updateConfigurations = [ ['table' => 'c_tool_intro', 'field' => 'intro_text'], ['table' => 'c_course_description', 'field' => 'content'], ['table' => 'c_quiz', 'fields' => ['description', 'text_when_finished']], @@ -48,7 +48,7 @@ final class Version20230913162700 extends AbstractMigrationChamilo ['table' => 'c_survey', 'fields' => ['title', 'subtitle']], ['table' => 'c_survey_question', 'fields' => ['survey_question', 'survey_question_comment']], ['table' => 'c_survey_question_option', 'field' => 'option_text'], - ]; + ];*/ /** @var Course $course */ foreach ($q->toIterable() as $course) { @@ -59,9 +59,9 @@ final class Version20230913162700 extends AbstractMigrationChamilo continue; } - foreach ($updateConfigurations as $config) { + /* foreach ($updateConfigurations as $config) { $this->updateContent($config, $courseDirectory, $courseId, $documentRepo); - } + }*/ $this->updateHtmlContent($courseDirectory, $courseId, $documentRepo, $resourceNodeRepo); } @@ -155,6 +155,13 @@ final class Version20230913162700 extends AbstractMigrationChamilo $documentPath = str_replace('/courses/'.$courseDirectory.'/document/', '/', $videoPath); + error_log('Debugging Replace URLs:'); + error_log('Full URL: ' . $fullUrl); + error_log('Video Path: ' . $videoPath); + error_log('Actual Course Directory: ' . $actualCourseDirectory); + error_log('Processed Document Path: ' . $documentPath); + + /* $sql = "SELECT iid, path, resource_node_id FROM c_document WHERE c_id = $courseId AND path LIKE '$documentPath'"; $result = $this->connection->executeQuery($sql); $documents = $result->fetchAllAssociative(); @@ -170,7 +177,7 @@ final class Version20230913162700 extends AbstractMigrationChamilo $contentText = str_replace($matches[0][$index], $replacement, $contentText); } } - } + }*/ } return $contentText; diff --git a/src/CourseBundle/Repository/CQuizRepository.php b/src/CourseBundle/Repository/CQuizRepository.php index cf95c8a689..08f20b322d 100644 --- a/src/CourseBundle/Repository/CQuizRepository.php +++ b/src/CourseBundle/Repository/CQuizRepository.php @@ -40,7 +40,9 @@ final class CQuizRepository extends ResourceRepository implements ResourceWithLi $this->addCategoryQueryBuilder($categoryId, $qb); $this->addActiveQueryBuilder($active, $qb); $this->addNotDeletedQueryBuilder($qb); - $this->addTitleQueryBuilder($title, $qb); + if (!empty($title)) { + $this->addTitleQueryBuilder($title, $qb); + } return $qb; } diff --git a/translations/messages.en.po b/translations/messages.en.po index ccb12f958e..f9d047b6ff 100644 --- a/translations/messages.en.po +++ b/translations/messages.en.po @@ -27805,3 +27805,24 @@ msgstr "Select the destination folder" msgid "All users" msgstr "All users" + +msgid "Edit introduction" +msgstr "Edit introduction" + +msgid "To teacher and tutor" +msgstr "To teacher and tutor" + +msgid "To HR only" +msgstr "To HR only" + +msgid "Only for teachers" +msgstr "Only for teachers" + +msgid "Only for students" +msgstr "Only for students" + +msgid "Autolaunch settings" +msgstr "Autolaunch settings" + +msgid "Auto-launch" +msgstr "Auto-launch" diff --git a/translations/messages.es.po b/translations/messages.es.po index da90b620ff..db2e6946b1 100644 --- a/translations/messages.es.po +++ b/translations/messages.es.po @@ -28783,3 +28783,24 @@ msgstr "Seleccione la carpeta de destino" msgid "All users" msgstr "Todos los usuarios" + +msgid "Edit introduction" +msgstr "Editar la introduccion" + +msgid "To teacher and tutor" +msgstr "Para profesor y tutor" + +msgid "To HR only" +msgstr "Solo para RRHH" + +msgid "Only for teachers" +msgstr "Solo para profesores" + +msgid "Only for students" +msgstr "Solo para estudiantes" + +msgid "Autolaunch settings" +msgstr "Configuración de inicio automático" + +msgid "Auto-launch" +msgstr "Inicio automático" diff --git a/translations/messages.fr.po b/translations/messages.fr.po index b3309760e0..b6b9d378a4 100644 --- a/translations/messages.fr.po +++ b/translations/messages.fr.po @@ -1812,7 +1812,7 @@ msgid "Course program" msgstr "Cahier des charges" msgid "Statistics" -msgstr "Suivi" +msgstr "Statistiques" msgid "Upload page and link to Home Page" msgstr "Déposer page et lier à l'accueil" @@ -29585,3 +29585,24 @@ msgstr "Choisir le dossier de destination" msgid "All users" msgstr "Tous les utilisateurs" + +msgid "Edit introduction" +msgstr "Éditer l'introduction" + +msgid "To teacher and tutor" +msgstr "Pour le professeur et le tuteur" + +msgid "To HR only" +msgstr "Pour les RH uniquement" + +msgid "Only for teachers" +msgstr "Pour les professeurs uniquement" + +msgid "Only for students" +msgstr "Pour les apprenants uniquement" + +msgid "Autolaunch settings" +msgstr "Paramètres d'auto démarrage" + +msgid "Auto-launch" +msgstr "Auto-démarrage" diff --git a/translations/messages.pot b/translations/messages.pot index 1d55378b7c..11efd85d20 100644 --- a/translations/messages.pot +++ b/translations/messages.pot @@ -27791,3 +27791,24 @@ msgstr "" msgid "All users" msgstr "" + +msgid "Edit introduction" +msgstr "" + +msgid "To teacher and tutor" +msgstr "" + +msgid "To HR only" +msgstr "" + +msgid "Only for teachers" +msgstr "" + +msgid "Only for students" +msgstr "" + +msgid "Autolaunch settings" +msgstr "" + +msgid "Auto-launch" +msgstr ""