Merge branch '20691' into 1.11.x

pull/4701/head
Yannick Warnier 3 years ago
commit 6f13d08fbe
  1. 172
      main/admin/export_exercise_results.php
  2. 5
      main/admin/index.php
  3. 14
      main/exercise/exercise.class.php
  4. 15
      main/exercise/exercise_report.php
  5. 23
      main/exercise/exercise_show.php
  6. 72
      main/inc/lib/exercise.lib.php
  7. 8
      main/inc/lib/pdf.lib.php
  8. 3
      main/lang/english/trad4all.inc.php
  9. 3
      main/lang/french/trad4all.inc.php
  10. 3
      main/lang/spanish/trad4all.inc.php

@ -0,0 +1,172 @@
<?php
/* For licensing terms, see /license.txt */
/**
* This script exports the PDF reports from a test for several students at once.
*/
$cidReset = true;
require_once __DIR__.'/../inc/global.inc.php';
// Setting the section (for the tabs).
$this_section = SECTION_PLATFORM_ADMIN;
api_protect_admin_script(true);
$sessionId = isset($_REQUEST['session_id']) ? (int) $_REQUEST['session_id'] : null;
$courseId = isset($_GET['selected_course']) ? (int) $_GET['selected_course'] : null;
$exerciseId = isset($_REQUEST['exerciseId']) ? (int) $_REQUEST['exerciseId'] : null;
$courseIdChanged = isset($_GET['course_id_changed']) ? (int) $_GET['course_id_changed'] : null;
$exerciseIdChanged = isset($_GET['exercise_id_changed']) ? (int) $_GET['exercise_id_changed'] : null;
// Get the session list
$sessionList = SessionManager::get_sessions_by_user(api_get_user_id(), api_is_platform_admin());
// Course list, get course list of session, or for course where user is admin
$courseList = [];
if (!empty($sessionId) && $sessionId != '-1' && !empty($sessionList)) {
$sessionInfo = [];
foreach ($sessionList as $session) {
if ($session['session_id'] == $sessionId) {
$sessionInfo = $session;
}
}
$courseList = $sessionInfo['courses'];
} else {
if (api_is_platform_admin()) {
$courseList = CourseManager::get_courses_list(0, 0, 'title');
} else {
$courseList = CourseManager::get_course_list_of_user_as_course_admin(api_get_user_id());
}
}
$courseInfo = [];
if (empty($courseId)) {
$exerciseId = 0;
} else {
$courseInfo = api_get_course_info_by_id($courseId);
}
$interbreadcrumb[] = ['url' => 'index.php', 'name' => get_lang('PlatformAdmin')];
$confirmYourChoice = addslashes(get_lang('ConfirmYourChoice'));
$htmlHeadXtra[] = "
<script>
function submit_form(obj) {
document.export_all_results_form.submit();
}
function mark_course_id_changed() {
$('#course_id_changed').val('0');
}
function mark_exercise_id_changed() {
$('#exercise_id_changed').val('0');
}
function confirm_your_choice() {
return confirm('$confirmYourChoice');
}
</script>";
$sessionSelectList = [0 => get_lang('Select')];
foreach ($sessionList as $item) {
$sessionSelectList[$item['session_id']] = $item['session_name'];
}
$courseSelectList = [0 => get_lang('Select')];
foreach ($courseList as $item) {
$courseItemId = $item['real_id'];
$courseInfo = api_get_course_info_by_id($courseItemId);
$courseSelectList[$courseItemId] = '';
if ($courseItemId == $courseId) {
$courseSelectList[$courseItemId] = '>&nbsp;&nbsp;&nbsp;&nbsp;';
}
$courseSelectList[$courseItemId] = $courseInfo['title'];
}
// If course has changed, reset the menu default
if (!empty($courseSelectList) && !in_array($courseId, array_keys($courseSelectList))) {
$courseId = 0;
}
$courseInfo = api_get_course_info_by_id($courseId);
// Get exercise list for this course
$exerciseList = ExerciseLib::get_all_exercises_for_course_id(
$courseInfo,
$sessionId,
$courseId,
false
);
$exerciseSelectList = [];
$exerciseSelectList = [0 => get_lang('Select')];
if (is_array($exerciseList)) {
foreach ($exerciseList as $row) {
$exerciseTitle = $row['title'];
$exerciseSelectList[$row['iid']] = $exerciseTitle;
}
}
$url = api_get_self().'?'.api_get_cidreq().'&'.http_build_query(
[
'session_id' => $sessionId,
'selected_course' => $courseId,
'exerciseId' => $exerciseId,
'course_id_changed' => $courseIdChanged,
'exercise_id_changed' => $exerciseIdChanged,
]
);
// Form
$form = new FormValidator('export_all_results_form', 'GET', $url);
$form->addHeader(get_lang('ExportExerciseAllResults'));
$form
->addSelect(
'session_id',
get_lang('Session'),
$sessionSelectList,
['onchange' => 'submit_form(this)', 'id' => 'session_id']
)
->setSelected($sessionId);
$form
->addSelect(
'selected_course',
get_lang('Course'),
$courseSelectList,
['onchange' => 'mark_course_id_changed(); submit_form(this);', 'id' => 'selected_course']
)
->setSelected($courseId);
$form
->addSelect(
'exerciseId',
get_lang('Exercise'),
$exerciseSelectList
)
->setSelected($exerciseId);
$form->addHidden('course_id_changed', '0');
$form->addHidden('exercise_id_changed', '0');
$form->addButtonExport(get_lang('Export'), 'name');
if ($form->validate()) {
$values = $form->getSubmitValues();
if (!empty($values['exerciseId']) && !empty($values['selected_course'])) {
$sessionId = (int) $values['session_id'];
$courseId = (int) $values['selected_course'];
$exerciseId = (int) $values['exerciseId'];
ExerciseLib::exportExerciseAllResultsZip($sessionId, $courseId, $exerciseId);
}
}
Display::display_header(get_lang('ExportExerciseAllResults'));
echo Display::return_message(
get_lang('PleaseWaitThisCouldTakeAWhile'),
'normal',
false,
);
echo $form->display();
Display::display_footer();

@ -623,6 +623,11 @@ if (api_is_platform_admin() || ($allowCareer && api_is_session_admin())) {
'url' => 'resource_sequence.php',
'label' => get_lang('ResourcesSequencing'),
];
$items[] = [
'class' => 'item-export-exercise-results',
'url' => 'export_exercise_results.php',
'label' => get_lang('ExportExerciseAllResults'),
];
}
$blocks['sessions']['items'] = $items;

@ -8426,7 +8426,7 @@ class Exercise
*
* @return array exercises
*/
public function getExerciseAndResult($courseId, $sessionId, $quizId = [])
public function getExerciseAndResult($courseId, $sessionId, $quizId = [], $status = null)
{
if (empty($quizId)) {
return [];
@ -8439,13 +8439,20 @@ class Exercise
$ids = array_map('intval', $ids);
$ids = implode(',', $ids);
$track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
$condition = '';
if (isset($status)) {
$condition .= " AND te.status = '$status' ";
}
if (0 != $sessionId) {
$sql = "SELECT * FROM $track_exercises te
INNER JOIN c_quiz cq ON cq.iid = te.exe_exo_id
WHERE
te.id = %s AND
te.c_id = %s AND
te.session_id = %s AND
cq.iid IN (%s)
$condition
ORDER BY cq.iid";
$sql = sprintf($sql, $courseId, $sessionId, $ids);
@ -8453,8 +8460,9 @@ class Exercise
$sql = "SELECT * FROM $track_exercises te
INNER JOIN c_quiz cq ON cq.iid = te.exe_exo_id
WHERE
te.id = %s AND
te.c_id = %s AND
cq.iid IN (%s)
$condition
ORDER BY cq.iid";
$sql = sprintf($sql, $courseId, $ids);
}

@ -149,6 +149,16 @@ if (!empty($_REQUEST['export_report']) && $_REQUEST['export_report'] == '1') {
$objExerciseTmp = new Exercise();
$exerciseExists = $objExerciseTmp->read($exercise_id);
$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : null;
switch ($action) {
case 'export_all_results':
$sessionId = api_get_session_id();
$courseId = api_get_course_int_id();
ExerciseLib::exportExerciseAllResultsZip($sessionId, $courseId, $exercise_id);
break;
}
//Send student email @todo move this code in a class, library
if (isset($_REQUEST['comments']) &&
$_REQUEST['comments'] === 'update' &&
@ -438,6 +448,11 @@ if ($is_allowedToEdit && $origin !== 'learnpath') {
api_get_path(WEB_CODE_PATH).'exercise/recalculate_all.php?'.api_get_cidreq()."&exercise=$exercise_id"
);
$actions .= Display::url(
Display::return_icon('export_pdf.png', get_lang('ExportExerciseAllResults'), [], ICON_SIZE_MEDIUM),
api_get_self().'?'.api_get_cidreq().'&action=export_all_results&exerciseId='.$exercise_id
);
// clean result before a selected date icon
if ($allowClean) {
$actions .= Display::url(

@ -20,6 +20,7 @@ $origin = api_get_origin();
$currentUserId = api_get_user_id();
$printHeaders = 'learnpath' === $origin;
$id = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : 0; //exe id
$exportTypeAllResults = ('export' === $_GET['action'] && 'all_results' === $_GET['export_type']);
if (empty($id)) {
api_not_allowed(true);
@ -38,6 +39,7 @@ $learnpath_id = $track_exercise_info['orig_lp_id'];
$learnpath_item_id = $track_exercise_info['orig_lp_item_id'];
$lp_item_view_id = $track_exercise_info['orig_lp_item_view_id'];
$isBossOfStudent = false;
if (!$exportTypeAllResults) {
if (api_is_student_boss()) {
// Check if boss has access to user info.
if (UserManager::userIsBossOfStudent($currentUserId, $student_id)) {
@ -48,6 +50,7 @@ if (api_is_student_boss()) {
} else {
api_protect_course_script($printHeaders, false, true);
}
}
// Database table definitions
$TBL_EXERCISE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
@ -93,7 +96,8 @@ $is_allowedToEdit =
api_is_course_tutor() ||
api_is_session_admin() ||
api_is_drh() ||
api_is_student_boss();
api_is_student_boss() ||
$exportTypeAllResults;
if (!empty($sessionId) && !$is_allowedToEdit) {
if (api_is_course_session_coach(
@ -982,7 +986,24 @@ if ('export' === $action) {
'orientation' => 'P',
];
$pdf = new PDF('A4', $params['orientation'], $params);
if ('all_results' === $_GET['export_type']) {
$sessionId = api_get_session_id();
$courseId = api_get_course_int_id();
$exportName = 'S'.$sessionId.'-C'.$courseId.'-T'.$exercise_id;
$baseDir = api_get_path(SYS_ARCHIVE_PATH);
$folderName = 'pdfexport-'.$exportName;
$exportFolderPath = $baseDir.$folderName;
if (!is_dir($exportFolderPath)) {
@mkdir($exportFolderPath);
}
$pdfFileName = $user_info['firstname'].' '.$user_info['lastname'].'-attemptId'.$id.'.pdf';
$pdfFileName = api_replace_dangerous_char($pdfFileName);
$fileNameToSave = $exportFolderPath.'/'.$pdfFileName;
$pdf->html_to_pdf_with_template($content, true, false, true, [], 'F', $fileNameToSave);
} else {
$pdf->html_to_pdf_with_template($content, false, false, true);
}
exit;
}

@ -7150,4 +7150,76 @@ EOT;
error_log("Exercise ping received: exe_id = $exeId. _user not found in session.");
}
public static function saveFileExerciseResultPdf(
int $exeId,
int $courseId,
int $sessionId
) {
$cinfo = api_get_course_info_by_id($courseId);
$courseCode = $cinfo['code'];
$cidReq = 'cidReq='.$courseCode.'&id_session='.$sessionId.'&gidReq=0&gradebook=0';
$url = api_get_path(WEB_PATH).'main/exercise/exercise_show.php?'.$cidReq.'&id='.$exeId.'&action=export&export_type=all_results';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_COOKIE, session_id());
curl_setopt($ch, CURLOPT_AUTOREFERER, true);
curl_setopt($ch, CURLOPT_COOKIESESSION, true);
curl_setopt($ch, CURLOPT_FAILONERROR, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
}
public static function exportExerciseAllResultsZip(
int $sessionId,
int $courseId,
int $exerciseId
) {
$objExerciseTmp = new Exercise($courseId);
$exeResults = $objExerciseTmp->getExerciseAndResult(
$courseId,
$sessionId,
$exerciseId,
''
);
if (!empty($exeResults)) {
$exportName = 'S'.$sessionId.'-C'.$courseId.'-T'.$exerciseId;
$baseDir = api_get_path(SYS_ARCHIVE_PATH);
$folderName = 'pdfexport-'.$exportName;
$exportFolderPath = $baseDir.$folderName;
// 1. Cleans the export folder if it exists.
if (is_dir($exportFolderPath)) {
rmdirr($exportFolderPath);
}
// 2. Create the pdfs inside a new export folder path.
if (!empty($exeResults)) {
foreach ($exeResults as $exeResult) {
$exeId = (int) $exeResult['exe_id'];
ExerciseLib::saveFileExerciseResultPdf($exeId, $courseId, $sessionId);
}
}
// 3. If export folder is not empty will be zipped.
$isFolderPathEmpty = (file_exists($exportFolderPath) && 2 == count(scandir($exportFolderPath)));
if (!$isFolderPathEmpty) {
$exportFilePath = $baseDir.$exportName.'.zip';
$zip = new \PclZip($exportFilePath);
$zip->create($exportFolderPath, PCLZIP_OPT_REMOVE_PATH, $exportFolderPath);
rmdirr($exportFolderPath);
DocumentManager::file_send_for_download($exportFilePath, true, $exportName.'.zip');
exit;
}
}
return false;
}
}

@ -109,7 +109,9 @@ class PDF
$saveToFile = false,
$returnHtml = false,
$addDefaultCss = false,
$extraRows = []
$extraRows = [],
$outputMode = 'D',
$fileToSave = null
) {
if (empty($this->template)) {
$tpl = new Template('', false, false, false, false, true, false);
@ -173,9 +175,9 @@ class PDF
$css,
$this->params['filename'],
$this->params['course_code'],
'D',
$outputMode,
$saveToFile,
null,
$fileToSave,
$returnHtml,
$addDefaultCss
);

@ -7291,7 +7291,7 @@ $CourseCreationUsesTemplateComment = "Set this to use the same template course (
$EnablePasswordStrengthCheckerText = "Password strength checker";
$EnablePasswordStrengthCheckerComment = "Enable this option to add a visual indicator of password strength, when the user changes his/her password. This will NOT prevent bad passwords to be added, it only acts as a visual helper.";
$EnableCaptchaText = "CAPTCHA";
$EnableCaptchaComment = "Enable a CAPTCHA on the login form to avoid password hammering";
$EnableCaptchaComment = "Enable a CAPTCHA on the login form, inscription form and lost password form to avoid password hammering";
$CaptchaNumberOfMistakesBeforeBlockingAccountText = "CAPTCHA mistakes allowance";
$CaptchaNumberOfMistakesBeforeBlockingAccountComment = "The number of times a user can make a mistake on the CAPTCHA box before his account is locked out.";
$CaptchaTimeAccountIsLockedText = "CAPTCHA account locking time";
@ -9013,4 +9013,5 @@ $AllowSubscriptions = "Allow subscriptions";
$MaxSubscriptions = "Maximum number of subscriptions";
$NoMoreAccessible = "No longer available";
$AccessibleFrom = "Available from";
$ExportExerciseAllResults = "Export all results from an exercise";
?>

@ -7243,7 +7243,7 @@ $CourseCreationUsesTemplateComment = "Configurez ce paramètre pour utiliser le
$EnablePasswordStrengthCheckerText = "Valider la complexité du mot de passe";
$EnablePasswordStrengthCheckerComment = "L'activation de cette option fera apparaître un indicateur de complexité de mot de passe quand l'utilisateur modifie son mot de passe. Ceci n'empêche *PAS* l'introduction d'un mauvais mot de passe. Il s'agit seulement d'une aide visuelle.";
$EnableCaptchaText = "CAPTCHA";
$EnableCaptchaComment = "Activer cette option fera apparaître un CAPTCHA dans le formulaire de login pour éviter les tentatives de pénétration par force brute.";
$EnableCaptchaComment = "Activer cette option fera apparaître un CAPTCHA dans les formulaires de login, d'inscription et de mot de passe perdu pour éviter les tentatives de pénétration par force brute.";
$CaptchaNumberOfMistakesBeforeBlockingAccountText = "Marge d'erreur du login avec CAPTCHA";
$CaptchaNumberOfMistakesBeforeBlockingAccountComment = "Nombre de fois qu'un utillisateur peut se tromper dans l'introduction de son nom d'utilisateur et de son mot de passe avant que son compte ne soit congelé pour un certain temps.";
$CaptchaTimeAccountIsLockedText = "Temps de blocage CAPTCHA";
@ -8947,4 +8947,5 @@ $AllowSubscriptions = "Autoriser les inscriptions";
$MaxSubscriptions = "Nombre maximum d'inscriptions";
$NoMoreAccessible = "Plus accessible";
$AccessibleFrom = "Accessible à partir du";
$ExportExerciseAllResults = "Exporter tous les résultats d'un exercice";
?>

@ -7317,7 +7317,7 @@ $CourseCreationUsesTemplateComment = "Configure este parámetro para usar el mis
$EnablePasswordStrengthCheckerText = "Validar complejidad de contraseña";
$EnablePasswordStrengthCheckerComment = "Al activar esta opción, aparecerá un indicador de complejidad de contraseña cuando el usuario cambie su contraseña. Esto *NO* prohíbe el ingreso de una mala contraseña. Solamente actúa como una ayuda visual.";
$EnableCaptchaText = "CAPTCHA";
$EnableCaptchaComment = "Al activar esta opción, aparecerá un CAPTCHA en el formulario de ingreso, para evitar los intentos de ingreso por fuerza bruta";
$EnableCaptchaComment = "Al activar esta opción, aparecerá un CAPTCHA en los formularios de ingreso, inscripcion y contraseña perdida para evitar los intentos de ingreso por fuerza bruta";
$CaptchaNumberOfMistakesBeforeBlockingAccountText = "Margen de errores en CAPTCHA";
$CaptchaNumberOfMistakesBeforeBlockingAccountComment = "Cuantas veces uno se puede equivocar al ingresar su usuario y contraseña con el CAPTCHA antes de que su cuenta quede congelada por un tiempo.";
$CaptchaTimeAccountIsLockedText = "Tiempo bloqueo CAPTCHA";
@ -9038,4 +9038,5 @@ $AllowSubscriptions = "Permitir suscripciones";
$MaxSubscriptions = "Cantidad máxima permitida de suscripciones";
$NoMoreAccessible = "No mas accesible";
$AccessibleFrom = "Accesible desde el";
$ExportExerciseAllResults = "Exportar todos los resultados de un ejercicio";
?>
Loading…
Cancel
Save