Exercise: fix code from 1.11.x

pull/3706/head
Julio Montoya 5 years ago
parent 45c9ff3977
commit 11526bb679
  1. 8
      public/main/exercise/TestCategory.php
  2. 6
      public/main/exercise/admin.php
  3. 10
      public/main/exercise/exercise.class.php
  4. 6
      public/main/exercise/exercise_admin.php
  5. 1
      public/main/exercise/exercise_report.php
  6. 2
      public/main/exercise/exercise_result.php
  7. 2
      public/main/exercise/exercise_show.php
  8. 51
      public/main/exercise/exercise_submit_modal.php
  9. 4
      public/main/exercise/export/aiken/aiken_import.inc.php
  10. 4
      public/main/exercise/export/exercise_import.inc.php
  11. 4
      public/main/exercise/export/qti2/qti2_export.php
  12. 25
      public/main/exercise/fill_blanks.class.php
  13. 52
      public/main/exercise/hotspot_answers.as.php
  14. 2
      public/main/exercise/hotspot_savescore.inc.php
  15. 2
      public/main/exercise/hotspot_updatescore.inc.php
  16. 4
      public/main/exercise/live_stats.php
  17. 2
      public/main/exercise/multiple_answer.class.php
  18. 2
      public/main/exercise/multiple_answer_combination.class.php
  19. 24
      public/main/exercise/multiple_answer_true_false.class.php
  20. 26
      public/main/exercise/overview.php
  21. 33
      public/main/exercise/question.class.php
  22. 8
      public/main/exercise/question_admin.inc.php
  23. 2
      public/main/exercise/question_create.php
  24. 10
      public/main/exercise/question_list_admin.inc.php
  25. 6
      public/main/exercise/question_pool.php
  26. 71
      public/main/exercise/result.php
  27. 30
      public/main/exercise/stats.php
  28. 1
      public/main/exercise/tests_category.php
  29. 2
      public/main/exercise/unique_answer.class.php
  30. 2
      public/main/exercise/unique_answer_no_option.class.php
  31. 12
      public/main/exercise/upload_exercise.php
  32. 6
      public/main/inc/ajax/exercise.ajax.php
  33. 1
      public/main/inc/lib/api.lib.php
  34. 655
      public/main/inc/lib/events.lib.php
  35. 771
      public/main/inc/lib/exercise.lib.php
  36. 79
      public/main/inc/lib/exercise_show_functions.lib.php
  37. 19
      src/CoreBundle/Entity/TrackEAttempt.php
  38. 19
      src/CoreBundle/Entity/TrackEAttemptRecording.php
  39. 4
      src/CoreBundle/Migrations/Schema/V200/Version20180904175500.php

@ -205,13 +205,13 @@ class TestCategory
$table = Database::get_course_table(TABLE_QUIZ_QUESTION_CATEGORY);
$categories = [];
if (empty($field)) {
$sql = "SELECT id FROM $table
$sql = "SELECT iid FROM $table
WHERE c_id = $courseId
ORDER BY title ASC";
$res = Database::query($sql);
while ($row = Database::fetch_array($res)) {
$category = new TestCategory();
$categories[] = $category->getCategory($row['id'], $courseId);
$categories[] = $category->getCategory($row['iid'], $courseId);
}
} else {
$field = Database::escape_string($field);
@ -265,7 +265,7 @@ class TestCategory
$table = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY);
$sql = "SELECT *
FROM $table
WHERE question_id = $questionId AND c_id = $courseId";
WHERE question_id = $questionId";
$res = Database::query($sql);
if (Database::num_rows($res) > 0) {
return Database::fetch_array($res, 'ASSOC');
@ -294,7 +294,7 @@ class TestCategory
$table = Database::get_course_table(TABLE_QUIZ_QUESTION_CATEGORY);
$sql = "SELECT title
FROM $table
WHERE id = $categoryId AND c_id = $courseId";
WHERE iid = $categoryId AND c_id = $courseId";
$res = Database::query($sql);
$data = Database::fetch_array($res);
$result = '';

@ -147,7 +147,7 @@ if ($objExercise->sessionId != $sessionId) {
// doesn't select the exercise ID if we come from the question pool
if (!$fromExercise) {
// gets the right exercise ID, and if 0 creates a new exercise
if (!$exerciseId = $objExercise->selectId()) {
if (!$exerciseId = $objExercise->getId()) {
$modifyExercise = 'yes';
}
}
@ -171,7 +171,7 @@ if ($editQuestion || $newQuestion || $modifyQuestion || $modifyAnswers) {
// checks if the object exists
if (is_object($objQuestion)) {
// gets the question ID
$questionId = $objQuestion->selectId();
$questionId = $objQuestion->getId();
}
}
@ -436,7 +436,7 @@ if ($newQuestion || $editQuestion) {
echo '</div>';
} else {
require 'question_admin.inc.php';
ExerciseLib::showTestsWhereQuestionIsUsed($objQuestion->iid, $objExercise->selectId());
ExerciseLib::showTestsWhereQuestionIsUsed($objQuestion->iid, $objExercise->getId());
}
}
}

@ -1227,10 +1227,10 @@ class Exercise
$table = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
$tableQuestion = Database::get_course_table(TABLE_QUIZ_QUESTION);
$sql = "SELECT q.id
$sql = "SELECT q.iid
FROM $table e
INNER JOIN $tableQuestion q
ON (e.question_id = q.id AND e.c_id = q.c_id)
ON (e.question_id = q.iid AND e.c_id = q.c_id)
WHERE
q.type = $type AND
e.c_id = {$this->course_id} AND
@ -3541,7 +3541,7 @@ class Exercise
$questionName = $objQuestionTmp->selectTitle();
$questionWeighting = $objQuestionTmp->selectWeighting();
$answerType = $objQuestionTmp->selectType();
$quesId = $objQuestionTmp->selectId();
$quesId = $objQuestionTmp->getId();
$extra = $objQuestionTmp->extra;
$next = 1; //not for now
$totalWeighting = 0;
@ -5682,8 +5682,8 @@ class Exercise
$message .= '<p>'.$comment.'</p>';
echo $message;
$_SESSION['hotspot_delineation_result'][$this->selectId()][$questionId][0] = $message;
$_SESSION['hotspot_delineation_result'][$this->selectId()][$questionId][1] = $_SESSION['exerciseResultCoordinates'][$questionId];
$_SESSION['hotspot_delineation_result'][$this->getId()][$questionId][0] = $message;
$_SESSION['hotspot_delineation_result'][$this->selgetIdectId()][$questionId][1] = $_SESSION['exerciseResultCoordinates'][$questionId];
} else {
echo $hotspot_delineation_result[0];
}

@ -131,7 +131,7 @@ if (!empty($exerciseId)) {
$form = new FormValidator(
'exercise_admin',
'post',
api_get_self().'?'.api_get_cidreq().'&id='.$exerciseId
api_get_self().'?'.api_get_cidreq().'&exerciseId='.$exerciseId
);
$objExercise->read($exerciseId, false);
$form->addElement('hidden', 'edit', 'true');
@ -160,7 +160,7 @@ if ($form->validate()) {
}
$exercise_id = $objExercise->getId();
Session::erase('objExercise');
header('Location:admin.php?id='.$exercise_id.'&'.api_get_cidreq());
header('Location:admin.php?exerciseId='.$exercise_id.'&'.api_get_cidreq());
exit;
} else {
if (api_is_in_gradebook()) {
@ -175,7 +175,7 @@ if ($form->validate()) {
'name' => get_lang('Tests'),
];
$interbreadcrumb[] = [
'url' => 'admin.php?id='.$objExercise->getId().'&'.api_get_cidreq(),
'url' => 'admin.php?exerciseId='.$objExercise->getId().'&'.api_get_cidreq(),
'name' => $objExercise->selectTitle(true),
];

@ -2,7 +2,6 @@
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Entity\TrackEAttemptRecording;
/**

@ -110,7 +110,7 @@ if (api_is_course_admin() && !in_array($origin, ['learnpath', 'embeddable'])) {
]
);
}
$exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id($exe_id);
$exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId);
$learnpath_id = isset($exercise_stat_info['orig_lp_id']) ? $exercise_stat_info['orig_lp_id'] : 0;
$learnpath_item_id = isset($exercise_stat_info['orig_lp_item_id']) ? $exercise_stat_info['orig_lp_item_id'] : 0;
$learnpath_item_view_id = isset($exercise_stat_info['orig_lp_item_view_id'])

@ -881,7 +881,7 @@ if (MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY != $answerType) {
$pluginEvaluation = QuestionOptionsEvaluationPlugin::create();
if ('true' === $pluginEvaluation->get(QuestionOptionsEvaluationPlugin::SETTING_ENABLE)) {
$formula = $pluginEvaluation->getFormulaForExercise($objExercise->selectId());
$formula = $pluginEvaluation->getFormulaForExercise($objExercise->getId());
if (!empty($formula)) {
$totalScore = $pluginEvaluation->getResultWithFormula($id, $formula);

@ -8,6 +8,7 @@ use ChamiloSession as Session;
* @author Julio Montoya <gugli100@gmail.com>
*/
require_once __DIR__.'/../inc/global.inc.php';
$current_course_tool = TOOL_QUIZ;
api_protect_course_script();
@ -36,7 +37,32 @@ $questionNum = (int) $_GET['num'];
$questionId = $questionList[$questionNum];
$choiceValue = isset($_GET['choice']) ? $_GET['choice'] : '';
$hotSpot = isset($_GET['hotspot']) ? $_GET['hotspot'] : '';
$tryAgain = isset($_GET['tryagain']) && 1 === (int) $_GET['tryagain'];
$allowTryAgain = false;
if ($tryAgain) {
// Check if try again exists in this question, otherwise only allow one attempt BT#15827.
$objQuestionTmp = Question::read($questionId);
$answerType = $objQuestionTmp->selectType();
$showResult = false;
$objAnswerTmp = new Answer($questionId, api_get_course_int_id());
$answers = $objAnswerTmp->getAnswers();
if (!empty($answers)) {
foreach ($answers as $answerData) {
if (isset($answerData['destination'])) {
$itemList = explode('@@', $answerData['destination']);
if (isset($itemList[0]) && !empty($itemList[0])) {
$allowTryAgain = true;
break;
}
}
}
}
}
$loaded = isset($_GET['loaded']);
if ($allowTryAgain) {
unset($exerciseResult[$questionId]);
}
if (empty($choiceValue) && isset($exerciseResult[$questionId])) {
$choiceValue = $exerciseResult[$questionId];
@ -54,6 +80,15 @@ if (!empty($choiceValue)) {
}
}
$header = '';
$exeId = 0;
if ($objExercise->getFeedbackType() === EXERCISE_FEEDBACK_TYPE_POPUP) {
$exeId = Session::read('exe_id');
$header = '
<div class="modal-header">
<h4 class="modal-title" id="global-modal-title">'.get_lang('Incorrect').'</h4>
</div>';
}
echo '<script>
function tryAgain() {
$(function () {
@ -63,7 +98,7 @@ function tryAgain() {
function SendEx(num) {
if (num == -1) {
window.location.href = "exercise_result.php?'.api_get_cidreq().'&take_session=1&exerciseId='.$exerciseId.'&num="+num+"&learnpath_item_id='.$learnpath_item_id.'&learnpath_id='.$learnpath_id.'";
window.location.href = "exercise_result.php?'.api_get_cidreq().'&exe_id='.$exeId.'&take_session=1&exerciseId='.$exerciseId.'&num="+num+"&learnpath_item_id='.$learnpath_item_id.'&learnpath_id='.$learnpath_id.'";
} else {
num -= 1;
window.location.href = "exercise_submit.php?'.api_get_cidreq().'&tryagain=1&exerciseId='.$exerciseId.'&num="+num+"&learnpath_item_id='.$learnpath_item_id.'&learnpath_id='.$learnpath_id.'";
@ -72,13 +107,6 @@ function SendEx(num) {
}
</script>';
$header = '';
if (EXERCISE_FEEDBACK_TYPE_POPUP === $objExercise->getFeedbackType()) {
$header = '
<div class="modal-header">
<h4 class="modal-title" id="global-modal-title">'.get_lang('Incorrect').'</h4>
</div>';
}
echo '<div id="delineation-container">';
// Getting the options by js
@ -169,6 +197,7 @@ $choice[$questionId] = isset($choiceValue) ? $choiceValue : null;
if (!is_array($exerciseResult)) {
$exerciseResult = [];
}
$saveResults = (int) $objExercise->getFeedbackType() == EXERCISE_FEEDBACK_TYPE_POPUP;
// if the user has answered at least one question
if (is_array($choice)) {
@ -232,12 +261,12 @@ switch ($answerType) {
ob_start();
$result = $objExercise->manage_answer(
0,
$exeId,
$questionId,
$choiceValue,
'exercise_result',
null,
false,
[],
$saveResults,
false,
$showResult,
null,

@ -177,7 +177,7 @@ function aiken_import_exercise($file)
$exercise = new Exercise();
$exercise->exercise = $exercise_info['name'];
$exercise->save();
$last_exercise_id = $exercise->selectId();
$last_exercise_id = $exercise->getId();
$tableQuestion = Database::get_course_table(TABLE_QUIZ_QUESTION);
$tableAnswer = Database::get_course_table(TABLE_QUIZ_ANSWER);
if (!empty($last_exercise_id)) {
@ -196,7 +196,7 @@ function aiken_import_exercise($file)
$type = $question->selectType();
$question->type = constant($type);
$question->save($exercise);
$last_question_id = $question->selectId();
$last_question_id = $question->getId();
// 3. Create answer
$answer = new Answer($last_question_id, $courseId, $exercise, false);

@ -162,7 +162,7 @@ function import_exercise($file)
}
$exercise->save();
$last_exercise_id = $exercise->selectId();
$last_exercise_id = $exercise->getId();
$courseId = api_get_course_int_id();
if (!empty($last_exercise_id)) {
// For each question found...
@ -210,7 +210,7 @@ function import_exercise($file)
$question->updateDescription($description);
$question->save($exercise);
$last_question_id = $question->selectId();
$last_question_id = $question->getId();
//3. Create answer
$answer = new Answer($last_question_id);
$answerList = $question_array['answer'];

@ -169,7 +169,7 @@ class ImsSection
public function start_section()
{
return '<section
ident = "EXO_'.$this->exercise->selectId().'"
ident = "EXO_'.$this->exercise->getId().'"
title = "'.cleanAttribute(formatExerciseQtiDescription($this->exercise->selectTitle())).'"
>'."\n";
}
@ -317,7 +317,7 @@ class ImsItem
{
$this->question = $question;
$this->answer = $question->answer;
$this->questionIdent = 'QST_'.$question->selectId();
$this->questionIdent = 'QST_'.$question->getId();
}
/**

@ -250,6 +250,10 @@ class FillBlanks extends Question
function changeBlankSeparator()
{
var definedSeparator = $("[name=select_separator] option:selected").text();
$("[name=select_separator] option").each(function (index, value) {
$("#defineoneblank").html($("#defineoneblank").html().replace($(value).html(), definedSeparator))
});
var separatorNumber = $("#select_separator").val();
var tabSeparator = getSeparatorFromNumber(separatorNumber);
blankSeparatorStart = tabSeparator[0];
@ -1127,18 +1131,6 @@ class FillBlanks extends Question
$result = '';
$listStudentAnswerInfo = self::getAnswerInfo($answer, true);
/*if (in_array($resultsDisabled, [
RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT,
RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK,
]
)
) {
$resultsDisabled = true;
if ($showTotalScoreAndUserChoices) {
$resultsDisabled = false;
}
}*/
// rebuild the answer with good HTML style
// this is the student answer, right or wrong
for ($i = 0; $i < count($listStudentAnswerInfo['student_answer']); $i++) {
@ -1163,6 +1155,11 @@ class FillBlanks extends Question
// rebuild the sentence with student answer inserted
for ($i = 0; $i < count($listStudentAnswerInfo['common_words']); $i++) {
if ($resultsDisabled == RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK) {
if (empty($listStudentAnswerInfo['student_answer'][$i])) {
continue;
}
}
$result .= isset($listStudentAnswerInfo['common_words'][$i]) ? $listStudentAnswerInfo['common_words'][$i] : '';
$studentLabel = isset($listStudentAnswerInfo['student_answer'][$i]) ? $listStudentAnswerInfo['student_answer'][$i] : '';
$result .= $studentLabel;
@ -1209,6 +1206,7 @@ class FillBlanks extends Question
break;
case RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK:
case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK:
case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT:
$hideExpectedAnswer = true;
if ($showTotalScoreAndUserChoices) {
@ -1318,6 +1316,9 @@ class FillBlanks extends Question
$resultsDisabled = false,
$showTotalScoreAndUserChoices = false
) {
if ($resultsDisabled == RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK) {
return '';
}
return self::getHtmlAnswer(
$answer,
$correct,

@ -24,6 +24,9 @@ $userId = api_get_user_id();
$courseId = api_get_course_int_id();
$objExercise = new Exercise($courseId);
$debug = false;
if ($debug) {
error_log("Call to hotspot_answers.as.php");
}
$trackExerciseInfo = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId);
// Check if student has access to the hotspot answers
@ -94,10 +97,12 @@ $data['image_height'] = $pictureHeight;
$data['courseCode'] = $_course['path'];
$data['hotspots'] = [];
$resultDisable = $objExercise->selectResultsDisabled();
$showTotalScoreAndUserChoicesInLastAttempt = true;
if (in_array(
$objExercise->selectResultsDisabled(), [
$resultDisable, [
RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT,
RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK,
RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK,
]
)
@ -128,14 +133,14 @@ if (in_array(
}
$hideExpectedAnswer = false;
if (0 == $objExercise->getFeedbackType() &&
RESULT_DISABLE_SHOW_SCORE_ONLY == $objExercise->selectResultsDisabled()
if ($objExercise->getFeedbackType() == 0 &&
$resultDisable == RESULT_DISABLE_SHOW_SCORE_ONLY
) {
$hideExpectedAnswer = true;
}
if (in_array(
$objExercise->selectResultsDisabled(), [
$resultDisable, [
RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT,
RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK,
]
@ -144,6 +149,36 @@ if (in_array(
$hideExpectedAnswer = $showTotalScoreAndUserChoicesInLastAttempt ? false : true;
}
if (in_array(
$resultDisable, [
RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK,
]
)
) {
$hideExpectedAnswer = false;
}
$hotSpotWithAnswer = [];
$data['answers'] = [];
$rs = $em
->getRepository('ChamiloCoreBundle:TrackEHotspot')
->findBy(
[
'hotspotQuestionId' => $questionId,
'cId' => $courseId,
'hotspotExeId' => $exeId,
],
['hotspotAnswerId' => 'ASC']
);
/** @var TrackEHotspot $row */
foreach ($rs as $row) {
$data['answers'][] = $row->getHotspotCoordinate();
if ($row->getHotspotCorrect()) {
$hotSpotWithAnswer[] = $row->getHotspotAnswerId();
}
}
if (!$hideExpectedAnswer) {
$qb = $em->createQueryBuilder();
$qb
@ -165,8 +200,15 @@ if (!$hideExpectedAnswer) {
$result = $qb->getQuery()->getResult();
/** @var CQuizAnswer $hotSpotAnswer */
foreach ($result as $hotSpotAnswer) {
if (RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK == $resultDisable) {
if (false === $showTotalScoreAndUserChoicesInLastAttempt) {
if (!in_array($hotSpotAnswer->getIid(), $hotSpotWithAnswer)) {
continue;
}
}
}
/** @var CQuizAnswer $hotSpotAnswer */
$hotSpot = [];
$hotSpot['id'] = $hotSpotAnswer->getIid();
$hotSpot['answer'] = $hotSpotAnswer->getAnswer();

@ -19,7 +19,7 @@ $courseCode = $_GET['coursecode'];
$questionId = $_GET['questionId'];
$coordinates = $_GET['coord'];
$objExercise = Session::read('objExercise');
$exerciseId = $objExercise->selectId();
$exerciseId = $objExercise->getId();
// Save clicking order
$answerOrderId = count($_SESSION['exerciseResult'][$questionId]['ids']) + 1;
if ('0' == $_GET['answerId']) {

@ -20,7 +20,7 @@ $questionId = $_GET['questionId'];
$coordinates = $_GET['coord'];
$objExercise = Session::read('objExercise');
$hotspotId = $_GET['hotspotId'];
$exerciseId = $objExercise->selectId();
$exerciseId = $objExercise->getId();
if ('0' == $_GET['answerId']) { // click is NOT on a hotspot
$hit = 0;
$answerId = $hotspotId;

@ -24,7 +24,7 @@ $interbreadcrumb[] = [
'name' => get_lang('Tests'),
];
$interbreadcrumb[] = [
'url' => "admin.php?id=$exercise_id&".api_get_cidreq(),
'url' => "admin.php?exerciseId=$exercise_id&".api_get_cidreq(),
'name' => $objExercise->selectTitle(true),
];
@ -108,7 +108,7 @@ $(function() {
</script>
<?php
$actions = '<a href="exercise_report.php?id='.(int) ($_GET['exerciseId']).'&'.api_get_cidreq().'">'.
$actions = '<a href="exercise_report.php?exerciseId='.(int) ($_GET['exerciseId']).'&'.api_get_cidreq().'">'.
Display::return_icon('back.png', get_lang('Go back to the questions list'), '', ICON_SIZE_MEDIUM).'</a>';
echo $actions = Display::div($actions, ['class' => 'actions']);

@ -235,7 +235,9 @@ class MultipleAnswer extends Question
$header .= '<th>'.get_lang('Status').'</th>';
}
if (false === $exercise->hideComment) {
$header .= '<th>'.get_lang('Comment').'</th>';
}
$header .= '</tr>';
return $header;

@ -236,7 +236,9 @@ class MultipleAnswerCombination extends Question
if ($exercise->showExpectedChoice()) {
$header .= '<th>'.get_lang('Status').'</th>';
}
if (false === $exercise->hideComment) {
$header .= '<th>'.get_lang('Comment').'</th>';
}
$header .= '</tr>';
return $header;

@ -117,7 +117,11 @@ class MultipleAnswerTrueFalse extends Question
$j = 1;
if (!empty($optionData)) {
foreach ($optionData as $id => $data) {
$form->addElement('radio', 'correct['.$i.']', null, null, $id);
$rdoCorrect = $form->addElement('radio', 'correct['.$i.']', null, null, $id);
if (isset($_POST['correct']) && isset($_POST['correct'][$i]) && $id == $_POST['correct'][$i]) {
$rdoCorrect->setValue(Security::remove_XSS($_POST['correct'][$i]));
}
$j++;
if (3 == $j) {
break;
@ -141,6 +145,9 @@ class MultipleAnswerTrueFalse extends Question
['ToolbarSet' => 'TestProposedAnswer', 'Width' => '100%', 'Height' => '100']
);
if (isset($_POST['answer']) && isset($_POST['answer'][$i])) {
$form->getElement("answer[$i]")->setValue(Security::remove_XSS($_POST['answer'][$i]));
}
// show comment when feedback is enable
if (EXERCISE_FEEDBACK_TYPE_EXAM != $obj_ex->getFeedbackType()) {
$form->addElement(
@ -154,6 +161,9 @@ class MultipleAnswerTrueFalse extends Question
'Height' => '100',
]
);
if (isset($_POST['comment']) && isset($_POST['comment'][$i])) {
$txtComment->setValue(Security::remove_XSS($_POST['comment'][$i]));
}
}
$form->addHtml('</tr>');
@ -206,9 +216,9 @@ class MultipleAnswerTrueFalse extends Question
$scores = explode(':', $this->extra);
if (!empty($scores)) {
for ($i = 1; $i <= 3; $i++) {
$defaults['option['.$i.']'] = $scores[$i - 1];
}
$txtOption1->setValue($scores[0]);
$txtOption2->setValue($scores[1]);
$txtOption3->setValue($scores[2]);
}
}
@ -224,9 +234,7 @@ class MultipleAnswerTrueFalse extends Question
$form->addGroup($buttonGroup);
}
if (!empty($this->id)) {
$form->setDefaults($defaults);
} else {
if (!empty($this->id) && !$form->isSubmitted()) {
$form->setDefaults($defaults);
}
$form->setConstants(['nb_answers' => $nb_answers]);
@ -327,8 +335,10 @@ class MultipleAnswerTrueFalse extends Question
]
)
) {
if (false === $exercise->hideComment) {
$header .= '<th>'.get_lang('Comment').'</th>';
}
}
$header .= '</tr>';
return $header;

@ -10,7 +10,9 @@
require_once __DIR__.'/../inc/global.inc.php';
// Clear the exercise session just in case
$current_course_tool = TOOL_QUIZ;
Exercise::cleanSessionVariables();
$this_section = SECTION_COURSES;
$js = '<script>'.api_get_language_translate_html().'</script>';
$htmlHeadXtra[] = $js;
@ -19,7 +21,7 @@ $htmlHeadXtra[] = $js;
api_protect_course_script(true);
$sessionId = api_get_session_id();
$courseCode = api_get_course_id();
$exercise_id = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : 0;
$exercise_id = isset($_REQUEST['exerciseId']) ? (int) $_REQUEST['exerciseId'] : 0;
$objExercise = new Exercise();
$result = $objExercise->read($exercise_id, true);
@ -28,15 +30,21 @@ if (!$result) {
api_not_allowed(true);
}
$learnpath_id = isset($_REQUEST['learnpath_id']) ? (int) ($_REQUEST['learnpath_id']) : null;
$learnpath_item_id = isset($_REQUEST['learnpath_item_id']) ? (int) ($_REQUEST['learnpath_item_id']) : null;
$learnpathItemViewId = isset($_REQUEST['learnpath_item_view_id']) ? (int) ($_REQUEST['learnpath_item_view_id']) : null;
if ('true' === api_get_plugin_setting('positioning', 'tool_enable')) {
$plugin = Positioning::create();
if ($plugin->blockFinalExercise(api_get_user_id(), $exercise_id, api_get_course_int_id(), $sessionId)) {
api_not_allowed(true);
}
}
$learnpath_id = isset($_REQUEST['learnpath_id']) ? (int) $_REQUEST['learnpath_id'] : null;
$learnpath_item_id = isset($_REQUEST['learnpath_item_id']) ? (int) $_REQUEST['learnpath_item_id'] : null;
$learnpathItemViewId = isset($_REQUEST['learnpath_item_view_id']) ? (int) $_REQUEST['learnpath_item_view_id'] : null;
$origin = api_get_origin();
$logInfo = [
'tool' => TOOL_QUIZ,
'tool_id' => $exercise_id,
'tool_id_detail' => 0,
'action' => isset($_REQUEST['learnpath_id']) ? 'learnpath_id' : '',
'action_details' => isset($_REQUEST['learnpath_id']) ? (int) $_REQUEST['learnpath_id'] : '',
];
@ -96,12 +104,12 @@ if ($is_allowed_to_edit) {
if ($objExercise->sessionId == $sessionId) {
$editLink = Display::url(
Display::return_icon('edit.png', get_lang('Edit'), [], ICON_SIZE_SMALL),
api_get_path(WEB_CODE_PATH).'exercise/admin.php?'.api_get_cidreq().'&id='.$objExercise->id
api_get_path(WEB_CODE_PATH).'exercise/admin.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id
);
}
$editLink .= Display::url(
Display::return_icon('test_results.png', get_lang('Results and feedback and feedback'), [], ICON_SIZE_SMALL),
api_get_path(WEB_CODE_PATH).'exercise/exercise_report.php?'.api_get_cidreq().'&id='.$objExercise->id,
api_get_path(WEB_CODE_PATH).'exercise/exercise_report.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id,
['title' => get_lang('Results and feedback and feedback')]
);
}
@ -218,6 +226,7 @@ if (in_array(
$objExercise->results_disabled,
[
RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT,
RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK,
RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK,
])
) {
@ -280,6 +289,7 @@ if (!empty($attempts)) {
RESULT_DISABLE_SHOW_SCORE_ONLY,
RESULT_DISABLE_SHOW_FINAL_SCORE_ONLY_WITH_CATEGORIES,
RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT,
RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK,
RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK,
RESULT_DISABLE_RANKING,
RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER,
@ -295,6 +305,7 @@ if (!empty($attempts)) {
RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING,
RESULT_DISABLE_SHOW_FINAL_SCORE_ONLY_WITH_CATEGORIES,
RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT,
RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK,
RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK,
RESULT_DISABLE_RANKING,
RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER,
@ -347,6 +358,7 @@ if (!empty($attempts)) {
}
break;
case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK:
case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT:
if ($blockShowAnswers) {
$header_names = [get_lang('Attempt'), get_lang('Start Date'), get_lang('IP'), get_lang('Score')];

@ -109,6 +109,11 @@ abstract class Question
];
}
public function getId()
{
return $this->iid;
}
/**
* @return int|null
*/
@ -214,18 +219,6 @@ abstract class Question
return false;
}
/**
* returns the question ID.
*
* @author Olivier Brouckaert
*
* @return int - question ID
*/
public function selectId()
{
return $this->id;
}
/**
* returns the question title.
*
@ -395,9 +388,15 @@ abstract class Question
c_id = ".$courseId;
$res = Database::query($sql);
$row = Database::fetch_array($res);
$allowMandatory = api_get_configuration_value('allow_mandatory_question_in_category');
if ($row['nb'] > 0) {
$extraMandatoryCondition = '';
if ($allowMandatory) {
$extraMandatoryCondition = ", mandatory = {$this->mandatory}";
}
$sql = "UPDATE $table
SET category_id = $categoryId
$extraMandatoryCondition
WHERE
question_id = $question_id ";
Database::query($sql);
@ -405,6 +404,14 @@ abstract class Question
$sql = "INSERT INTO $table (question_id, category_id)
VALUES ($question_id, $categoryId)";
Database::query($sql);
if ($allowMandatory) {
$id = Database::insert_id();
if ($id) {
$sql = "UPDATE $table SET mandatory = {$this->mandatory}
WHERE iid = $id";
Database::query($sql);
}
}
}
return true;
@ -1537,7 +1544,7 @@ abstract class Question
$explanation = $type->getExplanation();
echo '<li>';
echo '<div class="icon-image">';
$icon = '<a href="admin.php?'.api_get_cidreq().'&newQuestion=yes&answerType='.$i.'&id='.$exerciseId.'">'.
$icon = '<a href="admin.php?'.api_get_cidreq().'&newQuestion=yes&answerType='.$i.'&exerciseId='.$exerciseId.'">'.
Display::return_icon($img, $explanation, null, ICON_SIZE_BIG).'</a>';
if (false === $objExercise->force_edit_exercise_in_lp) {

@ -61,13 +61,13 @@ if (is_object($objQuestion)) {
if (isset($_GET['editQuestion'])) {
if (empty($exerciseId)) {
Display::addFlash(Display::return_message(get_lang('Item updated')));
$url = api_get_path(WEB_CODE_PATH).'exercise/admin.php?id='.$exerciseId.'&'.api_get_cidreq().'&editQuestion='.$objQuestion->id;
$url = api_get_path(WEB_CODE_PATH).'exercise/admin.php?exerciseId='.$exerciseId.'&'.api_get_cidreq().'&editQuestion='.$objQuestion->id;
header("Location: $url");
exit;
}
Display::addFlash(Display::return_message(get_lang('Item updated')));
$url = api_get_path(WEB_CODE_PATH).'exercise/admin.php?id='.$exerciseId.'&'.api_get_cidreq().'&page='.$page;
$url = api_get_path(WEB_CODE_PATH).'exercise/admin.php?exerciseId='.$exerciseId.'&'.api_get_cidreq().'&page='.$page;
header("Location: $url");
exit;
} else {
@ -78,12 +78,12 @@ if (is_object($objQuestion)) {
$page = round($objExercise->getQuestionCount() / $length);
}
Display::addFlash(Display::return_message(get_lang('Item added')));
$url = api_get_path(WEB_CODE_PATH).'exercise/admin.php?id='.$exerciseId.'&'.api_get_cidreq().'&page='.$page;
$url = api_get_path(WEB_CODE_PATH).'exercise/admin.php?exerciseId='.$exerciseId.'&'.api_get_cidreq().'&page='.$page;
header("Location: $url");
exit;
}
} else {
echo '<script type="text/javascript">window.location.href="admin.php?id='.$exerciseId.'&page='.$page.'&hotspotadmin='.$objQuestion->id.'&'.api_get_cidreq().'"</script>';
echo '<script type="text/javascript">window.location.href="admin.php?exerciseId='.$exerciseId.'&page='.$page.'&hotspotadmin='.$objQuestion->id.'&'.api_get_cidreq().'"</script>';
}
} else {
if (isset($questionName)) {

@ -94,7 +94,7 @@ if ($form->validate()) {
header('Location: question_create.php?'.api_get_cidreq().'&error=true');
exit;
}
header('Location: admin.php?id='.$values['exercise'].'&newQuestion=yes&isContent='.$values['is_content'].'&answerType='.$answer_type);
header('Location: admin.php?exerciseId='.$values['exercise'].'&newQuestion=yes&isContent='.$values['is_content'].'&answerType='.$answer_type);
exit;
} else {
// header

@ -161,6 +161,7 @@ if (!$inATest) {
// In the building exercise mode show question list ordered as is.
$objExercise->setCategoriesGrouping(false);
$originalQuestionSelectType = $objExercise->questionSelectionType;
// In building mode show all questions not render by teacher order.
$objExercise->questionSelectionType = EX_Q_SELECTION_ORDERED;
$allowQuestionOrdering = true;
@ -195,6 +196,7 @@ if (!$inATest) {
// Classic order
$questionList = $objExercise->selectQuestionList(true, true);
}
$objExercise->questionSelectionType = $originalQuestionSelectType;
echo '
<div class="row hidden-xs">
@ -285,7 +287,7 @@ if (!$inATest) {
$delete_link = '';
}
$btnDetail = implode(
$btnActions = implode(
PHP_EOL,
[$edit_link, $clone_link, $delete_link]
);
@ -310,7 +312,7 @@ if (!$inATest) {
// Question category
$questionCategory = Security::remove_XSS(
TestCategory::getCategoryNameForQuestion($objQuestionTmp->id)
TestCategory::getCategoryNameForQuestion($objQuestionTmp->getId())
);
if (empty($questionCategory)) {
$questionCategory = '-';
@ -327,7 +329,7 @@ if (!$inATest) {
$questionScore = $objQuestionTmp->selectWeighting();
echo '<div id="question_id_list_'.$id.'">
<div class="header_operations" data-exercise="'.$objExercise->selectId().'"
<div class="header_operations" data-exercise="'.$objExercise->getId().'"
data-question="'.$id.'">
<div class="row">
<div class="question col-sm-5 col-xs-12">'
@ -350,7 +352,7 @@ if (!$inATest) {
.$questionScore.'
</div>
<div class="btn-actions text-right col-sm-2 col-xs-6">
<div class="edition">'.$btnDetail.'</div>
<div class="edition">'.$btnActions.'</div>
</div>
</div>
</div>

@ -55,7 +55,7 @@ $interbreadcrumb[] = ['url' => 'exercise.php?'.api_get_cidreq(), 'name' => get_l
if (!empty($objExercise->id)) {
$interbreadcrumb[] = [
'url' => 'admin.php?id='.$objExercise->id.'&'.api_get_cidreq(),
'url' => 'admin.php?exerciseId='.$objExercise->id.'&'.api_get_cidreq(),
'name' => $objExercise->selectTitle(true),
];
}
@ -305,7 +305,7 @@ if (isset($fromExercise) && $fromExercise > 0) {
} else {
echo '<a href="exercise.php?'.api_get_cidreq().'">'.
Display::return_icon('back.png', get_lang('BackToTestsList'), '', ICON_SIZE_MEDIUM).'</a>';
echo "<a href='admin.php?id=0'>".
echo "<a href='admin.php?exerciseId=0'>".
Display::return_icon('add_question.gif', get_lang('New question'), '', ICON_SIZE_MEDIUM).'</a>';
$titleAdd = get_lang('Manage all questions');
}
@ -1063,7 +1063,7 @@ echo '<input type="hidden" name="selected_course" value="'.$selected_course.'">'
echo '<input type="hidden" name="course_id" value="'.$selected_course.'">';
echo '<input type="hidden" name="action">';
$table = new HTML_Table(['class' => 'table table-bordered data_table'], false);
$table = new HTML_Table(['class' => 'table table-hover table-striped table-bordered data_table'], false);
$row = 0;
$column = 0;
foreach ($headers as $header) {

@ -10,15 +10,16 @@ use ChamiloSession as Session;
* @author Julio Montoya - Simple exercise result page
*/
require_once __DIR__.'/../inc/global.inc.php';
$current_course_tool = TOOL_QUIZ;
$id = isset($_REQUEST['id']) ? (int) $_GET['id'] : 0; // exe id
$show_headers = isset($_REQUEST['show_headers']) ? (int) $_REQUEST['show_headers'] : null;
$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
$origin = api_get_origin();
if (in_array($origin, ['learnpath', 'embeddable', 'mobileapp'])) {
$show_headers = false;
}
$is_courseTutor = api_is_course_tutor();
api_protect_course_script($show_headers);
@ -37,7 +38,7 @@ if (empty($track_exercise_info)) {
}
$exercise_id = $track_exercise_info['exe_exo_id'];
$student_id = $track_exercise_info['exe_user_id'];
$student_id = (int) $track_exercise_info['exe_user_id'];
$current_user_id = api_get_user_id();
$objExercise = new Exercise();
@ -56,6 +57,15 @@ if (!$is_allowedToEdit) {
}
}
$allowSignature = false;
if ($student_id === $current_user_id && ExerciseSignaturePlugin::exerciseHasSignatureActivated($objExercise)) {
// Check if signature exists.
$signature = ExerciseSignaturePlugin::getSignature($current_user_id, $track_exercise_info);
if (false === $signature) {
$allowSignature = true;
}
}
$htmlHeadXtra[] = '<link rel="stylesheet" href="'.api_get_path(WEB_LIBRARY_JS_PATH).'hotspot/css/hotspot.css">';
$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'hotspot/js/hotspot.js"></script>';
//$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'annotation/js/annotation.js"></script>';
@ -92,20 +102,73 @@ if ($show_headers) {
$message = Session::read('attempt_remaining');
Session::erase('attempt_remaining');
$allowExportPdf = api_get_configuration_value('quiz_results_answers_report');
ob_start();
ExerciseLib::displayQuestionListByAttempt(
$stats = ExerciseLib::displayQuestionListByAttempt(
$objExercise,
$id,
false,
$message
$message,
$allowSignature,
$allowExportPdf,
'export' === $action
);
$pageContent = ob_get_contents();
ob_end_clean();
switch ($action) {
case 'export':
if ($allowExportPdf) {
$allAnswers = $stats['all_answers_html'];
@$pdf = new PDF();
$cssFile = api_get_path(SYS_CSS_PATH).'themes/chamilo/default.css';
$title = get_lang('ResponseReport');
$exerciseTitle = $objExercise->get_formated_title();
$studentInfo = api_get_user_info($student_id);
$userHeader = $objExercise->showExerciseResultHeader(
$studentInfo,
$track_exercise_info,
false,
false,
false
);
$filename = get_lang('Exercise').'_'.$exerciseTitle;
$pdf->content_to_pdf("
<html><body>
<h2 style='text-align: center'>$title</h2>
$userHeader
$allAnswers
</body></html>",
file_get_contents($cssFile),
$filename,
api_get_course_id(),
'D',
false,
null,
false,
true
);
} else {
api_not_allowed(true);
}
exit;
break;
}
$pageBottom = '<div class="question-return">';
$pageBottom .= Display::url(
get_lang('BackToAttemptList'),
api_get_path(WEB_CODE_PATH).'exercise/overview.php?exerciseId='.$exercise_id.'&'.api_get_cidreq(),
['class' => 'btn btn-primary']
);
$pageBottom .= '</div>';
$pageContent .= $pageBottom;
$template = new Template('', $show_headers, $show_headers);
$template->assign('page_top', '');
$template->assign('page_bottom', '');
$template->assign('page_content', $pageContent);
$template->assign('allow_signature', $allowSignature);
$template->assign('exe_id', $id);
$layout = $template->fetch($template->get_template('exercise/result.tpl'));
$template->assign('content', $layout);
$template->display_one_col_template();

@ -4,6 +4,7 @@
require_once __DIR__.'/../inc/global.inc.php';
$this_section = SECTION_COURSES;
api_protect_course_script(true, false, true);
$showPage = false;
@ -98,7 +99,7 @@ if (!empty($question_list)) {
}
// Format A table
$table = new HTML_Table(['class' => 'data_table']);
$table = new HTML_Table(['class' => 'table table-hover table-striped data_table']);
$row = 0;
$column = 0;
foreach ($headers as $header) {
@ -136,7 +137,8 @@ if (!empty($question_list)) {
$question_id,
$exerciseId,
$courseCode,
$sessionId
$sessionId,
true
);
$answer = new Answer($question_id);
@ -285,7 +287,7 @@ if (!empty($question_list)) {
}
// Format A table
$table = new HTML_Table(['class' => 'data_table']);
$table = new HTML_Table(['class' => 'table table-hover table-striped data_table']);
$row = 0;
$column = 0;
foreach ($headers as $header) {
@ -304,18 +306,32 @@ foreach ($data as $row_table) {
$row++;
}
$content .= $table->toHtml();
$exportPdf = isset($_GET['export_pdf']) && !empty($_GET['export_pdf']) ? (int) $_GET['export_pdf'] : 0;
if ($exportPdf) {
$fileName = get_lang('Report').'_'.api_get_course_id().'_'.api_get_local_time();
$params = [
'filename' => $fileName,
'pdf_title' => $objExercise->selectTitle(true).'<br>'.get_lang('ReportByQuestion'),
'pdf_description' => get_lang('Report'),
'format' => 'A4',
'orientation' => 'P',
];
Export::export_html_to_pdf($content, $params);
exit;
}
$interbreadcrumb[] = [
'url' => 'exercise.php?'.api_get_cidreq(),
'name' => get_lang('Tests'),
];
$interbreadcrumb[] = [
'url' => "admin.php?id=$exerciseId&".api_get_cidreq(),
'url' => "admin.php?exerciseId=$exerciseId&".api_get_cidreq(),
'name' => $objExercise->selectTitle(true),
];
$tpl = new Template(get_lang('Report by question'));
$actions = '<a href="exercise_report.php?id='.$exerciseId.'&'.api_get_cidreq().'">'.
$actions = '<a href="exercise_report.php?exerciseId='.$exerciseId.'&'.api_get_cidreq().'">'.
Display:: return_icon(
'back.png',
get_lang('Go back to the questions list'),
@ -323,6 +339,10 @@ $actions = '<a href="exercise_report.php?id='.$exerciseId.'&'.api_get_cidreq().'
ICON_SIZE_MEDIUM
)
.'</a>';
$actions .= Display::url(
Display::return_icon('pdf.png', get_lang('ExportToPDF'), [], ICON_SIZE_MEDIUM),
'stats.php?exerciseId='.$exerciseId.'&export_pdf=1&'.api_get_cidreq()
);
$actions = Display::div($actions, ['class' => 'actions']);
$content = $actions.$content;
$tpl->assign('content', $content);

@ -24,6 +24,7 @@ $nameTools = '';
require_once __DIR__.'/../inc/global.inc.php';
$this_section = SECTION_COURSES;
api_protect_course_script(true);
if (!api_is_allowed_to_edit()) {

@ -452,7 +452,9 @@ class UniqueAnswer extends Question
if ($exercise->showExpectedChoice()) {
$header .= '<th>'.get_lang('Status').'</th>';
}
if (false === $exercise->hideComment) {
$header .= '<th>'.get_lang('Comment').'</th>';
}
$header .= '</tr>';
return $header;

@ -409,7 +409,9 @@ class UniqueAnswerNoOption extends Question
if ($exercise->showExpectedChoice()) {
$header .= '<th>'.get_lang('Status').'</th>';
}
if (false === $exercise->hideComment) {
$header .= '<th>'.get_lang('Comment').'</th>';
}
$header .= '</tr>';
return $header;

@ -7,16 +7,19 @@ use ChamiloSession as Session;
/**
* Upload quiz: This script shows the upload quiz feature.
*/
$help_content = 'exercise_upload';
require_once __DIR__.'/../inc/global.inc.php';
api_protect_course_script(true);
$is_allowed_to_edit = api_is_allowed_to_edit(null, true);
$debug = false;
$origin = api_get_origin();
if (!$is_allowed_to_edit) {
api_not_allowed(true);
}
$this_section = SECTION_COURSES;
$htmlHeadXtra[] = "<script>
$(function(){
$('#user_custom_score').click(function() {
@ -434,8 +437,9 @@ function lp_upload_quiz_action_handling()
$score,
$id
);
if ($correct) {
$total += (float) $score;
}
$id++;
}
@ -558,6 +562,7 @@ function lp_upload_quiz_action_handling()
}
}
}
Display::addFlash(Display::return_message(get_lang('FileImported')));
if (isset($_SESSION['oLP']) && isset($_GET['lp_id'])) {
$previous = $_SESSION['oLP']->select_previous_item_id();
@ -576,8 +581,9 @@ function lp_upload_quiz_action_handling()
exit;
} else {
// header('location: exercise.php?' . api_get_cidreq());
echo '<script>window.location.href = "'.api_get_path(WEB_CODE_PATH).'exercise/admin.php?'.api_get_cidreq().'&exerciseId='.$quiz_id.'&session_id='.api_get_session_id().'"</script>';
}
$exerciseUrl = api_get_path(WEB_CODE_PATH).
'exercise/admin.php?'.api_get_cidreq().'&exerciseId='.$quiz_id.'&session_id='.api_get_session_id();
api_location($exerciseUrl);
}
}

@ -584,9 +584,9 @@ switch ($action) {
// This variable came from exercise_submit_modal.php.
$hotspot_delineation_result = null;
if (isset($_SESSION['hotspot_delineation_result']) &&
isset($_SESSION['hotspot_delineation_result'][$objExercise->selectId()])
isset($_SESSION['hotspot_delineation_result'][$objExercise->getId()])
) {
$hotspot_delineation_result = $_SESSION['hotspot_delineation_result'][$objExercise->selectId()][$my_question_id];
$hotspot_delineation_result = $_SESSION['hotspot_delineation_result'][$objExercise->getId()][$my_question_id];
}
if ('simple' === $type) {
@ -710,7 +710,7 @@ switch ($action) {
Session::write('duration_time', [$key => $now]);
Event::updateEventExercise(
$exeId,
$objExercise->selectId(),
$objExercise->getId(),
$total_score,
$total_weight,
$session_id,

@ -474,6 +474,7 @@ define('RESULT_DISABLE_RANKING', 6);
define('RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER', 7);
define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING', 8);
define('RESULT_DISABLE_RADAR', 9);
define('RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK', 10);
define('EXERCISE_MAX_NAME_SIZE', 80);

@ -2,6 +2,7 @@
/* See license terms in /license.txt */
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
use Chamilo\CoreBundle\Entity\TrackEAttemptRecording;
use ChamiloSession as Session;
/**
@ -13,13 +14,13 @@ use ChamiloSession as Session;
class Event
{
/**
* @author Sebastien Piraux <piraux_seb@hotmail.com> old code
* @author Julio Montoya
*
* @param int $userId
*
* @return bool
* @desc Record information for login event when an user identifies himself with username & password
* @author Sebastien Piraux <piraux_seb@hotmail.com> old code
* @author Julio Montoya
*
*/
public static function eventLogin($userId)
{
@ -76,13 +77,13 @@ class Event
{
if (!empty($sessionId)) {
$visibility = api_get_session_visibility($sessionId);
if (!empty($visibility) && SESSION_AVAILABLE != $visibility) {
if (!empty($visibility) && $visibility != SESSION_AVAILABLE) {
$extraFieldValue = new ExtraFieldValue('session');
$value = $extraFieldValue->get_values_by_handler_and_field_variable(
$sessionId,
'disable_log_after_session_ends'
);
if (!empty($value) && isset($value['value']) && 1 == (int) $value['value']) {
if (!empty($value) && isset($value['value']) && (int) $value['value'] == 1) {
return false;
}
}
@ -199,9 +200,15 @@ class Event
//$pos = strpos($_SERVER['HTTP_REFERER'],$_configuration['root_web'].$_cid);
$coursePath = isset($courseInfo['path']) ? $courseInfo['path'] : null;
$pos = isset($_SERVER['HTTP_REFERER']) ? strpos(strtolower($_SERVER['HTTP_REFERER']), strtolower(api_get_path(WEB_COURSE_PATH).$coursePath)) : false;
$pos = isset($_SERVER['HTTP_REFERER']) ? strpos(
strtolower($_SERVER['HTTP_REFERER']),
strtolower(api_get_path(WEB_COURSE_PATH).$coursePath)
) : false;
// added for "what's new" notification
$pos2 = isset($_SERVER['HTTP_REFERER']) ? strpos(strtolower($_SERVER['HTTP_REFERER']), strtolower(api_get_path(WEB_PATH)."index")) : false;
$pos2 = isset($_SERVER['HTTP_REFERER']) ? strpos(
strtolower($_SERVER['HTTP_REFERER']),
strtolower(api_get_path(WEB_PATH)."index")
) : false;
// end "what's new" notification
if (false !== $pos || false !== $pos2) {
@ -265,23 +272,16 @@ class Event
$courseId = api_get_course_int_id();
$sessionId = api_get_session_id();
$sql = "INSERT INTO $table (
down_user_id,
c_id,
down_doc_path,
down_date,
down_session_id
)
VALUES (
$userId,
$courseId,
'$documentUrl',
'$reallyNow',
$sessionId
)";
Database::query($sql);
return 1;
return Database::insert(
$table,
[
'down_user_id' => $userId,
'c_id' => $courseId,
'down_doc_path' => $documentUrl,
'down_date' => $reallyNow,
'down_session_id' => $sessionId,
]
);
}
/**
@ -368,21 +368,26 @@ class Event
/**
* Update the TRACK_E_EXERCICES exercises.
* Record result of user when an exercise was done.
*
* @param int exeid id of the attempt
* @param int exo_id exercise id
* @param mixed result score
* @param int weighting ( higher score )
* @param int duration ( duration of the attempt in seconds )
* @param int session_id
* @param int learnpath_id (id of the learnpath)
* @param int learnpath_item_id (id of the learnpath_item)
* @param int $exeId
* @param int $exoId
* @param mixed $score
* @param int $weighting
* @param int $sessionId
* @param int $learnpathId
* @param int $learnpathItemId
* @param int $learnpathItemViewId
* @param int $duration
* @param array $questionsList
* @param string $status
* @param array $remindList
* @param null $endDate
*
* @return bool
*
* @author Sebastien Piraux <piraux_seb@hotmail.com>
* @author Julio Montoya Armas <gugli100@gmail.com> Reworked 2010
* @desc Record result of user when an exercise was done
*/
public static function updateEventExercise(
$exeId,
@ -403,14 +408,6 @@ class Event
return false;
}
/*
* Code commented due BT#8423 do not change the score to 0.
*
* Validation in case of fraud with actived control time
if (!ExerciseLib::exercise_time_control_is_valid($exo_id, $learnpath_id, $learnpath_item_id)) {
$score = 0;
}
*/
if (!isset($status) || empty($status)) {
$status = '';
} else {
@ -471,19 +468,20 @@ class Event
/**
* Record an event for this attempt at answering an exercise.
*
* @param float Score achieved
* @param string Answer given
* @param int Question ID
* @param int Exercise attempt ID a.k.a exe_id (from track_e_exercise)
* @param int Position
* @param int Exercise ID (from c_quiz)
* @param bool update results?
* @param $fileName string Filename (for audio answers - using nanogong)
* @param int User ID The user who's going to get this score. Default value of null means "get from context".
* @param int Course ID (from the "id" column of course table). Default value of null means "get from context".
* @param int Session ID (from the session table). Default value of null means "get from context".
* @param int Learnpath ID (from c_lp table). Default value of null means "get from context".
* @param int Learnpath item ID (from the c_lp_item table). Default value of null means "get from context".
* @param float $score Score achieved
* @param string $answer Answer given
* @param int $question_id
* @param int $exe_id Exercise attempt ID a.k.a exe_id (from track_e_exercise)
* @param int $position
* @param int $exercise_id From c_quiz
* @param bool $updateResults
* @param int $duration Time spent in seconds
* @param string $fileName Filename (for audio answers - using nanogong)
* @param int $user_id The user who's going to get this score.
* @param int $course_id Default value of null means "get from context".
* @param int $session_id Default value of null means "get from context".
* @param int $learnpath_id (from c_lp table). Default value of null means "get from context".
* @param int $learnpath_item_id (from the c_lp_item table). Default value of null means "get from context".
*
* @return bool Result of the insert query
*/
@ -495,6 +493,7 @@ class Event
$position,
$exercise_id = 0,
$updateResults = false,
$questionDuration = 0,
$fileName = null,
$user_id = null,
$course_id = null,
@ -503,12 +502,13 @@ class Event
$learnpath_item_id = null
) {
global $debug;
$question_id = Database::escape_string($question_id);
$exe_id = Database::escape_string($exe_id);
$position = Database::escape_string($position);
$now = api_get_utc_datetime();
$questionDuration = (int) $questionDuration;
$question_id = (int) $question_id;
$exe_id = (int) $exe_id;
$position = (int) $position;
$course_id = (int) $course_id;
$recording = true == api_get_configuration_value('quiz_answer_extra_recording');
$now = api_get_utc_datetime();
$recording = api_get_configuration_value('quiz_answer_extra_recording');
// check user_id or get from context
if (empty($user_id)) {
@ -539,7 +539,7 @@ class Event
$TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
if ($debug) {
error_log("----- entering saveQuestionAttempt() function ------");
error_log('----- entering saveQuestionAttempt() function ------');
error_log("answer: $answer");
error_log("score: $score");
error_log("question_id : $question_id");
@ -555,12 +555,15 @@ class Event
$answer = 0;
}
if (!empty($question_id) && !empty($exe_id) && !empty($user_id)) {
if (is_null($answer)) {
if (empty($question_id) || empty($exe_id) || empty($user_id)) {
return false;
}
if (null === $answer) {
$answer = '';
}
if (is_null($score)) {
if (null === $score) {
$score = 0;
}
@ -575,6 +578,7 @@ class Event
'tms' => $now,
'filename' => !empty($fileName) ? basename($fileName) : $fileName,
'teacher_comment' => '',
'seconds_spent' => $questionDuration,
];
// Check if attempt exists.
@ -587,11 +591,10 @@ class Event
question_id = $question_id AND
position = $position";
$result = Database::query($sql);
$attemptData = [];
if (Database::num_rows($result)) {
if ($debug) {
error_log("Attempt already exist: exe_id: $exe_id - user_id:$user_id - question_id:$question_id");
}
if (false == $updateResults) {
$attemptData = Database::fetch_array($result, 'ASSOC');
if ($updateResults == false) {
//The attempt already exist do not update use update_event_exercise() instead
return false;
}
@ -601,35 +604,32 @@ class Event
if ($debug) {
error_log("updateResults : $updateResults");
error_log("Saving question attempt: ");
error_log('Saving question attempt:');
error_log($sql);
}
$recording_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
$em = Database::getManager();
if (false == $updateResults) {
$attempt_id = Database::insert($TBL_TRACK_ATTEMPT, $attempt);
if ($debug) {
error_log("Insert attempt with id #$attempt_id");
}
if ($recording) {
if ($debug) {
error_log("Saving e attempt recording ");
}
$attempt_recording = [
'exe_id' => $attempt_id,
'question_id' => $question_id,
'answer' => $answer,
'marks' => $score,
'insert_date' => $now,
'author' => '',
'session_id' => $session_id,
];
Database::insert($recording_table, $attempt_recording);
if ($attempt_id) {
$recording = new TrackEAttemptRecording();
$recording
->setExeId($attempt_id)
->setQuestionId($question_id)
->setAnswer($answer)
->setMarks($score)
->setAuthor('')
->setSessionId($session_id)
;
$em->persist($recording);
$em->flush();
}
} else {
if (api_get_configuration_value('allow_time_per_question')) {
$attempt['seconds_spent'] = $questionDuration + (int) $attemptData['seconds_spent'];
}
Database::update(
$TBL_TRACK_ATTEMPT,
$attempt,
@ -669,9 +669,6 @@ class Event
}
return $attempt_id;
} else {
return false;
}
}
/**
@ -760,98 +757,6 @@ class Event
}
}
/**
* Records information for common (or admin) events (in the track_e_default table).
*
* @author Yannick Warnier <yannick.warnier@beeznest.com>
*
* @param string $event_type Type of event
* @param string $event_value_type Type of value
* @param mixed $event_value Value (string, or array in the case of user info)
* @param string $datetime Datetime (UTC) (defaults to null)
* @param int $user_id User ID (defaults to null)
* @param int $course_id Course ID (defaults to null)
* @param int $sessionId Session ID
*
* @return bool
* @assert ('','','') === false
*/
public static function addEvent(
$event_type,
$event_value_type,
$event_value,
$datetime = null,
$user_id = null,
$course_id = null,
$sessionId = 0
) {
$table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
if (empty($event_type)) {
return false;
}
$event_type = Database::escape_string($event_type);
$event_value_type = Database::escape_string($event_value_type);
if (!empty($course_id)) {
$course_id = (int) $course_id;
} else {
$course_id = api_get_course_int_id();
}
if (!empty($sessionId)) {
$sessionId = (int) $sessionId;
} else {
$sessionId = api_get_session_id();
}
//Clean the user_info
if (LOG_USER_OBJECT == $event_value_type) {
if (is_array($event_value)) {
unset($event_value['complete_name']);
unset($event_value['complete_name_with_username']);
unset($event_value['firstName']);
unset($event_value['lastName']);
unset($event_value['avatar_small']);
unset($event_value['avatar']);
unset($event_value['mail']);
unset($event_value['password']);
unset($event_value['last_login']);
unset($event_value['picture_uri']);
$event_value = serialize($event_value);
}
}
// If event is an array then the $event_value_type should finish with
// the suffix _array for example LOG_WORK_DATA = work_data_array
if (is_array($event_value)) {
$event_value = serialize($event_value);
}
$event_value = Database::escape_string($event_value);
$sessionId = empty($sessionId) ? api_get_session_id() : (int) $sessionId;
if (!isset($datetime)) {
$datetime = api_get_utc_datetime();
}
$datetime = Database::escape_string($datetime);
if (!isset($user_id)) {
$user_id = api_get_user_id();
}
$params = [
'default_user_id' => $user_id,
'c_id' => $course_id,
'default_date' => $datetime,
'default_event_type' => $event_type,
'default_value_type' => $event_value_type,
'default_value' => $event_value,
'session_id' => $sessionId,
];
Database::insert($table, $params);
return true;
}
/**
* Gets the last attempt of an exercise based in the exe_id.
*
@ -893,9 +798,9 @@ class Event
$row = Database::fetch_array($result);
return $row['question_id'];
} else {
return false;
}
return false;
}
/**
@ -916,7 +821,7 @@ class Event
$lp_item_id,
$lp_item_view_id
) {
$stat_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
$table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
$user_id = (int) $user_id;
$exerciseId = (int) $exerciseId;
$lp_id = (int) $lp_id;
@ -926,7 +831,7 @@ class Event
$sessionId = api_get_session_id();
$sql = "SELECT count(*) as count
FROM $stat_table
FROM $table
WHERE
exe_exo_id = $exerciseId AND
exe_user_id = $user_id AND
@ -937,9 +842,9 @@ class Event
c_id = $courseId AND
session_id = $sessionId";
$query = Database::query($sql);
if (Database::num_rows($query) > 0) {
$attempt = Database::fetch_array($query, 'ASSOC');
$result = Database::query($sql);
if (Database::num_rows($result) > 0) {
$attempt = Database::fetch_array($result, 'ASSOC');
return (int) $attempt['count'];
}
@ -947,6 +852,53 @@ class Event
return 0;
}
public static function getAttemptPosition(
$exeId,
$user_id,
$exerciseId,
$lp_id,
$lp_item_id,
$lp_item_view_id
) {
$table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
$user_id = (int) $user_id;
$exerciseId = (int) $exerciseId;
$lp_id = (int) $lp_id;
$lp_item_id = (int) $lp_item_id;
$lp_item_view_id = (int) $lp_item_view_id;
$courseId = api_get_course_int_id();
$sessionId = api_get_session_id();
$sql = "SELECT exe_id
FROM $table
WHERE
exe_exo_id = $exerciseId AND
exe_user_id = $user_id AND
status = '' AND
orig_lp_id = $lp_id AND
orig_lp_item_id = $lp_item_id AND
orig_lp_item_view_id = $lp_item_view_id AND
c_id = $courseId AND
session_id = $sessionId
ORDER by exe_id
";
$result = Database::query($sql);
if (Database::num_rows($result) > 0) {
$position = 1;
while ($row = Database::fetch_array($result, 'ASSOC')) {
if ($row['exe_id'] === $exeId) {
break;
}
$position++;
}
return $position;
}
return 0;
}
/**
* @param $user_id
* @param $exerciseId
@ -985,10 +937,10 @@ class Event
if (Database::num_rows($query) > 0) {
$attempt = Database::fetch_array($query, 'ASSOC');
return $attempt['count'];
} else {
return 0;
return (int) $attempt['count'];
}
return 0;
}
/**
@ -996,6 +948,7 @@ class Event
* @param int $lp_id
* @param array $course
* @param int $session_id
* @param bool $disconnectExerciseResultsFromLp (Replace orig_lp_* variables to null)
*
* @return bool
*/
@ -1003,14 +956,15 @@ class Event
$user_id,
$lp_id,
$course,
$session_id
$session_id,
$disconnectExerciseResultsFromLp = false
) {
$lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
$lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
$lpInteraction = Database::get_course_table(TABLE_LP_IV_INTERACTION);
$lpObjective = Database::get_course_table(TABLE_LP_IV_OBJECTIVE);
if (empty($course)) {
if (empty($course) || empty($user_id)) {
return false;
}
@ -1065,15 +1019,6 @@ class Event
Database::query($sql);
}
$sql = "DELETE FROM $lp_view_table
WHERE
c_id = $course_id AND
user_id = $user_id AND
lp_id= $lp_id AND
session_id = $session_id
";
Database::query($sql);
$sql = "SELECT exe_id FROM $track_e_exercises
WHERE
exe_user_id = $user_id AND
@ -1081,13 +1026,21 @@ class Event
c_id = $course_id AND
orig_lp_id = $lp_id";
$result = Database::query($sql);
$exe_list = [];
$exeList = [];
while ($row = Database::fetch_array($result, 'ASSOC')) {
$exe_list[] = $row['exe_id'];
$exeList[] = $row['exe_id'];
}
if (!empty($exe_list) && is_array($exe_list) && count($exe_list) > 0) {
$exeListString = implode(',', $exe_list);
if (!empty($exeList) && count($exeList) > 0) {
$exeListString = implode(',', $exeList);
if ($disconnectExerciseResultsFromLp) {
$sql = "UPDATE $track_e_exercises
SET orig_lp_id = null,
orig_lp_item_id = null,
orig_lp_item_view_id = null
WHERE exe_id IN ($exeListString)";
Database::query($sql);
} else {
$sql = "DELETE FROM $track_e_exercises
WHERE exe_id IN ($exeListString)";
Database::query($sql);
@ -1100,6 +1053,16 @@ class Event
WHERE exe_id IN ($exeListString)";
Database::query($sql);
}
}
$sql = "DELETE FROM $lp_view_table
WHERE
c_id = $course_id AND
user_id = $user_id AND
lp_id= $lp_id AND
session_id = $session_id
";
Database::query($sql);
self::addEvent(
LOG_LP_ATTEMPT_DELETE,
@ -1114,6 +1077,98 @@ class Event
return true;
}
/**
* Records information for common (or admin) events (in the track_e_default table).
*
* @param string $event_type Type of event
* @param string $event_value_type Type of value
* @param mixed $event_value Value (string, or array in the case of user info)
* @param string $datetime Datetime (UTC) (defaults to null)
* @param int $user_id User ID (defaults to null)
* @param int $course_id Course ID (defaults to null)
* @param int $sessionId Session ID
*
* @return bool
* @assert ('','','') === false
* @author Yannick Warnier <yannick.warnier@beeznest.com>
*
*/
public static function addEvent(
$event_type,
$event_value_type,
$event_value,
$datetime = null,
$user_id = null,
$course_id = null,
$sessionId = 0
) {
$table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
if (empty($event_type)) {
return false;
}
$event_type = Database::escape_string($event_type);
$event_value_type = Database::escape_string($event_value_type);
if (!empty($course_id)) {
$course_id = (int) $course_id;
} else {
$course_id = api_get_course_int_id();
}
if (!empty($sessionId)) {
$sessionId = (int) $sessionId;
} else {
$sessionId = api_get_session_id();
}
//Clean the user_info
if (LOG_USER_OBJECT == $event_value_type) {
if (is_array($event_value)) {
unset($event_value['complete_name']);
unset($event_value['complete_name_with_username']);
unset($event_value['firstName']);
unset($event_value['lastName']);
unset($event_value['avatar_small']);
unset($event_value['avatar']);
unset($event_value['mail']);
unset($event_value['password']);
unset($event_value['last_login']);
unset($event_value['picture_uri']);
$event_value = serialize($event_value);
}
}
// If event is an array then the $event_value_type should finish with
// the suffix _array for example LOG_WORK_DATA = work_data_array
if (is_array($event_value)) {
$event_value = serialize($event_value);
}
$event_value = Database::escape_string($event_value);
$sessionId = empty($sessionId) ? api_get_session_id() : (int) $sessionId;
if (!isset($datetime)) {
$datetime = api_get_utc_datetime();
}
$datetime = Database::escape_string($datetime);
if (!isset($user_id)) {
$user_id = api_get_user_id();
}
$params = [
'default_user_id' => $user_id,
'c_id' => $course_id,
'default_date' => $datetime,
'default_event_type' => $event_type,
'default_value_type' => $event_value_type,
'default_value' => $event_value,
'session_id' => $sessionId,
];
Database::insert($table, $params);
return true;
}
/**
* Delete all exercise attempts (included in LP or not).
*
@ -1350,7 +1405,7 @@ class Event
*
* @param int user id
* @param int exercise id
* @param string course code
* @param int course id
* @param int session id
* @param int lp id
* @param int lp item id
@ -1405,6 +1460,7 @@ class Event
if (Database::num_rows($res_revised) > 0) {
$row['attempt_revised'] = 1;
}
$row['total_percentage'] = ($row['score'] / $row['max_score']) * 100;
$list[$row['exe_id']] = $row;
$sql = "SELECT * FROM $table_track_attempt
WHERE exe_id = $exeId";
@ -1536,6 +1592,7 @@ class Event
* @param int $exercise_id
* @param int $courseId
* @param int $session_id
* @param bool $skipLpResults
*
* @return array
*/
@ -1543,24 +1600,31 @@ class Event
$user_id,
$exercise_id,
$courseId,
$session_id = 0
$session_id = 0,
$skipLpResults = true
) {
$table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
$table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
$courseId = (int) $courseId;
$exercise_id = (int) $exercise_id;
$session_id = (int) $session_id;
$user_id = (int) $user_id;
$sql = "SELECT * FROM $table_track_exercises
$sql = "SELECT * FROM $table
WHERE
status = '' AND
c_id = $courseId AND
exe_exo_id = $exercise_id AND
session_id = $session_id AND
exe_user_id = $user_id AND
exe_user_id = $user_id
";
if ($skipLpResults) {
$sql .= ' AND
orig_lp_id = 0 AND
orig_lp_item_id = 0
ORDER BY exe_id";
orig_lp_item_id = 0 ';
}
$sql .= ' ORDER BY exe_id ';
$res = Database::query($sql);
$list = [];
@ -1722,9 +1786,12 @@ class Event
}
/**
* @param int $exeId
* Get all the track_e_attempt records for a given
* track_e_exercises.exe_id (pk).
*
* @return array
* @param int $exeId The exe_id from an exercise attempt record
*
* @return array The complete records from track_e_attempt that match the given exe_id
*/
public static function getAllExerciseEventByExeId($exeId)
{
@ -1746,11 +1813,15 @@ class Event
}
/**
* @param int $exeId
* @param int $user_id
* @param int $courseId
* @param int $session_id
* @param int $question_id
* Delete one record from the track_e_attempt table (recorded quiz answer)
* and register the deletion event (LOG_QUESTION_RESULT_DELETE) in
* track_e_default.
*
* @param int $exeId The track_e_exercises.exe_id (primary key)
* @param int $user_id The user who answered (already contained in exe_id)
* @param int $courseId The course in which it happened (already contained in exe_id)
* @param int $session_id The session in which it happened (already contained in exe_id)
* @param int $question_id The c_quiz_question.iid
*/
public static function delete_attempt(
$exeId,
@ -1788,6 +1859,9 @@ class Event
}
/**
* Delete one record from the track_e_hotspot table based on a given
* track_e_exercises.exe_id.
*
* @param $exeId
* @param $user_id
* @param int $courseId
@ -2126,6 +2200,62 @@ class Event
return true;
}
/**
* Register the logout of the course (usually when logging out of the platform)
* from the track_e_access_complete table.
*
* @param array $logInfo Information stored by local.inc.php
*
* @return bool
*/
public static function registerLog($logInfo)
{
$sessionId = api_get_session_id();
$courseId = api_get_course_int_id();
if (isset($logInfo['c_id']) && !empty($logInfo['c_id'])) {
$courseId = $logInfo['c_id'];
}
if (isset($logInfo['session_id']) && !empty($logInfo['session_id'])) {
$sessionId = $logInfo['session_id'];
}
if (!Tracking::minimumTimeAvailable($sessionId, $courseId)) {
return false;
}
if (false === self::isSessionLogNeedToBeSave($sessionId)) {
return false;
}
$loginAs = true === (int) Session::read('login_as');
$logInfo['user_id'] = isset($logInfo['user_id']) ? $logInfo['user_id'] : api_get_user_id();
$logInfo['date_reg'] = isset($logInfo['date_reg']) ? $logInfo['date_reg'] : api_get_utc_datetime();
$logInfo['tool'] = !empty($logInfo['tool']) ? $logInfo['tool'] : '';
$logInfo['tool_id'] = !empty($logInfo['tool_id']) ? (int) $logInfo['tool_id'] : 0;
$logInfo['tool_id_detail'] = !empty($logInfo['tool_id_detail']) ? (int) $logInfo['tool_id_detail'] : 0;
$logInfo['action'] = !empty($logInfo['action']) ? $logInfo['action'] : '';
$logInfo['action_details'] = !empty($logInfo['action_details']) ? $logInfo['action_details'] : '';
$logInfo['ip_user'] = api_get_real_ip();
$logInfo['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
$logInfo['session_id'] = $sessionId;
$logInfo['c_id'] = $courseId;
$logInfo['ch_sid'] = session_id();
$logInfo['login_as'] = $loginAs;
$logInfo['info'] = !empty($logInfo['info']) ? $logInfo['info'] : '';
$logInfo['url'] = $_SERVER['REQUEST_URI'];
$logInfo['current_id'] = isset($logInfo['current_id']) ? $logInfo['current_id'] : Session::read('last_id', 0);
$id = Database::insert('track_e_access_complete', $logInfo);
if ($id && empty($logInfo['current_id'])) {
Session::write('last_id', $id);
}
return true;
}
/**
* Removes a "fake" time spent on the platform, for example to match the
* estimated time he took to author an assignment/work, see configuration
@ -2141,7 +2271,8 @@ class Event
* @param int $courseId The course in which to add the time
* @param int $userId The user for whom to add the time
* @param int $sessionId The session in which to add the time (if any)
* @param string $virtualTime The amount of time to be added, in a hh:mm:ss format. If int, we consider it is expressed in hours.
* @param string $virtualTime The amount of time to be added, in a hh:mm:ss format. If int, we consider it is
* expressed in hours.
*
* @return true on successful removal, false otherwise
*/
@ -2168,7 +2299,7 @@ class Event
// @todo make sure this is portable between DBMSes
// @todo make sure this is portable between DBMSes
if (preg_match('/:/', $virtualTime)) {
list($h, $m, $s) = preg_split('/:/', $virtualTime);
[$h, $m, $s] = preg_split('/:/', $virtualTime);
$virtualTime = $h * 3600 + $m * 60 + $s;
} else {
$virtualTime *= 3600;
@ -2248,59 +2379,49 @@ class Event
return false;
}
/**
* Register the logout of the course (usually when logging out of the platform)
* from the track_e_access_complete table.
*
* @param array $logInfo Information stored by local.inc.php
*
* @return bool
*/
public static function registerLog($logInfo)
public static function getAttemptQuestionDuration($exeId, $questionId)
{
$sessionId = api_get_session_id();
$courseId = api_get_course_int_id();
if (isset($logInfo['c_id']) && !empty($logInfo['c_id'])) {
$courseId = $logInfo['c_id'];
// Check current attempt.
$questionAttempt = self::getQuestionAttemptByExeIdAndQuestion($exeId, $questionId);
$alreadySpent = 0;
if (!empty($questionAttempt) && $questionAttempt['seconds_spent']) {
$alreadySpent = $questionAttempt['seconds_spent'];
}
if (isset($logInfo['session_id']) && !empty($logInfo['session_id'])) {
$sessionId = $logInfo['session_id'];
$now = time();
$questionStart = Session::read('question_start', []);
if (!empty($questionStart) &&
isset($questionStart[$questionId]) && !empty($questionStart[$questionId])
) {
$time = $questionStart[$questionId];
} else {
$diff = 0;
if (!empty($alreadySpent)) {
$diff = $alreadySpent;
}
if (!Tracking::minimumTimeAvailable($sessionId, $courseId)) {
return false;
$time = $questionStart[$questionId] = $now - $diff;
Session::write('question_start', $questionStart);
}
if (false === self::isSessionLogNeedToBeSave($sessionId)) {
return false;
return $now - $time;
}
$loginAs = true === (int) Session::read('login_as');
$logInfo['user_id'] = isset($logInfo['user_id']) ? $logInfo['user_id'] : api_get_user_id();
$logInfo['date_reg'] = isset($logInfo['date_reg']) ? $logInfo['date_reg'] : api_get_utc_datetime();
$logInfo['tool'] = !empty($logInfo['tool']) ? $logInfo['tool'] : '';
$logInfo['tool_id'] = !empty($logInfo['tool_id']) ? (int) $logInfo['tool_id'] : 0;
$logInfo['tool_id_detail'] = !empty($logInfo['tool_id_detail']) ? (int) $logInfo['tool_id_detail'] : 0;
$logInfo['action'] = !empty($logInfo['action']) ? $logInfo['action'] : '';
$logInfo['action_details'] = !empty($logInfo['action_details']) ? $logInfo['action_details'] : '';
$logInfo['ip_user'] = api_get_real_ip();
$logInfo['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
$logInfo['session_id'] = $sessionId;
$logInfo['c_id'] = $courseId;
$logInfo['ch_sid'] = session_id();
$logInfo['login_as'] = $loginAs;
$logInfo['info'] = !empty($logInfo['info']) ? $logInfo['info'] : '';
$logInfo['url'] = $_SERVER['REQUEST_URI'];
$logInfo['current_id'] = isset($logInfo['current_id']) ? $logInfo['current_id'] : Session::read('last_id', 0);
public static function getQuestionAttemptByExeIdAndQuestion($exeId, $questionId)
{
$table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
$exeId = (int) $exeId;
$questionId = (int) $questionId;
$id = Database::insert('track_e_access_complete', $logInfo);
if ($id && empty($logInfo['current_id'])) {
Session::write('last_id', $id);
$sql = "SELECT * FROM $table
WHERE
exe_id = $exeId AND
question_id = $questionId
ORDER BY position";
$result = Database::query($sql);
$attempt = [];
if (Database::num_rows($result)) {
$attempt = Database::fetch_array($result, 'ASSOC');
}
return true;
return $attempt;
}
}

File diff suppressed because it is too large Load Diff

@ -1,16 +1,6 @@
<?php
/* See license terms in /license.txt */
/**
* EVENTS LIBRARY.
*
* This is the events library for Chamilo.
* Functions of this library are used to record informations when some kind
* of event occur. Each event has his own types of informations then each event
* use its own function.
*
* @todo convert queries to use Database API
*/
class ExerciseShowFunctions
{
/**
@ -207,6 +197,7 @@ class ExerciseShowFunctions
* @param bool $showTotalScoreAndUserChoices
*/
public static function display_hotspot_answer(
$exercise,
$feedback_type,
$answerId,
$answer,
@ -230,9 +221,25 @@ class ExerciseShowFunctions
$hide_expected_answer = false;
}
break;
case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK:
$hide_expected_answer = true;
if ($showTotalScoreAndUserChoices) {
$hide_expected_answer = false;
}
if (false === $showTotalScoreAndUserChoices && empty($studentChoice)) {
return '';
}
break;
}
if (!$hide_expected_answer
&& !$studentChoice
&& in_array($resultsDisabled, [RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER])
) {
return;
}
$hotspot_colors = [
$hotspotColors = [
'', // $i starts from 1 on next loop (ugly fix)
'#4271B5',
'#FE8E16',
@ -249,39 +256,38 @@ class ExerciseShowFunctions
'#F7BDE2',
];
$content = '<table class="data_table"><tr>';
$content = '<tr>';
$content .= '<td class="text-center" width="5%">';
$content .= '<span class="fa fa-square fa-fw fa-2x" aria-hidden="true" style="color:'.
$hotspot_colors[$orderColor].'"></span>';
$hotspotColors[$orderColor].'"></span>';
$content .= '</td>';
$content .= '<td class="text-left" width="25%">';
$content .= "$answerId - $answer";
$content .= '</td>';
if (false === $exercise->hideComment) {
$content .= '<td class="text-left" width="10%">';
if (!$hide_expected_answer) {
$status = Display::label(get_lang('Incorrect'), 'danger');
if ($studentChoice) {
$status = Display::label(get_lang('Correct'), 'success');
} else {
if (in_array($resultsDisabled, [
RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER,
])
) {
return '';
}
}
$content .= $status;
} else {
$content .= '&nbsp;';
}
$content .= '</td>';
if (EXERCISE_FEEDBACK_TYPE_EXAM != $feedback_type) {
$content .= '<td class="text-left" width="60%">';
if ($studentChoice) {
$content .= '<span style="font-weight: bold; color: #008000;">'.nl2br($answerComment).'</span>';
} else {
$content .= '&nbsp;';
}
$content .= '</td>';
} else {
$content .= '<td class="text-left" width="60%">&nbsp;</td>';
}
}
$content .= '</tr>';
echo $content;
@ -319,6 +325,9 @@ class ExerciseShowFunctions
$showTotalScoreAndUserChoices,
$export = false
) {
if (true === $exercise->hideNoAnswer && empty($studentChoice)) {
return '';
}
if ($export) {
$answer = strip_tags_blacklist($answer, ['title', 'head']);
// Fix answers that contains this tags
@ -356,6 +365,11 @@ class ExerciseShowFunctions
$hide_expected_answer = false;
}
break;
case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK:
if (false === $showTotalScoreAndUserChoices && empty($studentChoiceInt)) {
return '';
}
break;
}
$icon = in_array($answerType, [UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION]) ? 'radio' : 'checkbox';
@ -418,6 +432,7 @@ class ExerciseShowFunctions
$showComment = true;
}
if (false === $exercise->hideComment) {
if ($showComment) {
echo '<td width="20%">';
$color = 'black';
@ -435,6 +450,7 @@ class ExerciseShowFunctions
} else {
echo '<td>&nbsp;</td>';
}
}
echo '</tr>';
}
@ -486,6 +502,11 @@ class ExerciseShowFunctions
$hide_expected_answer = false;
}
break;
case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK:
if (false === $showTotalScoreAndUserChoices && empty($studentChoice)) {
return '';
}
break;
}
$content = '<tr>';
@ -531,6 +552,7 @@ class ExerciseShowFunctions
$content .= '</td>';
}
if (false === $exercise->hideComment) {
if (EXERCISE_FEEDBACK_TYPE_EXAM != $feedbackType) {
$content .= '<td width="20%">';
$color = 'black';
@ -553,6 +575,7 @@ class ExerciseShowFunctions
}
$content .= '</td>';
}
}
$content .= '</tr>';
echo $content;
@ -645,6 +668,7 @@ class ExerciseShowFunctions
'</div>
</td>';
if (false === $exercise->hideComment) {
if (EXERCISE_FEEDBACK_TYPE_EXAM != $feedbackType) {
echo '<td width="20%">';
if (isset($newOptions[$studentChoice])) {
@ -654,6 +678,7 @@ class ExerciseShowFunctions
} else {
echo '<td>&nbsp;</td>';
}
}
echo '</tr>';
}
@ -704,6 +729,11 @@ class ExerciseShowFunctions
$hide_expected_answer = false;
}
break;
case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK:
if (false === $showTotalScoreAndUserChoices && empty($studentChoice)) {
return '';
}
break;
}
echo '<tr>';
@ -750,13 +780,17 @@ class ExerciseShowFunctions
echo '</td>';
}
if (false === $exercise->hideComment) {
if (EXERCISE_FEEDBACK_TYPE_EXAM != $feedbackType) {
echo '<td width="20%">';
//@todo replace this harcoded value
if ($studentChoice || in_array($resultsDisabled, [
if ($studentChoice || in_array(
$resultsDisabled,
[
RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER,
RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING,
])
]
)
) {
$color = 'black';
if ($studentChoice == $answerCorrect) {
@ -771,6 +805,7 @@ class ExerciseShowFunctions
} else {
echo '<td>&nbsp;</td>';
}
}
echo '</tr>';
}

@ -118,13 +118,14 @@ class TrackEAttempt
/**
* @var int
*
* @ORM\Column(name="second_spent", type="integer")
* @ORM\Column(name="seconds_spent", type="integer")
*/
protected $secondSpent;
protected $secondsSpent;
public function __construct()
{
$this->secondSpent = 0;
$this->teacherComment = '';
$this->secondsSpent = 0;
}
/**
@ -352,4 +353,16 @@ class TrackEAttempt
{
return $this->id;
}
public function getSecondsSpent(): int
{
return $this->secondsSpent;
}
public function setSecondsSpent(int $secondsSpent): TrackEAttempt
{
$this->secondsSpent = $secondsSpent;
return $this;
}
}

@ -269,4 +269,23 @@ class TrackEAttemptRecording
{
return $this->id;
}
/**
* @return int
*/
public function getAnswer()
{
return $this->answer;
}
/**
* @param int $answer
*/
public function setAnswer($answer): self
{
$this->answer = $answer;
return $this;
}
}

@ -97,8 +97,8 @@ class Version20180904175500 extends AbstractMigrationChamilo
$this->addSql('CREATE INDEX idx_track_e_attempt_tms ON track_e_attempt (tms)');
}
if (false === $table->hasColumn('second_spent')) {
$this->addSql('ALTER TABLE track_e_attempt ADD second_spent INT NOT NULL, CHANGE user_id user_id INT DEFAULT NULL');
if (false === $table->hasColumn('seconds_spent')) {
$this->addSql('ALTER TABLE track_e_attempt ADD seconds_spent INT NOT NULL, CHANGE user_id user_id INT DEFAULT NULL');
}
if (false === $table->hasForeignKey('FK_A89CC3B691D79BD3')) {

Loading…
Cancel
Save