This commit is contained in:
Xes
2025-08-14 22:39:38 +02:00
parent 3641e93527
commit 5403f346e3
3370 changed files with 327179 additions and 0 deletions
+173
View File
@@ -0,0 +1,173 @@
<?php
/* For license terms, see /license.txt */
require_once '../config.php';
$plugin = Test2pdfPlugin::create();
$enable = $plugin->get('enable_plugin') == 'true';
if (!$enable) {
header('Location: ../../../index.php');
exit;
}
api_protect_course_script();
$courseId = intval($_GET['c_id']);
$sessionId = api_get_session_id();
$quizId = intval($_GET['id_quiz']);
$infoCourse = api_get_course_info_by_id($courseId);
$infoQuiz = getInfoQuiz($courseId, $quizId);
$titleCourse = removeHtml($infoCourse['title']);
$titleQuiz = removeHtml($infoQuiz['title']);
$mpdf = new PDF();
$mpdf->set_header($infoCourse);
$mpdf->set_footer();
$pdf = $mpdf->pdf;
$pdf->SetTitle($titleCourse.' - '.$titleQuiz);
$pdf->AddPage();
$pdf->SetFont('Arial', '', 16);
$pdf->SetTextColor(64);
$pdf->MultiCell(0, 7, $titleQuiz, 0, 'L', false);
if (!empty($infoQuiz['description'])) {
$pdf->WriteHTML(
PDF::fixImagesPaths(
removeQuotes($infoQuiz['description']),
$infoCourse
)
);
}
// Select all questions of the supported types from the given course
$questionsList = getQuestionsFromCourse($courseId, $quizId, $sessionId);
// Go through all questions and get the answers
if ($_GET['type'] == 'question' || $_GET['type'] == 'all') {
$j = 1;
foreach ($questionsList as $key => $value) {
$infoQuestion = getInfoQuestion($courseId, $value);
if ($pdf->y > 240) {
$pdf->AddPage();
}
$pdf->SetFont('Arial', '', 12);
$pdf->SetTextColor(64);
$pdf->MultiCell(0, 7, ($key + $j).' - '.$infoQuestion['question'], 0, 'L', false);
if (!empty($infoQuestion['description'])) {
$pdf->WriteHTML(
PDF::fixImagesPaths(
removeQuotes($infoQuestion['description']),
$infoCourse
)
);
}
$infoAnswer = getAnswers($courseId, $value);
foreach ($infoAnswer as $key2 => $value2) {
$pdf->SetFont('Arial', 'I', 10);
$pdf->SetTextColor(96);
if ($infoQuestion['type'] == 3) {
$listAnswerInfo = getAnswerFillInBlanks($value2['answer']);
$answerText = $listAnswerInfo['text'];
if (!empty($listAnswerInfo['words_with_bracket'])) {
foreach ($listAnswerInfo['words_with_bracket'] as $wordWithBracket) {
if (!empty($wordWithBracket)) {
$answerText = str_replace($wordWithBracket, '[_____]', $answerText);
}
}
}
$answerText = str_replace(['<p>', '</p>'], ['', '. '], $answerText);
$pdf->MultiCell(0, 5, removeHtml($answerText), 0, 'L', false);
} else {
$pdf->Cell(1, 7, '', 0, 0);
$pdf->Rect($pdf->x + 2, $pdf->y, 4, 4);
$pdf->Cell(7, 7, '', 0, 0);
$pdf->MultiCell(0, 5, $letters[$key2].' - '.removeHtml($value2['answer']), 0, 'L', false);
}
$pdf->Ln(1);
}
$pdf->Ln(4);
}
}
$j = 1;
if ($_GET['type'] == 'answer' || $_GET['type'] == 'all') {
$answerList = [];
foreach ($questionsList as $key => $value) {
$infoQuestion = getInfoQuestion($courseId, $value);
if ($infoQuestion['question'] == $plugin->get_lang('Statement')) {
$j = 0;
} else {
$answers = '';
$infoQuestion = getInfoQuestion($courseId, $value);
if ($infoQuestion['type'] == 2 ||
$infoQuestion['type'] == 3 ||
$infoQuestion['type'] == 9 ||
$infoQuestion['type'] == 11 ||
$infoQuestion['type'] == 12 ||
$infoQuestion['type'] == 14
) {
$infoAnswer = getAnswers($courseId, $value);
$answers .= ' '.($key + $j).' -';
if ($infoQuestion['type'] == 3) {
foreach ($infoAnswer as $key2 => $value2) {
$listAnswerInfo = getAnswerFillInBlanks($value2['answer']);
if (!empty($listAnswerInfo['words'])) {
$answersList = implode(', ', $listAnswerInfo['words']);
$answers .= clearStudentAnswer($answersList);
}
}
} else {
foreach ($infoAnswer as $key2 => $value2) {
if ($value2['correct'] == 1) {
$answers .= ' '.$letters[$key2].',';
}
}
$i = strrpos($answers, ',');
$answers = substr($answers, 0, $i);
$answers .= ' ';
}
$answerList[] = $answers;
} else {
$infoAnswer = getAnswers($courseId, $value);
foreach ($infoAnswer as $key2 => $value2) {
if ($value2['correct'] == 1) {
$answers .= ' '.($key + $j).' - '.$letters[$key2].' ';
break;
}
}
$answerList[] = $answers;
}
}
}
$pdf->SetFont('Arial', '', 12);
$pdf->SetTextColor(64);
$pdf->Cell(0, 7, $plugin->get_lang('AnswersColumn'), 0, 1, 'L', false);
$pdf->SetFont('Arial', 'I', 10);
$pdf->SetTextColor(64, 64, 255);
$i = 1;
foreach ($answerList as $resp) {
$pdf->Cell(50, 6, $resp, 0);
if ($i % 4 == 0) {
$pdf->Ln();
}
$i++;
}
}
$typeSuffix = 'q';
switch ($_GET['type']) {
case 'question':
$typeSuffix = 'Q';
break;
case 'answer':
$typeSuffix = 'A';
break;
case 'all':
$typeSuffix = 'QA';
break;
}
// Name the file download as something like 'C2-S0-Q34-QA' where C is course ID, S is session ID, Q is quiz ID & QA is the type
$filename = 'C'.$courseId.'-S'.(empty($sessionId) ? '0' : $sessionId).'-Q'.$quizId.'-'.$typeSuffix;
$pdf->Output($filename, 'I');
+1
View File
@@ -0,0 +1 @@
<?php
+7
View File
@@ -0,0 +1,7 @@
<?php
/* For license terms, see /license.txt */
/**
* Index of the Test to pdf plugin courses list.
*
* @package chamilo.plugin.test2pdf
*/
+434
View File
@@ -0,0 +1,434 @@
<?php
/* For license terms, see /license.txt */
/**
* Functions.
*
* @package chamilo.plugin.test2pdf
*/
$letters = [
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z',
];
/**
* List exercises.
*
* @param int $courseId Course ID
* @param int $sessionId Session ID
*
* @throws Exception
*
* @return array Results (list of exercise details)
*/
function showExerciseCourse($courseId, $sessionId = 0)
{
$tableQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
$tableLpItem = Database::get_course_table(TABLE_LP_ITEM);
$courseId = (int) $courseId;
$sessionId = (int) $sessionId;
$conditionSession = api_get_session_condition($sessionId, true, true, 'a.session_id');
$sql = "SELECT a.*
FROM $tableQuiz a
LEFT JOIN $tableLpItem b ON a.iid = b.path AND a.c_id = b.c_id
WHERE a.c_id = $courseId
AND (a.active = 1 OR (item_type = 'quiz' AND b.c_id = $courseId))
$conditionSession
ORDER BY a.title ASC;";
$res = Database::query($sql);
$aux = [];
while ($row = Database::fetch_assoc($res)) {
$aux[] = $row;
}
return $aux;
}
/**
* List quiz details.
*
* @throws Exception
*
* @return array Results (list of quiz details)
*/
function getInfoQuiz($courseId, $id)
{
$courseId = (int) $courseId;
$id = (int) $id;
$tableQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
$sql = "SELECT * FROM $tableQuiz WHERE iid = $id";
$res = Database::query($sql);
$row = Database::fetch_assoc($res);
return $row;
}
/**
* List question_id.
*
* @throws Exception
*
* @return array Results (list question ID)
*/
function getQuestionsFromCourse($courseId, $quizId, $sessionId = 0)
{
$courseId = (int) $courseId;
$quizId = (int) $quizId;
$sessionId = (int) $sessionId;
$tableQuizQuestion = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
$tableQuestion = Database::get_course_table(TABLE_QUIZ_QUESTION);
$tableQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
$conditionSession = api_get_session_condition($sessionId, true, true, 'q.session_id');
$sql = "SELECT a.question_id AS question_id
FROM $tableQuizQuestion a
INNER JOIN $tableQuestion b ON a.question_id = b.iid
INNER JOIN $tableQuiz q ON q.iid = a.exercice_id
WHERE a.c_id = $courseId AND a.exercice_id = $quizId
AND (b.type IN (1, 2, 3, 9, 10, 11, 12, 14))
$conditionSession
ORDER BY question_order ASC;";
$res = Database::query($sql);
$aux = [];
while ($row = Database::fetch_assoc($res)) {
$aux[] = $row['question_id'];
}
return $aux;
}
/**
* List question details.
*
* @throws Exception
*
* @return array Results (list of question details)
*/
function getInfoQuestion($courseId, $id)
{
$courseId = (int) $courseId;
$id = (int) $id;
$tableQuestion = Database::get_course_table(TABLE_QUIZ_QUESTION);
$sql = "SELECT * FROM $tableQuestion
WHERE
iid = $id
AND (type IN (1, 2, 3, 9, 10, 11, 12, 14))";
$res = Database::query($sql);
$row = Database::fetch_assoc($res);
return $row;
}
/**
* List answer details.
*
* @throws Exception
*
* @return array Results (list of answer by question_id)
*/
function getAnswers($courseId, $id)
{
$courseId = (int) $courseId;
$id = (int) $id;
$tableQuizAnswer = Database::get_course_table(TABLE_QUIZ_ANSWER);
$sql = "SELECT * FROM $tableQuizAnswer
WHERE question_id = $id
ORDER BY position ASC;";
$res = Database::query($sql);
$aux = [];
while ($row = Database::fetch_assoc($res)) {
$aux[] = $row;
}
return $aux;
}
/**
* Information of an answer type Fill in the blanks.
*
* @param $answer
*
* @return array
*/
function getAnswerFillInBlanks($answer)
{
$listAnswerResults = [];
$listAnswerResults['text'] = '';
$listAnswerResults['words_count'] = 0;
$listAnswerResults['words_with_bracket'] = [];
$listAnswerResults['words'] = [];
$listAnswerResults['weighting'] = [];
$listAnswerResults['input_size'] = [];
$listAnswerResults['switchable'] = '';
$listAnswerResults['student_answer'] = [];
$listAnswerResults['student_score'] = [];
$listAnswerResults['blank_separator_number'] = 0;
$listDoubleColon = [];
api_preg_match("/(.*)::(.*)$/s", $answer, $listResult);
if (count($listResult) < 2) {
$listDoubleColon[] = '';
$listDoubleColon[] = '';
} else {
$listDoubleColon[] = $listResult[1];
$listDoubleColon[] = $listResult[2];
}
$listAnswerResults['system_string'] = $listDoubleColon[1];
// Make sure we only take the last bit to find special marks
$listArobaseSplit = explode('@', $listDoubleColon[1]);
if (count($listArobaseSplit) < 2) {
$listArobaseSplit[1] = '';
}
// Take the complete string except after the last '::'
$listDetails = explode(':', $listArobaseSplit[0]);
// < number of item after the ::[score]:[size]:[separator_id]@ , here there are 3
if (count($listDetails) < 3) {
$listWeightings = explode(',', $listDetails[0]);
$listSizeOfInput = [];
for ($i = 0; $i < count($listWeightings); $i++) {
$listSizeOfInput[] = 200;
}
$blankSeparatorNumber = 0; // 0 is [...]
} else {
$listWeightings = explode(',', $listDetails[0]);
$listSizeOfInput = explode(',', $listDetails[1]);
$blankSeparatorNumber = $listDetails[2];
}
$listSeparators = [
['[', ']'],
['{', '}'],
['(', ')'],
['*', '*'],
['#', '#'],
['%', '%'],
['$', '$'],
];
$listAnswerResults['text'] = $listDoubleColon[0];
$listAnswerResults['weighting'] = $listWeightings;
$listAnswerResults['input_size'] = $listSizeOfInput;
$listAnswerResults['switchable'] = $listArobaseSplit[1];
$listAnswerResults['blank_separator_start'] = $listSeparators[$blankSeparatorNumber][0];
$listAnswerResults['blank_separator_end'] = $listSeparators[$blankSeparatorNumber][1];
$listAnswerResults['blank_separator_number'] = $blankSeparatorNumber;
$blankCharStart = $listSeparators[$blankSeparatorNumber][0];
$blankCharEnd = $listSeparators[$blankSeparatorNumber][1];
$blankCharStartForRegexp = escapeForRegexp($blankCharStart);
$blankCharEndForRegexp = escapeForRegexp($blankCharEnd);
// Get all blanks words
$listAnswerResults['words_count'] = api_preg_match_all(
'/'.$blankCharStartForRegexp.'[^'.$blankCharEndForRegexp.']*'.$blankCharEndForRegexp.'/',
$listDoubleColon[0],
$listWords
);
if ($listAnswerResults['words_count'] > 0) {
$listAnswerResults['words_with_bracket'] = $listWords[0];
// remove [ and ] in string
array_walk(
$listWords[0],
function (&$value, $key, $tabBlankChar) {
$trimChars = '';
for ($i = 0; $i < count($tabBlankChar); $i++) {
$trimChars .= $tabBlankChar[$i];
}
$value = trim($value, $trimChars);
$key = trim($key);
},
[$blankCharStart, $blankCharEnd]
);
$listAnswerResults['words'] = $listWords[0];
}
// Get all common words
$commonWords = api_preg_replace(
'/'.$blankCharStartForRegexp.'[^'.$blankCharEndForRegexp.']*'.$blankCharEndForRegexp.'/',
'::',
$listDoubleColon[0]
);
$listAnswerResults['common_words'] = explode('::', $commonWords);
return $listAnswerResults;
}
/**
* Escapes text used for question type Fill in the blanks.
*
* @param $inChar
*
* @return mixed|string
*/
function escapeForRegexp($inChar)
{
$listChars = [
".",
"+",
"*",
"?",
"[",
"^",
"]",
"$",
"(",
")",
"{",
"}",
"=",
"!",
">",
"|",
":",
"-",
")",
];
if (in_array($inChar, $listChars)) {
return "\\".$inChar;
} else {
return $inChar;
}
}
/**
* Clear the answer entered.
*
* @param string $answer
*
* @return string
*/
function clearStudentAnswer($answer)
{
$answer = api_html_entity_decode($answer);
$answer = api_preg_replace('/\s\s+/', ' ', $answer); // replace excess white spaces
$answer = str_replace('&#39;', '&#039;', $answer);
$answer = strtr($answer, array_flip(get_html_translation_table(HTML_ENTITIES, ENT_QUOTES)));
return trim($answer);
}
/**
* Remove all html tag.
*
* @param string $string The string to be stripped of HTML
*
* @return string clean of html tag
*/
function removeHtml($string)
{
$txt = str_replace("<html>", "", $string);
$txt = str_replace("<head>", "", $txt);
$txt = str_replace("<title>", "", $txt);
$txt = str_replace("</title>", "", $txt);
$txt = str_replace("</head>", "", $txt);
$txt = str_replace("<body>", "", $txt);
$txt = str_replace("</body>", "", $txt);
$txt = str_replace("</html>", "", $txt);
$txt = strip_tags($txt);
$txt = str_replace(chr(13).chr(10), "", $txt);
$txt = str_replace("&nbsp;", " ", $txt);
$txt = str_replace("&Aacute;", "Á", $txt);
$txt = str_replace("&aacute;", "á", $txt);
$txt = str_replace("&Eacute;", "É", $txt);
$txt = str_replace("&eacute;", "é", $txt);
$txt = str_replace("&Iacute;", "Í", $txt);
$txt = str_replace("&iacute;", "í", $txt);
$txt = str_replace("&Oacute;", "Ó", $txt);
$txt = str_replace("&oacute;", "ó", $txt);
$txt = str_replace("&Uacute;", "Ú", $txt);
$txt = str_replace("&uacute;", "ú", $txt);
$txt = str_replace("&Ntilde;", "Ñ", $txt);
$txt = str_replace("&ntilde;", "ñ", $txt);
$txt = str_replace("&agrave;", "à", $txt);
$txt = str_replace("&Agrave;", "À", $txt);
$txt = str_replace("&iexcl;", "¡", $txt);
$txt = str_replace("&middot;", "·", $txt);
$txt = str_replace("&Ccedil;", "Ç", $txt);
$txt = str_replace("&ccedil;", "ç", $txt);
$txt = str_replace("&quot;", '"', $txt);
$txt = str_replace("&ordf;", 'ª', $txt);
$txt = str_replace("&ordm;", 'º', $txt);
$txt = str_replace("&amp;", '&', $txt);
$txt = str_replace("&bull;", '•', $txt);
$txt = str_replace("&iquest;", '¿', $txt);
$txt = str_replace("&euro;", 'EUR', $txt);
$txt = str_replace("&uuml;", 'ü', $txt);
$txt = str_replace("&Uuml;", 'Ü', $txt);
$txt = str_replace("&uml;", '¨', $txt);
return $txt;
}
/**
* Remove all html tag.
*
* @param string $string The string to be stripped of accents
*
* @return string clean of html tag
*/
function removeQuotes($string)
{
$txt = str_replace("&nbsp;", " ", $string);
$txt = str_replace("&Aacute;", "Á", $txt);
$txt = str_replace("&aacute;", "á", $txt);
$txt = str_replace("&Eacute;", "É", $txt);
$txt = str_replace("&eacute;", "é", $txt);
$txt = str_replace("&Iacute;", "Í", $txt);
$txt = str_replace("&iacute;", "í", $txt);
$txt = str_replace("&Oacute;", "Ó", $txt);
$txt = str_replace("&oacute;", "ó", $txt);
$txt = str_replace("&Uacute;", "Ú", $txt);
$txt = str_replace("&uacute;", "ú", $txt);
$txt = str_replace("&Ntilde;", "Ñ", $txt);
$txt = str_replace("&ntilde;", "ñ", $txt);
$txt = str_replace("&quot;", '"', $txt);
$txt = str_replace("&ordf;", 'ª', $txt);
$txt = str_replace("&ordm;", 'º', $txt);
$txt = str_replace("&amp;", '&', $txt);
$txt = str_replace("&bull;", '•', $txt);
$txt = str_replace("&iquest; &", '¿', $txt);
$txt = str_replace("&agrave;", "à", $txt);
$txt = str_replace("&Agrave;", "À", $txt);
$txt = str_replace("&iexcl;", "¡", $txt);
$txt = str_replace("&middot;", "·", $txt);
$txt = str_replace("&Ccedil;", "Ç", $txt);
$txt = str_replace("&ccedil;", "ç", $txt);
$txt = str_replace("&euro;", 'EUR', $txt);
$txt = str_replace("&uuml;", 'ü', $txt);
$txt = str_replace("&Uuml;", 'Ü', $txt);
$txt = str_replace("uml;", '¨', $txt);
return $txt;
}
@@ -0,0 +1,77 @@
<?php
/**
* Plugin class for the Test2Pdf plugin.
*
* @package chamilo.plugin.test2pdf
*
* @author Jose Angel Ruiz <desarrollo@nosolored.com>
*/
class Test2pdfPlugin extends Plugin
{
public $isCoursePlugin = true;
protected function __construct()
{
parent::__construct(
'1.0',
'Jose Angel Ruiz - NoSoloRed (original author)',
[
'enable_plugin' => 'boolean',
]
);
}
/**
* @return StaticPlugin
*/
public static function create()
{
static $result = null;
return $result ? $result : $result = new self();
}
/**
* This method creates the tables required to this plugin.
*/
public function install()
{
//Installing course settings
$this->setCourseToolDefaultVisibility(false);
$this->install_course_fields_in_all_courses();
$list = [
'/64/test2pdf.png',
'/64/test2pdf_na.png',
'/32/test2pdf.png',
'/32/test2pdf_na.png',
'/22/test2pdf.png',
];
$res = true;
foreach ($list as $file) {
$source = __DIR__.'/../resources/img/'.$file;
$destination = __DIR__.'/../../../main/img/icons/'.$file;
$res = @copy($source, $destination);
if (!$res) {
break;
}
}
if (!$res) {
$warning = 'Test2PDF plugin icons could not be copied to main/img/ because of folder permissions.
To fix, give web server user permissions to write to main/img/ before enabling this plugin.';
Display::addFlash(Display::return_message($warning, 'warning'));
}
}
/**
* This method drops the plugin tables.
*/
public function uninstall()
{
// Deleting course settings.
$this->uninstall_course_fields_in_all_courses($this->course_settings);
$this->manageTab(false);
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/* For license terms, see /license.txt */
/**
* Configuration script for the Test to Pdf plugin.
*
* @package chamilo.plugin.test2pdf
*/
require_once '../config.php';
api_protect_course_script(true);
$plugin = Test2pdfPlugin::create();
$enable = $plugin->get('enable_plugin') == 'true';
if ($enable) {
$templateName = $plugin->get_lang('ViewExercises');
$tpl = new Template($templateName);
$courseId = api_get_course_int_id();
$sessionId = api_get_session_id();
$infoExercise = showExerciseCourse($courseId, $sessionId);
$tpl->assign('infoExercise', $infoExercise);
$tpl->assign('course_id', $courseId);
$listing_tpl = 'test2pdf/view/view-pdf.tpl';
$content = $tpl->fetch($listing_tpl);
$tpl->assign('content', $content);
$tpl->display_one_col_template();
} else {
Display::addFlash(Display::return_message($plugin->get_lang('PluginDisabledFromAdminPanel')));
header(
'Location:'.api_get_path(WEB_PATH).'courses/'.
api_get_course_id().'/index.php?id_session='.api_get_session_id()
);
exit;
}