From 483203f31f4b9d3031b8e95118876f86d6e3a9e5 Mon Sep 17 00:00:00 2001 From: Nosolored Date: Thu, 14 Nov 2019 14:14:07 +0100 Subject: [PATCH 1/8] New feature: Personality test --- main/exercise/admin.php | 5 + main/exercise/answer.class.php | 46 +- main/exercise/exercise.class.php | 889 +++++++++++-- main/exercise/exercise.php | 3 +- main/exercise/exercise_admin.php | 15 +- main/exercise/exercise_result.php | 1 + main/exercise/exercise_show.php | 2 +- main/exercise/exercise_submit.php | 36 +- main/exercise/overview.php | 25 +- main/exercise/ptest_admin.php | 389 ++++++ main/exercise/ptest_agree_disagree.class.php | 293 +++++ main/exercise/ptest_agree_reorder.class.php | 293 +++++ main/exercise/ptest_agree_scale.class.php | 346 +++++ main/exercise/ptest_category.class.php | 1145 +++++++++++++++++ .../exercise/ptest_category_ranking.class.php | 296 +++++ main/exercise/ptest_exercise_report.php | 504 ++++++++ main/exercise/ptest_exercise_show.php | 166 +++ main/exercise/ptest_stats.php | 258 ++++ main/exercise/ptest_stats_graph.php | 223 ++++ main/exercise/ptests_category.php | 252 ++++ main/exercise/question.class.php | 206 ++- .../question_list_ptest_admin.inc.php | 315 +++++ main/exercise/question_ptest_admin.inc.php | 91 ++ main/exercise/result.php | 1 + main/img/icons/22/new_personality_test.png | Bin 0 -> 791 bytes main/img/icons/22/ptaod.png | Bin 0 -> 897 bytes main/img/icons/22/ptorder.png | Bin 0 -> 509 bytes main/img/icons/22/ptscale.png | Bin 0 -> 466 bytes main/img/icons/32/new_personality_test.png | Bin 0 -> 1336 bytes main/img/icons/64/ptaod.png | Bin 0 -> 2718 bytes main/img/icons/64/ptorder.png | Bin 0 -> 1470 bytes main/img/icons/64/ptscale.png | Bin 0 -> 1424 bytes main/inc/ajax/exercise.ajax.php | 61 +- main/inc/ajax/model.ajax.php | 44 + main/inc/lib/api.lib.php | 14 + main/inc/lib/database.constants.inc.php | 1 + main/inc/lib/exercise.lib.php | 631 +++++++-- main/inc/lib/exercise_show_functions.lib.php | 88 +- main/lang/english/trad4all.inc.php | 25 + main/lang/spanish/trad4all.inc.php | 25 + .../CourseBundle/Entity/CQuizAnswer.php | 32 + 41 files changed, 6526 insertions(+), 195 deletions(-) create mode 100644 main/exercise/ptest_admin.php create mode 100644 main/exercise/ptest_agree_disagree.class.php create mode 100644 main/exercise/ptest_agree_reorder.class.php create mode 100644 main/exercise/ptest_agree_scale.class.php create mode 100644 main/exercise/ptest_category.class.php create mode 100644 main/exercise/ptest_category_ranking.class.php create mode 100644 main/exercise/ptest_exercise_report.php create mode 100644 main/exercise/ptest_exercise_show.php create mode 100644 main/exercise/ptest_stats.php create mode 100644 main/exercise/ptest_stats_graph.php create mode 100644 main/exercise/ptests_category.php create mode 100644 main/exercise/question_list_ptest_admin.inc.php create mode 100644 main/exercise/question_ptest_admin.inc.php create mode 100644 main/img/icons/22/new_personality_test.png create mode 100644 main/img/icons/22/ptaod.png create mode 100644 main/img/icons/22/ptorder.png create mode 100644 main/img/icons/22/ptscale.png create mode 100644 main/img/icons/32/new_personality_test.png create mode 100644 main/img/icons/64/ptaod.png create mode 100644 main/img/icons/64/ptorder.png create mode 100644 main/img/icons/64/ptscale.png diff --git a/main/exercise/admin.php b/main/exercise/admin.php index 7a99f359838..76b49e7eb19 100755 --- a/main/exercise/admin.php +++ b/main/exercise/admin.php @@ -174,6 +174,11 @@ api_not_allowed(true); } +if ($objExercise->selectPtType() == EXERCISE_PT_TYPE_PTEST) { + header('Location: '.api_get_path(WEB_CODE_PATH).'exercise/ptest_admin.php?exerciseId='.$exerciseId.'&'.api_get_cidreq()); + exit(); +} + // 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 diff --git a/main/exercise/answer.class.php b/main/exercise/answer.class.php index 13d2d85003d..27a07baa5fb 100755 --- a/main/exercise/answer.class.php +++ b/main/exercise/answer.class.php @@ -25,6 +25,7 @@ class Answer public $hotspot_coordinates; public $hotspot_type; public $destination; + public $ptest_category; // these arrays are used to save temporarily new answers // then they are moved into the arrays above or deleted in the event of cancellation public $new_answer; @@ -39,6 +40,7 @@ class Answer public $nbrAnswers; public $new_nbrAnswers; public $new_destination; // id of the next question if feedback option is set to Directfeedback + public $new_ptest_category; // id c_quiz_category_test public $course; //Course information public $iid; public $questionJSId; @@ -67,6 +69,7 @@ public function __construct($questionId, $course_id = 0, $exercise = null, $read $this->hotspot_coordinates = []; $this->hotspot_type = []; $this->destination = []; + $this->ptest_category = []; // clears $new_* arrays $this->cancel(); @@ -114,6 +117,7 @@ public function cancel() $this->new_hotspot_type = []; $this->new_nbrAnswers = 0; $this->new_destination = []; + $this->new_ptest_category = []; } /** @@ -146,6 +150,7 @@ public function read() $this->hotspot_coordinates[$i] = $object->hotspot_coordinates; $this->hotspot_type[$i] = $object->hotspot_type; $this->destination[$i] = $object->destination; + $this->ptest_category[$i] = $object->ptest_category; $this->autoId[$i] = $object->id_auto; $this->iid[$i] = $object->iid; $i++; @@ -249,6 +254,7 @@ public function readOrderedBy($field, $order = 'ASC') hotspot_coordinates, hotspot_type, destination, + ptest_category, id_auto, iid FROM $TBL_ANSWER @@ -274,6 +280,7 @@ public function readOrderedBy($field, $order = 'ASC') $this->hotspot_coordinates[$i] = $object->hotspot_coordinates; $this->hotspot_type[$i] = $object->hotspot_type; $this->destination[$i] = $object->destination; + $this->ptest_category[$i] = $object->ptest_category; $this->autoId[$i] = $object->id_auto; $this->iid[$i] = $object->iid; $i++; @@ -288,6 +295,7 @@ public function readOrderedBy($field, $order = 'ASC') $this->hotspot_coordinates[$i] = isset($object->hotspot_coordinates) ? $object->hotspot_coordinates : 0; $this->hotspot_type[$i] = isset($object->hotspot_type) ? $object->hotspot_type : 0; $this->destination[$i] = $doubt_data->destination; + $this->ptest_category[$i] = $object->ptest_category; $this->autoId[$i] = $doubt_data->id_auto; $this->iid[$i] = $doubt_data->iid; $i++; @@ -347,6 +355,20 @@ public function selectDestination($id) return isset($this->destination[$id]) ? $this->destination[$id] : null; } + /** + * returns the question ID of the personality test question. + * + * @author Jose Angel Ruiz (NOSOLORED) + * + * @param int $id + * + * @return int - the question ID + */ + public function selectPtCategory($id) + { + return isset($this->ptest_category[$id]) ? $this->ptest_category[$id] : null; + } + /** * returns the answer title. * @@ -445,6 +467,7 @@ public function getAnswersList($decode = false) 'hotspot_type' => $this->hotspot_type[$i], 'correct' => $this->correct[$i], 'destination' => $this->destination[$i], + 'ptest_category' => $this->ptest_category[$i], ]; } } @@ -598,7 +621,8 @@ public function createAnswer( $position, $new_hotspot_coordinates = null, $new_hotspot_type = null, - $destination = '' + $destination = '', + $ptest_category = null ) { $this->new_nbrAnswers++; $id = $this->new_nbrAnswers; @@ -610,6 +634,7 @@ public function createAnswer( $this->new_hotspot_coordinates[$id] = $new_hotspot_coordinates; $this->new_hotspot_type[$id] = $new_hotspot_type; $this->new_destination[$id] = $destination; + $this->new_ptest_category[$id] = $ptest_category; } /** @@ -626,6 +651,7 @@ public function createAnswer( * @param string $destination * @param string $hotSpotCoordinates * @param string $hotSpotType + * @param int $ptestCategory * * @return CQuizAnswer */ @@ -638,7 +664,8 @@ public function updateAnswers( $position, $destination, $hotSpotCoordinates, - $hotSpotType + $hotSpotType, + $ptestCategory ) { $em = Database::getManager(); @@ -653,7 +680,8 @@ public function updateAnswers( ->setPosition($position) ->setDestination($destination) ->setHotspotCoordinates($hotSpotCoordinates) - ->setHotspotType($hotSpotType); + ->setHotspotType($hotSpotType) + ->setPtestCategory($ptestCategory); $em->merge($quizAnswer); $em->flush(); @@ -687,6 +715,7 @@ public function save() $hotspot_coordinates = isset($this->new_hotspot_coordinates[$i]) ? $this->new_hotspot_coordinates[$i] : ''; $hotspot_type = isset($this->new_hotspot_type[$i]) ? $this->new_hotspot_type[$i] : ''; $destination = isset($this->new_destination[$i]) ? $this->new_destination[$i] : ''; + $ptestCategory = isset($this->new_ptest_category[$i]) ? $this->new_ptest_category[$i] : ''; $autoId = $this->selectAutoId($i); $iid = isset($this->iid[$i]) ? $this->iid[$i] : 0; @@ -703,7 +732,8 @@ public function save() ->setPosition($position) ->setHotspotCoordinates($hotspot_coordinates) ->setHotspotType($hotspot_type) - ->setDestination($destination); + ->setDestination($destination) + ->setPtestCategory($ptestCategory); $em->persist($quizAnswer); $em->flush(); @@ -750,7 +780,8 @@ public function save() $this->new_position[$i], $this->new_destination[$i], $this->new_hotspot_coordinates[$i], - $this->new_hotspot_type[$i] + $this->new_hotspot_type[$i], + $this->new_ptest_category[$i] ); } @@ -818,6 +849,7 @@ public function save() $this->hotspot_type = $this->new_hotspot_type; $this->nbrAnswers = $this->new_nbrAnswers; $this->destination = $this->new_destination; + $this->ptest_category = $this->new_ptest_category; $this->cancel(); } @@ -893,6 +925,7 @@ public function duplicate($newQuestion, $course_info = null) 'hotspot_coordinates' => $this->hotspot_coordinates[$i], 'hotspot_type' => $this->hotspot_type[$i], 'destination' => $this->destination[$i], + 'ptest_category' => $this->ptest_category[$i], ]; $temp[$answer['position']] = $answer; $allAnswers[$this->id[$i]] = $this->answer[$i]; @@ -977,7 +1010,8 @@ public function duplicate($newQuestion, $course_info = null) ->setPosition($this->position[$i]) ->setHotspotCoordinates($this->hotspot_coordinates[$i]) ->setHotspotType($this->hotspot_type[$i]) - ->setDestination($this->destination[$i]); + ->setDestination($this->destination[$i]) + ->setPtestCategory($this->ptest_category[$i]); $em->persist($quizAnswer); $em->flush(); diff --git a/main/exercise/exercise.class.php b/main/exercise/exercise.class.php index a4cee6213e7..5fc59724265 100755 --- a/main/exercise/exercise.class.php +++ b/main/exercise/exercise.class.php @@ -30,6 +30,7 @@ class Exercise public $exercise; public $description; public $sound; + public $pt_type; //EXERCISE_PT_TYPE_CLASSIC public $type; //ALL_ON_ONE_PAGE or ONE_PER_PAGE public $random; public $random_answers; @@ -104,6 +105,7 @@ public function __construct($courseId = 0) $this->exercise = ''; $this->description = ''; $this->sound = ''; + $this->pt_type = EXERCISE_PT_TYPE_CLASSIC; $this->type = ALL_ON_ONE_PAGE; $this->random = 0; $this->random_answers = 0; @@ -177,6 +179,7 @@ public function read($id, $parseQuestionList = true) $this->title = $object->title; $this->description = $object->description; $this->sound = $object->sound; + $this->pt_type = $object->pt_type; $this->type = $object->type; if (empty($this->type)) { $this->type = ONE_PER_PAGE; @@ -363,6 +366,18 @@ public function selectSound() return $this->sound; } + /** + * returns the exercise type. + * + * @author Olivier Brouckaert + * + * @return int - exercise type + */ + public function selectPtType() + { + return $this->pt_type; + } + /** * returns the exercise type. * @@ -1420,6 +1435,18 @@ public function updateSound($sound, $delete) } } + /** + * changes the exercise type. + * + * @author Olivier Brouckaert + * + * @param int $type - exercise type + */ + public function updatePtType($ptType) + { + $this->pt_type = $ptType; + } + /** * changes the exercise type. * @@ -1518,6 +1545,7 @@ public function save($type_e = '') $exercise = $this->exercise; $description = $this->description; $sound = $this->sound; + $ptType = $this->pt_type; $type = $this->type; $attempts = isset($this->attempts) ? $this->attempts : 0; $feedback_type = isset($this->feedback_type) ? $this->feedback_type : 0; @@ -1562,6 +1590,7 @@ public function save($type_e = '') if ($type_e != 'simple') { $paramsExtra = [ 'sound' => $sound, + 'pt_type' => $ptType, 'type' => $type, 'random' => $random, 'random_answers' => $random_answers, @@ -1652,6 +1681,7 @@ public function save($type_e = '') 'title' => $exercise, 'description' => $description, 'sound' => $sound, + 'pt_type' => $ptType, 'type' => $type, 'random' => $random, 'random_answers' => $random_answers, @@ -2415,6 +2445,315 @@ public function createForm($form, $type = 'full') $form->addElement('html', ''); } + if ($type == 'ptest') { + $form->addElement('hidden', 'exercisePtType', EXERCISE_PT_TYPE_PTEST); + $form->addElement('hidden', 'results_disabled', RESULT_DISABLE_PT_TYPE_PTEST); + + // Type of questions disposition on page + $radios = []; + $radios[] = $form->createElement( + 'radio', + 'exerciseType', + null, + get_lang('SimpleExercise'), + '1', + [ + 'onclick' => 'check_per_page_all()', + 'id' => 'option_page_all', + ] + ); + $radios[] = $form->createElement( + 'radio', + 'exerciseType', + null, + get_lang('SequentialExercise'), + '2', + [ + 'onclick' => 'check_per_page_one()', + 'id' => 'option_page_one', + ] + ); + + $form->addGroup($radios, null, get_lang('QuestionsPerPage')); + + $option = [ + EX_Q_SELECTION_ORDERED => get_lang('OrderedByUser'), + // Defined by user + EX_Q_SELECTION_RANDOM => get_lang('Random'), + // 1-10, All + 'per_categories' => '--------'.get_lang('UsingCategories').'----------', + // Base (A 123 {3} B 456 {3} C 789{2} D 0{0}) --> Matrix {3, 3, 2, 0} + EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_ORDERED => get_lang('OrderedCategoriesAlphabeticallyWithQuestionsOrdered'), + // A 123 B 456 C 78 (0, 1, all) + EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED => get_lang('RandomCategoriesWithQuestionsOrdered'), + // C 78 B 456 A 123 + EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_RANDOM => get_lang('OrderedCategoriesAlphabeticallyWithRandomQuestions'), + // A 321 B 654 C 87 + EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM => get_lang('RandomCategoriesWithRandomQuestions'), + // C 87 B 654 A 321 + //EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED_NO_GROUPED => get_lang('RandomCategoriesWithQuestionsOrderedNoQuestionGrouped'), + /* B 456 C 78 A 123 + 456 78 123 + 123 456 78 + */ + //EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM_NO_GROUPED => get_lang('RandomCategoriesWithRandomQuestionsNoQuestionGrouped'), + /* + A 123 B 456 C 78 + B 456 C 78 A 123 + B 654 C 87 A 321 + 654 87 321 + 165 842 73 + */ + //EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_ORDERED => get_lang('OrderedCategoriesByParentWithQuestionsOrdered'), + //EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_RANDOM => get_lang('OrderedCategoriesByParentWithQuestionsRandom'), + ]; + + $form->addElement( + 'select', + 'question_selection_type', + [get_lang('QuestionSelection')], + $option, + [ + 'id' => 'questionSelection', + 'onchange' => 'checkQuestionSelection()', + ] + ); + + $pageConfig = api_get_configuration_value('allow_quiz_results_page_config'); + if ($pageConfig) { + $group = [ + $form->createElement( + 'checkbox', + 'hide_expected_answer', + null, + get_lang('HideExpectedAnswer') + ), + $form->createElement( + 'checkbox', + 'hide_total_score', + null, + get_lang('HideTotalScore') + ), + $form->createElement( + 'checkbox', + 'hide_question_score', + null, + get_lang('HideQuestionScore') + ), + ]; + $form->addGroup($group, null, get_lang('ResultsConfigurationPage')); + } + + $displayMatrix = 'none'; + $displayRandom = 'none'; + $selectionType = $this->getQuestionSelectionType(); + switch ($selectionType) { + case EX_Q_SELECTION_RANDOM: + $displayRandom = 'block'; + break; + case $selectionType >= EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_ORDERED: + $displayMatrix = 'block'; + break; + } + + $form->addElement( + 'html', + '
' + ); + // Number of random question. + $max = ($this->id > 0) ? $this->getQuestionCount() : 10; + $option = range(0, $max); + $option[0] = get_lang('No'); + $option[-1] = get_lang('AllQuestionsShort'); + $form->addElement( + 'select', + 'randomQuestions', + [ + get_lang('RandomQuestions'), + get_lang('RandomQuestionsHelp'), + ], + $option, + ['id' => 'randomQuestions'] + ); + $form->addElement('html', '
'); + + $form->addElement( + 'html', + '
' + ); + + // Category selection. + $cat = new TestCategory(); + $cat_form = $cat->returnCategoryForm($this); + if (empty($cat_form)) { + $cat_form = ''.get_lang('NoCategoriesDefined').''; + } + $form->addElement('label', null, $cat_form); + $form->addElement('html', '
'); + + // Random answers. + $radios_random_answers = [ + $form->createElement('radio', 'randomAnswers', null, get_lang('Yes'), '1'), + $form->createElement('radio', 'randomAnswers', null, get_lang('No'), '0'), + ]; + $form->addGroup($radios_random_answers, null, get_lang('RandomAnswers')); + + // Category name. + $radio_display_cat_name = [ + $form->createElement('radio', 'display_category_name', null, get_lang('Yes'), '1'), + $form->createElement('radio', 'display_category_name', null, get_lang('No'), '0'), + ]; + $form->addGroup($radio_display_cat_name, null, get_lang('QuestionDisplayCategoryName')); + + // Hide question title. + $group = [ + $form->createElement('radio', 'hide_question_title', null, get_lang('Yes'), '1'), + $form->createElement('radio', 'hide_question_title', null, get_lang('No'), '0'), + ]; + $form->addGroup($group, null, get_lang('HideQuestionTitle')); + + $allow = api_get_configuration_value('allow_quiz_show_previous_button_setting'); + + if ($allow === true) { + // Hide question title. + $group = [ + $form->createElement( + 'radio', + 'show_previous_button', + null, + get_lang('Yes'), + '1' + ), + $form->createElement( + 'radio', + 'show_previous_button', + null, + get_lang('No'), + '0' + ), + ]; + $form->addGroup($group, null, get_lang('ShowPreviousButton')); + } + + // Attempts + $attempt_option = range(0, 10); + $attempt_option[0] = get_lang('Infinite'); + + $form->addElement( + 'select', + 'exerciseAttempts', + get_lang('ExerciseAttempts'), + $attempt_option, + ['id' => 'exerciseAttempts'] + ); + + // Exercise time limit + $form->addElement( + 'checkbox', + 'activate_start_date_check', + null, + get_lang('EnableStartTime'), + ['onclick' => 'activate_start_date()'] + ); + + if (!empty($this->start_time)) { + $form->addElement('html', '
'); + } else { + $form->addElement('html', ''); + $form->addElement( + 'checkbox', + 'activate_end_date_check', + null, + get_lang('EnableEndTime'), + ['onclick' => 'activate_end_date()'] + ); + + if (!empty($this->end_time)) { + $form->addElement('html', '
'); + } else { + $form->addElement('html', ''); + + $display = 'block'; + + $form->addElement('html', '
 
'); + $form->addElement('checkbox', 'review_answers', null, get_lang('ReviewAnswers')); + $form->addElement('html', '
'); + + // Timer control + $form->addElement( + 'checkbox', + 'enabletimercontrol', + null, + get_lang('EnableTimerControl'), + [ + 'onclick' => 'option_time_expired()', + 'id' => 'enabletimercontrol', + 'onload' => 'check_load_time()', + ] + ); + + $expired_date = (int) $this->selectExpiredTime(); + + if (($expired_date != '0')) { + $form->addElement('html', '
'); + } else { + $form->addElement('html', ''); + + // add the text_when_finished textbox + $form->addHtmlEditor( + 'text_when_finished', + get_lang('TextWhenFinished'), + false, + false, + $editor_config + ); + + $allow = api_get_configuration_value('allow_notification_setting_per_exercise'); + if ($allow === true) { + $settings = ExerciseLib::getNotificationSettings(); + $group = []; + foreach ($settings as $itemId => $label) { + $group[] = $form->createElement( + 'checkbox', + 'notifications[]', + null, + $label, + ['value' => $itemId] + ); + } + $form->addGroup($group, '', [get_lang('EmailNotifications')]); + } + + $form->addCheckBox( + 'update_title_in_lps', + null, + get_lang('UpdateTitleInLps') + ); + + $form->addElement('html', '
'); //End advanced setting + $form->addElement('html', '
'); + + } + // submit if (isset($_GET['exerciseId'])) { $form->addButtonSave(get_lang('ModifyExercise'), 'submitExercise'); @@ -2492,6 +2831,66 @@ public function createForm($form, $type = 'full') $defaults['on_success_message'] = null; $defaults['on_failed_message'] = null; } + } else if ($type == 'ptest') { + // rules + $form->addRule('exerciseAttempts', get_lang('Numeric'), 'numeric'); + $form->addRule('start_time', get_lang('InvalidDate'), 'datetime'); + $form->addRule('end_time', get_lang('InvalidDate'), 'datetime'); + + if ($this->id > 0) { + $defaults['randomQuestions'] = $this->random; + $defaults['randomAnswers'] = $this->getRandomAnswers(); + $defaults['exerciseType'] = $this->selectType(); + $defaults['exerciseTitle'] = $this->get_formated_title(); + $defaults['exerciseDescription'] = $this->selectDescription(); + $defaults['exerciseAttempts'] = $this->selectAttempts(); + $defaults['review_answers'] = $this->review_answers; + $defaults['randomByCat'] = $this->getRandomByCategory(); + $defaults['text_when_finished'] = $this->getTextWhenFinished(); + $defaults['display_category_name'] = $this->selectDisplayCategoryName(); + $defaults['question_selection_type'] = $this->getQuestionSelectionType(); + $defaults['hide_question_title'] = $this->getHideQuestionTitle(); + $defaults['show_previous_button'] = $this->showPreviousButton(); + $defaults['exercise_category_id'] = $this->getExerciseCategoryId(); + + if (!empty($this->start_time)) { + $defaults['activate_start_date_check'] = 1; + } + if (!empty($this->end_time)) { + $defaults['activate_end_date_check'] = 1; + } + + $defaults['start_time'] = !empty($this->start_time) ? api_get_local_time($this->start_time) : date('Y-m-d 12:00:00'); + $defaults['end_time'] = !empty($this->end_time) ? api_get_local_time($this->end_time) : date('Y-m-d 12:00:00', time() + 84600); + + // Get expired time + if ($this->expired_time != '0') { + $defaults['enabletimercontrol'] = 1; + $defaults['enabletimercontroltotalminutes'] = $this->expired_time; + } else { + $defaults['enabletimercontroltotalminutes'] = 0; + } + } else { + $defaults['exerciseType'] = 2; + $defaults['exerciseAttempts'] = 0; + $defaults['randomQuestions'] = 0; + $defaults['randomAnswers'] = 0; + $defaults['exerciseDescription'] = ''; + $defaults['exerciseFeedbackType'] = 0; + $defaults['results_disabled'] = RESULT_DISABLE_PT_TYPE_PTEST; + $defaults['randomByCat'] = 0; + $defaults['text_when_finished'] = ''; + $defaults['start_time'] = date('Y-m-d 12:00:00'); + $defaults['display_category_name'] = 1; + $defaults['end_time'] = date('Y-m-d 12:00:00', time() + 84600); + $defaults['pass_percentage'] = ''; + $defaults['end_button'] = $this->selectEndButton(); + $defaults['question_selection_type'] = 1; + $defaults['hide_question_title'] = 0; + $defaults['show_previous_button'] = 1; + $defaults['on_success_message'] = null; + $defaults['on_failed_message'] = null; + } } else { $defaults['exerciseTitle'] = $this->selectTitle(); $defaults['exerciseDescription'] = $this->selectDescription(); @@ -2602,6 +3001,7 @@ public function processCreation($form, $type = '') $this->updateDescription($form->getSubmitValue('exerciseDescription')); $this->updateAttempts($form->getSubmitValue('exerciseAttempts')); $this->updateFeedbackType($form->getSubmitValue('exerciseFeedbackType')); + $this->updatePtType($form->getSubmitValue('exercisePtType')); $this->updateType($form->getSubmitValue('exerciseType')); $this->setRandom($form->getSubmitValue('randomQuestions')); $this->updateRandomAnswers($form->getSubmitValue('randomAnswers')); @@ -3503,9 +3903,14 @@ public function manage_answer( $table_ans = Database::get_course_table(TABLE_QUIZ_ANSWER); $studentChoiceDegree = null; + $ptest = false; + if ($this->selectPtType() == EXERCISE_PT_TYPE_PTEST) { + $ptest = true; + } + // Creates a temporary Question object $course_id = $this->course_id; - $objQuestionTmp = Question::read($questionId, $this->course); + $objQuestionTmp = Question::read($questionId, $this->course, true, $ptest); if ($objQuestionTmp === false) { return false; @@ -4767,6 +5172,70 @@ public function manage_answer( $questionScore = 0; } break; + case QUESTION_PT_TYPE_CATEGORY_RANKING: + if ($from_database) { + $sql = "SELECT answer FROM $TBL_TRACK_ATTEMPT + WHERE + exe_id = $exeId AND + question_id = $questionId"; + $result = Database::query($sql); + $choice = Database::result($result, 0, 'answer'); + + $studentChoice = $choice == $answerAutoId ? 1 : 0; + } else { + $studentChoice = $choice == $answerAutoId ? 1 : 0; + } + break; + case QUESTION_PT_TYPE_AGREE_OR_DISAGREE: + $studentChoice = 0; + if ($from_database) { + $sql = "SELECT answer FROM $TBL_TRACK_ATTEMPT + WHERE + exe_id = $exeId AND + question_id = $questionId"; + $result = Database::query($sql); + $choice = Database::result($result, 0, 'answer'); + $aux = explode(',', $choice); + if ($aux[0] == $answerAutoId) { + $studentChoice = ANSWER_AGREE; + } + if ($aux[1] == $answerAutoId) { + $studentChoice = ANSWER_DISAGREE; + } + } + break; + case QUESTION_PT_TYPE_AGREE_SCALE: + $studentChoice = 0; + if ($from_database) { + $sql = "SELECT answer FROM $TBL_TRACK_ATTEMPT + WHERE + exe_id = $exeId AND + question_id = $questionId"; + $result = Database::query($sql); + $choice = Database::result($result, 0, 'answer'); + $aux = json_decode($choice, true); + if (!empty($aux[$answerAutoId])) { + $studentChoice = $aux[$answerAutoId]; + } + } + break; + case QUESTION_PT_TYPE_AGREE_REORDER: + $studentChoice = 0; + if ($from_database) { + $sql = "SELECT answer FROM $TBL_TRACK_ATTEMPT + WHERE + exe_id = $exeId AND + question_id = $questionId"; + $result = Database::query($sql); + $choice = Database::result($result, 0, 'answer'); + $aux = json_decode($choice, true); + foreach ($aux as $key => $value) { + if ($value == $answerAutoId) { + $studentChoice = $key; + } + } + } + break; } if ($show_result) { @@ -5087,6 +5556,23 @@ public function manage_answer( $questionScore, $results_disabled ); + } elseif (in_array( + $answerType, + [ + QUESTION_PT_TYPE_CATEGORY_RANKING, + QUESTION_PT_TYPE_AGREE_OR_DISAGREE, + QUESTION_PT_TYPE_AGREE_SCALE, + QUESTION_PT_TYPE_AGREE_REORDER, + ] + )) { + ExerciseShowFunctions::display_ptest_answer( + $this, + $answerType, + $studentChoice, + $answer, + $answerComment, + $this->export + ); } } } else { @@ -5799,10 +6285,28 @@ public function manage_answer( false, $objQuestionTmp->getAbsoluteFilePath() ); + } elseif ($answerType == QUESTION_PT_TYPE_AGREE_OR_DISAGREE) { + $answer = implode(',', $choice); + Event::saveQuestionAttempt( + $questionScore, + $answer, + $quesId, + $exeId, + 0, + $this->id + ); } elseif ( in_array( $answerType, - [UNIQUE_ANSWER, UNIQUE_ANSWER_IMAGE, UNIQUE_ANSWER_NO_OPTION, READING_COMPREHENSION] + [ + UNIQUE_ANSWER, + UNIQUE_ANSWER_IMAGE, + UNIQUE_ANSWER_NO_OPTION, + READING_COMPREHENSION, + QUESTION_PT_TYPE_CATEGORY_RANKING, + QUESTION_PT_TYPE_AGREE_SCALE, + QUESTION_PT_TYPE_AGREE_REORDER, + ] ) ) { $answer = $choice; @@ -6157,6 +6661,197 @@ public function showExerciseResultHeader( return $content; } + /** + * Returns the exercise charts. + * + * @param int $exeId + * @param int $courseId + * @param int $sessionId + * + * @return string + */ + public function showPtestCharts($exeId, $courseId = 0, $sessionId = 0) + { + $exeId = (int) $exeId; + $courseId = empty($courseId) ? api_get_course_int_id() : (int) $courseId; + $sessionId = empty($sessionId) ? api_get_session_id() : (int) $sessionId; + + $cList = PTestCategory::getCategoryListInfo($this->id); + + $categoryList = []; + foreach ($cList as $item) { + $categoryList[$item->id]['label'] = $item->name; + $categoryList[$item->id]['num'] = 0; + } + + $answerTable = Database::get_course_table(TABLE_QUIZ_ANSWER); + $questionTable = Database::get_course_table(TABLE_QUIZ_QUESTION); + $attemptsTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT); + + // QUESTION_PT_TYPE_CATEGORY_RANKING + $sql = "SELECT an.ptest_category AS id, COUNT(*) AS rep FROM $attemptsTable att + INNER JOIN $answerTable an ON att.answer = an.id + INNER JOIN $questionTable q ON att.question_id = q.id + WHERE + att.exe_id = $exeId AND + att.c_id = $courseId AND + att.session_id = $sessionId AND + q.type = ".QUESTION_PT_TYPE_CATEGORY_RANKING." + GROUP BY an.ptest_category"; + $res = Database::query($sql); + while ($row = Database::fetch_assoc($res)) { + $categoryList[$row['id']]['num'] = $row['rep']; + } + + // QUESTION_PT_TYPE_AGREE_OR_DISAGREE + $sql = "SELECT att.answer AS answer FROM $attemptsTable att + INNER JOIN $questionTable q ON att.question_id = q.id + WHERE + att.exe_id = $exeId AND + att.c_id = $courseId AND + att.session_id = $sessionId AND + q.type = ".QUESTION_PT_TYPE_AGREE_OR_DISAGREE; + $res = Database::query($sql); + while ($row = Database::fetch_assoc($res)) { + $answer = explode(',', $row['answer']); + // ANSWER_AGREE + $sql = "SELECT * FROM $answerTable WHERE id = ".$answer[0]; + $result = Database::query($sql); + $object = Database::fetch_object($result); + $ptestCategoryId = $object->ptest_category; + $categoryList[$ptestCategoryId]['num']++; + // ANSWER_DISAGREE + $sql = "SELECT * FROM $answerTable WHERE id = ".$answer[1]; + $result = Database::query($sql); + $object = Database::fetch_object($result); + $ptestCategoryId = $object->ptest_category; + $categoryList[$ptestCategoryId]['num']--; + } + + // QUESTION_PT_TYPE_AGREE_SCALE + $sql = "SELECT att.answer AS answer FROM $attemptsTable att + INNER JOIN $questionTable q ON att.question_id = q.id + WHERE + att.exe_id = $exeId AND + att.c_id = $courseId AND + att.session_id = $sessionId AND + q.type = ".QUESTION_PT_TYPE_AGREE_SCALE; + $res = Database::query($sql); + while ($row = Database::fetch_assoc($res)) { + $answer = json_decode($row['answer'], 1); + foreach ($answer as $key => $value) { + $sql = "SELECT * FROM $answerTable WHERE id = ".$key; + $result = Database::query($sql); + $object = Database::fetch_object($result); + $ptestCategoryId = $object->ptest_category; + $categoryList[$ptestCategoryId]['num'] += $value; + } + } + + // QUESTION_PT_TYPE_AGREE_REORDER + $sql = "SELECT att.answer AS answer FROM $attemptsTable att + INNER JOIN $questionTable q ON att.question_id = q.id + WHERE + att.exe_id = $exeId AND + att.c_id = $courseId AND + att.session_id = $sessionId AND + q.type = ".QUESTION_PT_TYPE_AGREE_REORDER; + $res = Database::query($sql); + while ($row = Database::fetch_assoc($res)) { + $answer = json_decode($row['answer'], 1); + foreach ($answer as $key => $value) { + $sql = "SELECT * FROM $answerTable WHERE id = ".$value; + $result = Database::query($sql); + $object = Database::fetch_object($result); + $ptestCategoryId = $object->ptest_category; + $categoryList[$ptestCategoryId]['num'] += $key; + } + } + + $labels = []; + $num = []; + foreach ($categoryList as $item) { + $labels[] = $item['label']; + $data[] = (int) $item['num']; + } + + $html = ''; + $html .= '
'; + $html .= '
'; + $html .= ''; + $html .= '
'; + $html .= '
'; + $html .= ''; + $html .= '
'; + $html .= '

'; + + $html .= ''; + + return $html; + } + /** * Returns the exercise result. * @@ -8084,6 +8779,10 @@ public function showExpectedChoiceColumn() return false; } + if ($this->pt_type == EXERCISE_PT_TYPE_PTEST) { + return false; + } + return true; } @@ -8726,6 +9425,11 @@ public static function exerciseGrid($categoryId, $keyword = '') $embeddableIcon = Display::return_icon('om_integration.png', get_lang('ThisQuizCanBeEmbeddable')); $url .= Display::div($embeddableIcon, ['class' => 'pull-right']); } + + if ($row['pt_type'] == EXERCISE_PT_TYPE_PTEST) { + $embeddableIcon = Display::return_icon('new_personality_test.png', get_lang('PtestType')); + $url .= Display::div($embeddableIcon, ['class' => 'pull-right']); + } $currentRow['title'] = $url.' '.$session_img.$lp_blocked; @@ -8753,83 +9457,99 @@ public static function exerciseGrid($categoryId, $keyword = '') } $actions .= $settings; - // Exercise results - $resultsLink = ''. - Display::return_icon('test_results.png', get_lang('Results'), '', ICON_SIZE_SMALL).''; - - if ($limitTeacherAccess) { - if (api_is_platform_admin()) { + if ($exercise->pt_type != EXERCISE_PT_TYPE_PTEST) { + // Exercise results + $resultsLink = ''. + Display::return_icon('test_results.png', get_lang('Results'), '', ICON_SIZE_SMALL).''; + + if ($limitTeacherAccess) { + if (api_is_platform_admin()) { + $actions .= $resultsLink; + } + } else { + // Exercise results $actions .= $resultsLink; } - } else { - // Exercise results - $actions .= $resultsLink; - } - - // Auto launch - if ($autoLaunchAvailable) { - $autoLaunch = $exercise->getAutoLaunch(); - if (empty($autoLaunch)) { - $actions .= Display::url( + + // Auto launch + if ($autoLaunchAvailable) { + $autoLaunch = $exercise->getAutoLaunch(); + if (empty($autoLaunch)) { + $actions .= Display::url( + Display::return_icon( + 'launch_na.png', + get_lang('Enable'), + '', + ICON_SIZE_SMALL + ), + 'exercise.php?'.api_get_cidreq().'&choice=enable_launch&sec_token='.$token.'&exerciseId='.$row['id'] + ); + } else { + $actions .= Display::url( + Display::return_icon( + 'launch.png', + get_lang('Disable'), + '', + ICON_SIZE_SMALL + ), + 'exercise.php?'.api_get_cidreq().'&choice=disable_launch&sec_token='.$token.'&exerciseId='.$row['id'] + ); + } + } + + // Export + $actions .= Display::url( + Display::return_icon('cd.png', get_lang('CopyExercise')), + '', + [ + 'onclick' => "javascript:if(!confirm('".addslashes(api_htmlentities(get_lang('AreYouSureToCopy'), ENT_QUOTES, $charset))." ".addslashes($row['title'])."?"."')) return false;", + 'href' => 'exercise.php?'.api_get_cidreq().'&choice=copy_exercise&sec_token='.$token.'&exerciseId='.$row['id'], + ] + ); + + // Clean exercise + if ($locked == false) { + $clean = Display::url( Display::return_icon( - 'launch_na.png', - get_lang('Enable'), + 'clean.png', + get_lang('CleanStudentResults'), '', ICON_SIZE_SMALL ), - 'exercise.php?'.api_get_cidreq().'&choice=enable_launch&sec_token='.$token.'&exerciseId='.$row['id'] + '', + [ + 'onclick' => "javascript:if(!confirm('".addslashes(api_htmlentities(get_lang('AreYouSureToDeleteResults'), ENT_QUOTES, $charset))." ".addslashes($row['title'])."?"."')) return false;", + 'href' => 'exercise.php?'.api_get_cidreq().'&choice=clean_results&sec_token='.$token.'&exerciseId='.$row['id'], + ] ); } else { - $actions .= Display::url( - Display::return_icon( - 'launch.png', - get_lang('Disable'), - '', - ICON_SIZE_SMALL - ), - 'exercise.php?'.api_get_cidreq().'&choice=disable_launch&sec_token='.$token.'&exerciseId='.$row['id'] - ); - } - } - - // Export - $actions .= Display::url( - Display::return_icon('cd.png', get_lang('CopyExercise')), - '', - [ - 'onclick' => "javascript:if(!confirm('".addslashes(api_htmlentities(get_lang('AreYouSureToCopy'), ENT_QUOTES, $charset))." ".addslashes($row['title'])."?"."')) return false;", - 'href' => 'exercise.php?'.api_get_cidreq().'&choice=copy_exercise&sec_token='.$token.'&exerciseId='.$row['id'], - ] - ); - - // Clean exercise - if ($locked == false) { - $clean = Display::url( - Display::return_icon( - 'clean.png', - get_lang('CleanStudentResults'), + $clean = Display::return_icon( + 'clean_na.png', + get_lang('ResourceLockedByGradebook'), '', ICON_SIZE_SMALL - ), - '', - [ - 'onclick' => "javascript:if(!confirm('".addslashes(api_htmlentities(get_lang('AreYouSureToDeleteResults'), ENT_QUOTES, $charset))." ".addslashes($row['title'])."?"."')) return false;", - 'href' => 'exercise.php?'.api_get_cidreq().'&choice=clean_results&sec_token='.$token.'&exerciseId='.$row['id'], - ] - ); + ); + } + + if ($limitTeacherAccess && !api_is_platform_admin()) { + $clean = ''; + } + $actions .= $clean; } else { - $clean = Display::return_icon( - 'clean_na.png', - get_lang('ResourceLockedByGradebook'), - '', - ICON_SIZE_SMALL - ); - } - - if ($limitTeacherAccess && !api_is_platform_admin()) { - $clean = ''; + // Exercise results + $resultsLink = ''. + Display::return_icon('test_results.png', get_lang('Results'), '', ICON_SIZE_SMALL).''; + + if ($limitTeacherAccess) { + if (api_is_platform_admin()) { + $actions .= $resultsLink; + } + } else { + // Exercise results + $actions .= $resultsLink; + } } - $actions .= $clean; + // Visible / invisible // Check if this exercise was added in a LP if ($exercise->exercise_was_added_in_lp == true) { @@ -8870,22 +9590,24 @@ public static function exerciseGrid($categoryId, $keyword = '') $actions .= $visibility; - // Export qti ... - $export = Display::url( - Display::return_icon( - 'export_qti2.png', - 'IMS/QTI', - '', - ICON_SIZE_SMALL - ), - 'exercise.php?action=exportqti2&exerciseId='.$row['id'].'&'.api_get_cidreq() - ); - - if ($limitTeacherAccess && !api_is_platform_admin()) { - $export = ''; + if ($exercise->pt_type != EXERCISE_PT_TYPE_PTEST) { + // Export qti ... + $export = Display::url( + Display::return_icon( + 'export_qti2.png', + 'IMS/QTI', + '', + ICON_SIZE_SMALL + ), + 'exercise.php?action=exportqti2&exerciseId='.$row['id'].'&'.api_get_cidreq() + ); + + if ($limitTeacherAccess && !api_is_platform_admin()) { + $export = ''; + } + + $actions .= $export; } - - $actions .= $export; } else { // not session $actions = Display::return_icon( @@ -9141,6 +9863,9 @@ public static function exerciseGrid($categoryId, $keyword = '') $attempt_text = get_lang('NotAttempted'); } } + if ($my_result_disabled == RESULT_DISABLE_PT_TYPE_PTEST) { + $attempt_text = $num.' '.get_lang('Attempts'); + } } } diff --git a/main/exercise/exercise.php b/main/exercise/exercise.php index dadeab4ec3f..0baefd1c7aa 100644 --- a/main/exercise/exercise.php +++ b/main/exercise/exercise.php @@ -525,7 +525,8 @@ Display::return_icon('new_exercice.png', get_lang('NewEx'), '', ICON_SIZE_MEDIUM).''; $actionsLeft .= ''. Display::return_icon('new_question.png', get_lang('AddQ'), '', ICON_SIZE_MEDIUM).''; - + $actionsLeft .= ''. + Display::return_icon('new_personality_test.png', get_lang('AddPT'), '', ICON_SIZE_MEDIUM).''; if (api_get_configuration_value('allow_exercise_categories')) { $actionsLeft .= ''; $actionsLeft .= Display::return_icon('folder.png', get_lang('Category'), '', ICON_SIZE_MEDIUM); diff --git a/main/exercise/exercise_admin.php b/main/exercise/exercise_admin.php index 8b4bd361def..bd2b9a9464a 100755 --- a/main/exercise/exercise_admin.php +++ b/main/exercise/exercise_admin.php @@ -130,6 +130,13 @@ function setFocus(){ $course_id = api_get_course_int_id(); //INIT FORM +$ptest = false; +$ptestUrl = ''; +if (!empty($_GET['ptest'])) { + $ptest = true; + $ptestUrl = '&ptest=1'; +} + if (isset($_GET['exerciseId'])) { $form = new FormValidator( 'exercise_admin', @@ -142,12 +149,16 @@ function setFocus(){ $form = new FormValidator( 'exercise_admin', 'post', - api_get_self().'?'.api_get_cidreq() + api_get_self().'?'.api_get_cidreq().$ptestUrl ); $form->addElement('hidden', 'edit', 'false'); } -$objExercise->createForm($form); +if ($ptest || $objExercise->selectPtType() == EXERCISE_PT_TYPE_PTEST) { + $objExercise->createForm($form, 'ptest'); +} else { + $objExercise->createForm($form); +} // VALIDATE FORM if ($form->validate()) { diff --git a/main/exercise/exercise_result.php b/main/exercise/exercise_result.php index 9249081f8f0..c28a500f7ca 100755 --- a/main/exercise/exercise_result.php +++ b/main/exercise/exercise_result.php @@ -71,6 +71,7 @@ $htmlHeadXtra[] = ''; $htmlHeadXtra[] = ''; $htmlHeadXtra[] = ''; +$htmlHeadXtra[] = api_get_js('chartjs/Chart.min.js'); if (api_get_configuration_value('quiz_prevent_copy_paste')) { $htmlHeadXtra[] = ''; } diff --git a/main/exercise/exercise_show.php b/main/exercise/exercise_show.php index bc24988aa18..9a2f46194fc 100755 --- a/main/exercise/exercise_show.php +++ b/main/exercise/exercise_show.php @@ -498,7 +498,7 @@ function getFCK(vals, marksid) { $(function() { new HotspotQuestion({ questionId: $questionId, - exerciseId: {$objExercise->id}, + exerciseId: {$objExercise->id}, exeId: $id, selector: '#hotspot-solution-$questionId-$id', for: 'solution', diff --git a/main/exercise/exercise_submit.php b/main/exercise/exercise_submit.php index 997b3114756..f08d96b7419 100755 --- a/main/exercise/exercise_submit.php +++ b/main/exercise/exercise_submit.php @@ -168,6 +168,10 @@ exit; } +$ptest = false; +if ($objExercise->selectPtType() == EXERCISE_PT_TYPE_PTEST) { + $ptest = true; +} // if the user has submitted the form $exercise_title = $objExercise->selectTitle(); $exercise_sound = $objExercise->selectSound(); @@ -1050,7 +1054,7 @@ if (!empty($questionList)) { foreach ($questionList as $questionId) { $i++; - $objQuestionTmp = Question::read($questionId); + $objQuestionTmp = Question::read($questionId, null, true, $ptest); // for sequential exercises if ($objExercise->type == ONE_PER_PAGE) { @@ -1097,7 +1101,7 @@ function addExerciseEvent(elm, evType, fn, useCapture) { return; } - var calledUpdateDuration = false; + var calledUpdateDuration = false; function updateDuration() { if (calledUpdateDuration === false) { var saveDurationUrl = "'.$saveDurationUrl.'"; @@ -1113,7 +1117,7 @@ function updateDuration() { return; } } - + $(function() { //This pre-load the save.png icon var saveImage = new Image(); @@ -1123,14 +1127,14 @@ function updateDuration() { $(".block_on_enter").keypress(function(event) { return event.keyCode != 13; }); - + $(".checkCalculatedQuestionOnEnter").keypress(function(event) { if (event.keyCode === 13) { event.preventDefault(); var id = $(this).attr("id"); var parts = id.split("_"); var buttonId = "button_" + parts[1]; - document.getElementById(buttonId).click(); + document.getElementById(buttonId).click(); } }); @@ -1154,7 +1158,7 @@ function updateDuration() { });*/ $("form#exercise_form").prepend($("#exercise-description")); - + $(\'button[name="previous_question_and_save"]\').on("touchstart click", function (e) { e.preventDefault(); e.stopPropagation(); @@ -1195,8 +1199,8 @@ function updateDuration() { }); // Save attempt duration - addExerciseEvent(window, \'unload\', updateDuration , false); - addExerciseEvent(window, \'beforeunload\', updateDuration , false); + addExerciseEvent(window, \'unload\', updateDuration , false); + addExerciseEvent(window, \'beforeunload\', updateDuration , false); }); function previous_question(question_num) { @@ -1244,6 +1248,16 @@ function save_now(question_id, url_extra, validate) { // 4. choice for degree of certainty var my_choiceDc = $(\'*[name*="choiceDegreeCertainty[\'+question_id+\']"]\').serialize(); + // 5. Agree o Disagree choice inputs + var my_choice_agree = $(\'*[name*="choice-agree[\'+question_id+\']"]\').serialize(); + var my_choice_disagree = $(\'*[name*="choice-disagree[\'+question_id+\']"]\').serialize(); + + // 6. Agree scale choice inputs + var my_choice_agree_scale = $(\'*[name*="choice[\'+question_id+\']"]\').serialize(); + + // 7. Agree reorder choice inputs + var my_choice_agree_reorder = $(\'*[name*="choice[\'+question_id+\']"]\').serialize(); + // Checking CkEditor if (question_id) { if (CKEDITOR.instances["choice["+question_id+"]"]) { @@ -1253,7 +1267,7 @@ function save_now(question_id, url_extra, validate) { my_choice = $.param(my_choice); } } - + if ($(\'input[name="remind_list[\'+question_id+\']"]\').is(\':checked\')) { $("#question_div_"+question_id).addClass("remind_highlight"); } else { @@ -1262,8 +1276,8 @@ function save_now(question_id, url_extra, validate) { // Only for the first time var dataparam = "'.$params.'&type=simple&question_id="+question_id; - dataparam += "&"+my_choice+"&"+hotspot+"&"+remind_list+"&"+my_choiceDc; - + dataparam += "&"+my_choice+"&"+hotspot+"&"+remind_list+"&"+my_choiceDc+"&"+my_choice_agree+"&"+my_choice_disagree+"&"+my_choice_agree_scale+"&"+my_choice_agree_reorder; + $("#save_for_now_"+question_id).html(\''. Display::returnFontAwesomeIcon('spinner', null, true, 'fa-spin').'\'); $.ajax({ diff --git a/main/exercise/overview.php b/main/exercise/overview.php index 45acaf451fc..3b6aafd6768 100755 --- a/main/exercise/overview.php +++ b/main/exercise/overview.php @@ -96,11 +96,19 @@ 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'), [], ICON_SIZE_SMALL), - api_get_path(WEB_CODE_PATH).'exercise/exercise_report.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id, - ['title' => get_lang('Results')] - ); + if ($objExercise->selectPtType() == EXERCISE_PT_TYPE_PTEST) { + $editLink .= Display::url( + Display::return_icon('test_results.png', get_lang('Results'), [], ICON_SIZE_SMALL), + api_get_path(WEB_CODE_PATH).'exercise/ptest_exercise_report.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id, + ['title' => get_lang('Results')] + ); + } else { + $editLink .= Display::url( + Display::return_icon('test_results.png', get_lang('Results'), [], ICON_SIZE_SMALL), + api_get_path(WEB_CODE_PATH).'exercise/exercise_report.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id, + ['title' => get_lang('Results')] + ); + } } $iconExercise = Display::return_icon('test-quiz.png', null, [], ICON_SIZE_MEDIUM); @@ -244,6 +252,9 @@ if ($attempt_result['attempt_revised'] == 0) { $teacher_revised = Display::label(get_lang('NotValidated'), 'info'); } + if ($objExercise->results_disabled == RESULT_DISABLE_PT_TYPE_PTEST) { + $teacher_revised = ''; + } $row = [ 'count' => $i, 'date' => api_convert_and_format_date( @@ -280,6 +291,7 @@ RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK, RESULT_DISABLE_RANKING, RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER, + RESULT_DISABLE_PT_TYPE_PTEST, ] ) || ( $objExercise->results_disabled == RESULT_DISABLE_SHOW_SCORE_ONLY && @@ -369,6 +381,9 @@ ]; } break; + case RESULT_DISABLE_PT_TYPE_PTEST: + $header_names = [get_lang('Attempt'), get_lang('StartDate'), get_lang('IP'), get_lang('Details')]; + break; } $column = 0; foreach ($header_names as $item) { diff --git a/main/exercise/ptest_admin.php b/main/exercise/ptest_admin.php new file mode 100644 index 00000000000..96687807163 --- /dev/null +++ b/main/exercise/ptest_admin.php @@ -0,0 +1,389 @@ + $val) { + if (is_string($val)) { + $_POST[$key] = stripslashes($val); + } elseif (is_array($val)) { + foreach ($val as $key2 => $val2) { + $_POST[$key][$key2] = stripslashes($val2); + } + } + $GLOBALS[$key] = $_POST[$key]; + } +} + +$newQuestion = isset($_GET['newQuestion']) ? $_GET['newQuestion'] : 0; +$modifyAnswers = isset($_GET['modifyAnswers']) ? $_GET['modifyAnswers'] : 0; +$editQuestion = isset($_GET['editQuestion']) ? $_GET['editQuestion'] : 0; +$page = isset($_GET['page']) && !empty($_GET['page']) ? (int) $_GET['page'] : 1; +$modifyQuestion = isset($_GET['modifyQuestion']) ? $_GET['modifyQuestion'] : 0; +$deleteQuestion = isset($_GET['deleteQuestion']) ? $_GET['deleteQuestion'] : 0; +if (empty($questionId)) { + $questionId = Session::read('questionId'); +} +if (empty($modifyExercise)) { + $modifyExercise = isset($_GET['modifyExercise']) ? $_GET['modifyExercise'] : null; +} + +$fromExercise = isset($fromExercise) ? $fromExercise : null; +$cancelExercise = isset($cancelExercise) ? $cancelExercise : null; +$cancelAnswers = isset($cancelAnswers) ? $cancelAnswers : null; +$modifyIn = isset($modifyIn) ? $modifyIn : null; +$cancelQuestion = isset($cancelQuestion) ? $cancelQuestion : null; + +/* Cleaning all incomplete attempts of the admin/teacher to avoid weird problems + when changing the exercise settings, number of questions, etc */ +Event::delete_all_incomplete_attempts( + api_get_user_id(), + $exerciseId, + api_get_course_int_id(), + api_get_session_id() +); + +// get from session +$objExercise = Session::read('objExercise'); +$objQuestion = Session::read('objQuestion'); + +if (isset($_REQUEST['convertAnswer'])) { + $objQuestion = $objQuestion->swapSimpleAnswerTypes(); + Session::write('objQuestion', $objQuestion); +} +$objAnswer = Session::read('objAnswer'); +$_course = api_get_course_info(); + +// document path +$documentPath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'; + +// picture path +$picturePath = $documentPath.'/images'; + +// audio path +$audioPath = $documentPath.'/audio'; + +// tables used in the exercise tool +if (!empty($_GET['action']) && $_GET['action'] == 'exportqti2' && !empty($_GET['questionId'])) { + require_once 'export/qti2/qti2_export.php'; + $export = export_question_qti($_GET['questionId'], true); + $qid = (int) $_GET['questionId']; + $archive_path = api_get_path(SYS_ARCHIVE_PATH); + $temp_dir_short = uniqid(); + $temp_zip_dir = $archive_path."/".$temp_dir_short; + if (!is_dir($temp_zip_dir)) { + mkdir($temp_zip_dir, api_get_permissions_for_new_directories()); + } + $temp_zip_file = $temp_zip_dir."/".api_get_unique_id().".zip"; + $temp_xml_file = $temp_zip_dir."/qti2export_".$qid.'.xml'; + file_put_contents($temp_xml_file, $export); + $zip_folder = new PclZip($temp_zip_file); + $zip_folder->add($temp_xml_file, PCLZIP_OPT_REMOVE_ALL_PATH); + $name = 'qti2_export_'.$qid.'.zip'; + + DocumentManager::file_send_for_download($temp_zip_file, true, $name); + unlink($temp_zip_file); + unlink($temp_xml_file); + rmdir($temp_zip_dir); + exit; //otherwise following clicks may become buggy +} + +// Exercise object creation. +if (!is_object($objExercise) || $objExercise->selectPtType() != EXERCISE_PT_TYPE_PTEST) { + // construction of the Exercise object + $objExercise = new Exercise(); + + // creation of a new exercise if wrong or not specified exercise ID + if ($exerciseId) { + $parseQuestionList = $showPagination > 0 ? false : true; + if ($editQuestion) { + $parseQuestionList = false; + $showPagination = true; + } + $objExercise->read($exerciseId, $parseQuestionList); + } + // saves the object into the session + Session::write('objExercise', $objExercise); +} + +// Exercise can be edited in their course. +if ($objExercise->sessionId != $sessionId) { + api_not_allowed(true); +} + +// 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()) { + $modifyExercise = 'yes'; + } +} + +$nbrQuestions = $objExercise->getQuestionCount(); + +// Question object creation. +if ($editQuestion || $newQuestion || $modifyQuestion || $modifyAnswers) { + if ($editQuestion || $newQuestion) { + // reads question data + if ($editQuestion) { + // question not found + if (!$objQuestion = Question::read($editQuestion, null, true, true)) { + api_not_allowed(true); + } + // saves the object into the session + Session::write('objQuestion', $objQuestion); + } + } + + // checks if the object exists + if (is_object($objQuestion)) { + // gets the question ID + $questionId = $objQuestion->selectId(); + } +} + +// if cancelling an exercise +if ($cancelExercise) { + // existing exercise + if ($exerciseId) { + unset($modifyExercise); + } else { + // new exercise + // goes back to the exercise list + header('Location: '.api_get_path(WEB_CODE_PATH).'exercise/exercise.php?'.api_get_cidreq()); + exit(); + } +} + +// if cancelling question creation/modification +if ($cancelQuestion) { + // if we are creating a new question from the question pool + if (!$exerciseId && !$questionId) { + // goes back to the question pool + header('Location: question_pool.php?'.api_get_cidreq()); + exit(); + } else { + // goes back to the question viewing + $editQuestion = $modifyQuestion; + unset($newQuestion, $modifyQuestion); + } +} + +// if cancelling answer creation/modification +if ($cancelAnswers) { + // goes back to the question viewing + $editQuestion = $modifyAnswers; + unset($modifyAnswers); +} + +$nameTools = ''; +// modifies the query string that is used in the link of tool name +if ($editQuestion || $modifyQuestion || $newQuestion || $modifyAnswers) { + $nameTools = get_lang('QuestionManagement'); +} + +if (api_is_in_gradebook()) { + $interbreadcrumb[] = [ + 'url' => Category::getUrl(), + 'name' => get_lang('ToolGradebook'), + ]; +} + +$interbreadcrumb[] = ['url' => 'exercise.php?'.api_get_cidreq(), 'name' => get_lang('Exercises')]; +if (isset($_GET['newQuestion']) || isset($_GET['editQuestion'])) { + $interbreadcrumb[] = [ + 'url' => 'admin.php?exerciseId='.$objExercise->id.'&'.api_get_cidreq(), + 'name' => $objExercise->selectTitle(true), + ]; +} else { + $interbreadcrumb[] = [ + 'url' => '#', + 'name' => $objExercise->selectTitle(true), + ]; +} + +// if the question is duplicated, disable the link of tool name +if ($modifyIn === 'thisExercise') { + if ($buttonBack) { + $modifyIn = 'allExercises'; + } +} + +$htmlHeadXtra[] = api_get_js('jqueryui-touch-punch/jquery.ui.touch-punch.min.js'); +$htmlHeadXtra[] = api_get_js('jquery.jsPlumb.all.js'); + +$template = new Template(); +$templateName = $template->get_template('exercise/submit.js.tpl'); +$htmlHeadXtra[] = $template->fetch($templateName); +$htmlHeadXtra[] = api_get_js('d3/jquery.xcolor.js'); +$htmlHeadXtra[] = ''; +$htmlHeadXtra[] = ''; + +if (isset($_GET['message'])) { + if (in_array($_GET['message'], ['ExerciseStored', 'ItemUpdated', 'ItemAdded'])) { + Display::addFlash(Display::return_message(get_lang($_GET['message']), 'confirmation')); + } +} + +Display::display_header($nameTools, 'Exercise'); + +// If we are in a test +$inATest = isset($exerciseId) && $exerciseId > 0; + +if ($inATest) { + echo '
'; + if (isset($_GET['hotspotadmin']) || isset($_GET['newQuestion'])) { + echo ''. + Display::return_icon('back.png', get_lang('GoBackToQuestionList'), '', ICON_SIZE_MEDIUM).''; + } + + echo ''. + Display::return_icon('preview_view.png', get_lang('Preview'), '', ICON_SIZE_MEDIUM).''; + echo Display::url( + Display::return_icon('test_results.png', get_lang('Results'), '', ICON_SIZE_MEDIUM), + api_get_path(WEB_CODE_PATH).'exercise/exercise_report.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id + ); + echo ''. + Display::return_icon('settings.png', get_lang('ModifyExercise'), '', ICON_SIZE_MEDIUM).''; + echo '
'; + if ($objExercise->added_in_lp()) { + echo Display::return_message(get_lang('AddedToLPCannotBeAccessed'), 'warning'); + } + if ($editQuestion && $objQuestion->existsInAnotherExercise()) { + echo Display::return_message( + Display::returnFontAwesomeIcon('exclamation-triangle"') + .get_lang('ThisQuestionExistsInAnotherExercisesWarning'), + 'warning', + false + ); + } + +} elseif (isset($_GET['newQuestion'])) { + // we are in create a new question from question pool not in a test + echo ''; +} else { + // If we are in question_pool but not in an test, go back to question create in pool + echo ''; +} + +if ($newQuestion || $editQuestion) { + // Question management + $type = isset($_REQUEST['answerType']) ? Security::remove_XSS($_REQUEST['answerType']) : null; + echo ''; + + if ($newQuestion === 'yes') { + $objExercise->edit_exercise_in_lp = true; + require 'question_ptest_admin.inc.php'; + } + if ($editQuestion) { + // Question preview if teacher clicked the "switch to student" + if ($studentViewActive && $is_allowedToEdit) { + echo '
'; + echo Display::div($objQuestion->selectTitle(), ['class' => 'question_title']); + ExerciseLib::showQuestion( + $objExercise, + $editQuestion, + false, + null, + null, + false, + true, + false, + true, + true + ); + echo '
'; + } else { + require 'question_ptest_admin.inc.php'; + } + } +} + +if (!$newQuestion && !$modifyQuestion && !$editQuestion && !isset($_GET['hotspotadmin'])) { + // question list management + require 'question_list_ptest_admin.inc.php'; +} + +// if we are in question authoring, display warning to user is feedback not shown at the end of the test -ref #6619 +// this test to display only message in the question authoring page and not in the question list page too +if ($objExercise->getFeedbackType() == EXERCISE_FEEDBACK_TYPE_EXAM) { + echo Display::return_message(get_lang('TestFeedbackNotShown'), 'normal'); +} + +Session::write('objExercise', $objExercise); +Session::write('objQuestion', $objQuestion); +Session::write('objAnswer', $objAnswer); +Display::display_footer(); diff --git a/main/exercise/ptest_agree_disagree.class.php b/main/exercise/ptest_agree_disagree.class.php new file mode 100644 index 00000000000..bb715af25c3 --- /dev/null +++ b/main/exercise/ptest_agree_disagree.class.php @@ -0,0 +1,293 @@ +type = QUESTION_PT_TYPE_AGREE_OR_DISAGREE; + $this->isContent = $this->getIsContent(); + } + + /** + * {@inheritdoc} + */ + public function createAnswersForm($form) + { + // Getting the exercise list + /** @var Exercise $obj_ex */ + $obj_ex = Session::read('objExercise'); + + $editor_config = [ + 'ToolbarSet' => 'TestProposedAnswer', + 'Width' => '100%', + 'Height' => '125', + ]; + + // Categories options select + $category = new PTestCategory(); + $categoriesList = $category->getCategoryListInfo($obj_ex->selectId()); + $categoriesOptions = [null => get_lang('None')]; + foreach ($categoriesList as $categoryItem) { + $categoriesOptions[$categoryItem->id] = (string) $categoryItem->name; + } + + //this line defines how many questions by default appear when creating a choice question + // The previous default value was 2. See task #1759. + $nb_answers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : count($categoriesList); + $nb_answers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0)); + + $html = ' + + + + + + + + '; + + $form->addHeader(get_lang('Answers')); + $form->addHtml($html); + + $defaults = []; + if (!empty($this->id)) { + $answer = new Answer($this->id); + $answer->read(); + if ($answer->nbrAnswers > 0 && !$form->isSubmitted()) { + $nb_answers = $answer->nbrAnswers; + } + } + $form->addElement('hidden', 'nb_answers'); + + //$temp_scenario = []; + if ($nb_answers < 1) { + $nb_answers = 1; + echo Display::return_message( + get_lang('YouHaveToCreateAtLeastOneAnswer') + ); + } + + for ($i = 1; $i <= $nb_answers; $i++) { + $form->addHtml(''); + if (isset($answer) && is_object($answer)) { + $defaults['answer['.$i.']'] = isset($answer->answer[$i]) ? $answer->answer[$i] : ''; + $defaults['ptest_category['.$i.']'] = isset($answer->ptest_category[$i]) ? $answer->ptest_category[$i] : 0; + } + + $renderer = $form->defaultRenderer(); + $renderer->setElementTemplate( + '', + 'counter['.$i.']' + ); + $renderer->setElementTemplate( + '', + 'answer['.$i.']' + ); + $renderer->setElementTemplate( + '', + 'ptest_category['.$i.']' + ); + $answer_number = $form->addElement( + 'text', + 'counter['.$i.']', + null, + ' value = "'.$i.'"' + ); + $answer_number->freeze(); + + $form->addHtmlEditor('answer['.$i.']', null, null, false, $editor_config); + + $form->addRule( + 'answer['.$i.']', + get_lang('ThisFieldIsRequired'), + 'required' + ); + + $form->addSelect( + 'ptest_category['.$i.']', + null, + $categoriesOptions + ); + + $form->addHtml(''); + } + + $form->addHtml(''); + $form->addHtml('
'.get_lang('Number').''.get_lang('Answer').''.get_lang('PtestCategory').'
{error}
{element}
{error}
{element}
{error}
{element}
'); + + global $text; + $buttonGroup = []; + + if ($obj_ex->edit_exercise_in_lp == true || + (empty($this->exerciseList) && empty($obj_ex->id)) + ) { + //setting the save button here and not in the question class.php + $buttonGroup[] = $form->addButtonDelete(get_lang('LessAnswer'), 'lessAnswers', true); + $buttonGroup[] = $form->addButtonCreate(get_lang('PlusAnswer'), 'moreAnswers', true); + $buttonGroup[] = $form->addButton( + 'submitQuestion', + $text, + 'check', + 'primary', + 'default', + null, + ['id' => 'submit-question'], + true + ); + $form->addGroup($buttonGroup); + } + + if (!empty($this->id)) { + $form->setDefaults($defaults); + } else { + if ($this->isContent == 1) { + // Default sample content. + $form->setDefaults($defaults); + } + } + $form->setConstants(['nb_answers' => $nb_answers]); + } + + /** + * {@inheritdoc} + */ + public function processAnswersCreation($form, $exercise) + { + $objAnswer = new Answer($this->id); + $nb_answers = $form->getSubmitValue('nb_answers'); + + for ($i = 1; $i <= $nb_answers; $i++) { + $answer = trim($form->getSubmitValue('answer['.$i.']')); + $goodAnswer = false; + $comment = ''; + $weighting = 0; + $ptestCategory = (int) $form->getSubmitValue('ptest_category['.$i.']'); + $dest = ''; + + $objAnswer->createAnswer( + $answer, + $goodAnswer, + $comment, + $weighting, + $i, + null, + null, + $dest, + $ptestCategory + ); + } + + // saves the answers into the data base + $objAnswer->save(); + + $this->save($exercise); + } + + /** + * {@inheritdoc} + */ + public function return_header(Exercise $exercise, $counter = null, $score = []) + { + $header = parent::return_header($exercise, $counter); //, $score); + $header .= ''; + + $header .= ''; + $header .= ''; + $header .= ''; + + return $header; + } + + /** + * Saves one answer to the database. + * + * @param int $id The ID of the answer (has to be calculated for this course) + * @param int $question_id The question ID (to which the answer is attached) + * @param string $title The text of the answer + * @param string $comment The feedback for the answer + * @param float $score The score you get when picking this answer + * @param int $correct Whether this answer is considered *the* correct one (this is the unique answer type) + */ + public function addAnswer( + $id, + $question_id, + $title, + $comment, + $score = 0.0, + $correct = 0 + ) { + $em = Database::getManager(); + $tbl_quiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER); + $tbl_quiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION); + $course_id = api_get_course_int_id(); + $question_id = intval($question_id); + $score = floatval($score); + $correct = intval($correct); + $title = Database::escape_string($title); + $comment = Database::escape_string($comment); + // Get the max position. + $sql = "SELECT max(position) as max_position + FROM $tbl_quiz_answer + WHERE + c_id = $course_id AND + question_id = $question_id"; + $rs_max = Database::query($sql); + $row_max = Database::fetch_object($rs_max); + $position = $row_max->max_position + 1; + + // Insert a new answer + $quizAnswer = new CQuizAnswer(); + $quizAnswer + ->setCId($course_id) + ->setId($id) + ->setQuestionId($question_id) + ->setAnswer($title) + ->setCorrect($correct) + ->setComment($comment) + ->setPonderation($score) + ->setPosition($position) + ->setDestination('0@@0@@0@@0'); + + $em->persist($quizAnswer); + $em->flush(); + + $id = $quizAnswer->getIid(); + + if ($id) { + $quizAnswer + ->setId($id); + + $em->merge($quizAnswer); + $em->flush(); + } + + if ($correct) { + $sql = "UPDATE $tbl_quiz_question + SET ponderation = (ponderation + $score) + WHERE c_id = $course_id AND id = ".$question_id; + Database::query($sql); + } + } +} diff --git a/main/exercise/ptest_agree_reorder.class.php b/main/exercise/ptest_agree_reorder.class.php new file mode 100644 index 00000000000..d7556ef6fee --- /dev/null +++ b/main/exercise/ptest_agree_reorder.class.php @@ -0,0 +1,293 @@ +type = QUESTION_PT_TYPE_AGREE_REORDER; + $this->isContent = $this->getIsContent(); + } + + /** + * {@inheritdoc} + */ + public function createAnswersForm($form) + { + // Getting the exercise list + /** @var Exercise $obj_ex */ + $obj_ex = Session::read('objExercise'); + + $editor_config = [ + 'ToolbarSet' => 'TestProposedAnswer', + 'Width' => '100%', + 'Height' => '125', + ]; + + // Categories options select + $category = new PTestCategory(); + $categoriesList = $category->getCategoryListInfo($obj_ex->selectId()); + $categoriesOptions = [null => get_lang('None')]; + foreach ($categoriesList as $categoryItem) { + $categoriesOptions[$categoryItem->id] = (string) $categoryItem->name; + } + + //this line defines how many questions by default appear when creating a choice question + // The previous default value was 2. See task #1759. + $nb_answers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : count($categoriesList); + $nb_answers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0)); + + $html = '
'.get_lang('Choice').''.get_lang('Answer').'
+ + + + + + + + '; + + $form->addHeader(get_lang('Answers')); + $form->addHtml($html); + + $defaults = []; + if (!empty($this->id)) { + $answer = new Answer($this->id); + $answer->read(); + if ($answer->nbrAnswers > 0 && !$form->isSubmitted()) { + $nb_answers = $answer->nbrAnswers; + } + } + $form->addElement('hidden', 'nb_answers'); + + //$temp_scenario = []; + if ($nb_answers < 1) { + $nb_answers = 1; + echo Display::return_message( + get_lang('YouHaveToCreateAtLeastOneAnswer') + ); + } + + for ($i = 1; $i <= $nb_answers; $i++) { + $form->addHtml(''); + if (isset($answer) && is_object($answer)) { + $defaults['answer['.$i.']'] = isset($answer->answer[$i]) ? $answer->answer[$i] : ''; + $defaults['ptest_category['.$i.']'] = isset($answer->ptest_category[$i]) ? $answer->ptest_category[$i] : 0; + } + + $renderer = $form->defaultRenderer(); + $renderer->setElementTemplate( + '', + 'counter['.$i.']' + ); + $renderer->setElementTemplate( + '', + 'answer['.$i.']' + ); + $renderer->setElementTemplate( + '', + 'ptest_category['.$i.']' + ); + $answer_number = $form->addElement( + 'text', + 'counter['.$i.']', + null, + ' value = "'.$i.'"' + ); + $answer_number->freeze(); + + $form->addHtmlEditor('answer['.$i.']', null, null, false, $editor_config); + + $form->addRule( + 'answer['.$i.']', + get_lang('ThisFieldIsRequired'), + 'required' + ); + + $form->addSelect( + 'ptest_category['.$i.']', + null, + $categoriesOptions + ); + + $form->addHtml(''); + } + + $form->addHtml(''); + $form->addHtml('
'.get_lang('Number').''.get_lang('Answer').''.get_lang('PtestCategory').'
{error}
{element}
{error}
{element}
{error}
{element}
'); + + global $text; + $buttonGroup = []; + + if ($obj_ex->edit_exercise_in_lp == true || + (empty($this->exerciseList) && empty($obj_ex->id)) + ) { + //setting the save button here and not in the question class.php + $buttonGroup[] = $form->addButtonDelete(get_lang('LessAnswer'), 'lessAnswers', true); + $buttonGroup[] = $form->addButtonCreate(get_lang('PlusAnswer'), 'moreAnswers', true); + $buttonGroup[] = $form->addButton( + 'submitQuestion', + $text, + 'check', + 'primary', + 'default', + null, + ['id' => 'submit-question'], + true + ); + $form->addGroup($buttonGroup); + } + + if (!empty($this->id)) { + $form->setDefaults($defaults); + } else { + if ($this->isContent == 1) { + // Default sample content. + $form->setDefaults($defaults); + } + } + $form->setConstants(['nb_answers' => $nb_answers]); + } + + /** + * {@inheritdoc} + */ + public function processAnswersCreation($form, $exercise) + { + $objAnswer = new Answer($this->id); + $nb_answers = $form->getSubmitValue('nb_answers'); + + for ($i = 1; $i <= $nb_answers; $i++) { + $answer = trim($form->getSubmitValue('answer['.$i.']')); + $goodAnswer = false; + $comment = ''; + $weighting = 0; + $ptestCategory = (int) $form->getSubmitValue('ptest_category['.$i.']'); + $dest = ''; + + $objAnswer->createAnswer( + $answer, + $goodAnswer, + $comment, + $weighting, + $i, + null, + null, + $dest, + $ptestCategory + ); + } + + // saves the answers into the data base + $objAnswer->save(); + + $this->save($exercise); + } + + /** + * {@inheritdoc} + */ + public function return_header(Exercise $exercise, $counter = null, $score = []) + { + $header = parent::return_header($exercise, $counter); //, $score); + $header .= ''; + + $header .= ''; + $header .= ''; + $header .= ''; + + return $header; + } + + /** + * Saves one answer to the database. + * + * @param int $id The ID of the answer (has to be calculated for this course) + * @param int $question_id The question ID (to which the answer is attached) + * @param string $title The text of the answer + * @param string $comment The feedback for the answer + * @param float $score The score you get when picking this answer + * @param int $correct Whether this answer is considered *the* correct one (this is the unique answer type) + */ + public function addAnswer( + $id, + $question_id, + $title, + $comment, + $score = 0.0, + $correct = 0 + ) { + $em = Database::getManager(); + $tbl_quiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER); + $tbl_quiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION); + $course_id = api_get_course_int_id(); + $question_id = intval($question_id); + $score = floatval($score); + $correct = intval($correct); + $title = Database::escape_string($title); + $comment = Database::escape_string($comment); + // Get the max position. + $sql = "SELECT max(position) as max_position + FROM $tbl_quiz_answer + WHERE + c_id = $course_id AND + question_id = $question_id"; + $rs_max = Database::query($sql); + $row_max = Database::fetch_object($rs_max); + $position = $row_max->max_position + 1; + + // Insert a new answer + $quizAnswer = new CQuizAnswer(); + $quizAnswer + ->setCId($course_id) + ->setId($id) + ->setQuestionId($question_id) + ->setAnswer($title) + ->setCorrect($correct) + ->setComment($comment) + ->setPonderation($score) + ->setPosition($position) + ->setDestination('0@@0@@0@@0'); + + $em->persist($quizAnswer); + $em->flush(); + + $id = $quizAnswer->getIid(); + + if ($id) { + $quizAnswer + ->setId($id); + + $em->merge($quizAnswer); + $em->flush(); + } + + if ($correct) { + $sql = "UPDATE $tbl_quiz_question + SET ponderation = (ponderation + $score) + WHERE c_id = $course_id AND id = ".$question_id; + Database::query($sql); + } + } +} diff --git a/main/exercise/ptest_agree_scale.class.php b/main/exercise/ptest_agree_scale.class.php new file mode 100644 index 00000000000..64618713629 --- /dev/null +++ b/main/exercise/ptest_agree_scale.class.php @@ -0,0 +1,346 @@ +type = QUESTION_PT_TYPE_AGREE_SCALE; + $this->isContent = $this->getIsContent(); + } + + /** + * {@inheritdoc} + */ + public function createAnswersForm($form) + { + // Getting the exercise list + /** @var Exercise $obj_ex */ + $obj_ex = Session::read('objExercise'); + + $editor_config = [ + 'ToolbarSet' => 'TestProposedAnswer', + 'Width' => '100%', + 'Height' => '125', + ]; + + // Categories options select + $category = new PTestCategory(); + $categoriesList = $category->getCategoryListInfo($obj_ex->selectId()); + $categoriesOptions = [null => get_lang('None')]; + foreach ($categoriesList as $categoryItem) { + $categoriesOptions[$categoryItem->id] = (string) $categoryItem->name; + } + + //this line defines how many questions by default appear when creating a choice question + // The previous default value was 2. See task #1759. + $nb_answers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : count($categoriesList); + $nb_answers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0)); + + $html = '
'.get_lang('Choice').''.get_lang('Answer').'
+ + + + + + + + '; + + $form->addHeader(get_lang('Answers')); + $form->addHtml($html); + + $defaults = []; + if (!empty($this->id)) { + $answer = new Answer($this->id); + $answer->read(); + if ($answer->nbrAnswers > 0 && !$form->isSubmitted()) { + $nb_answers = $answer->nbrAnswers; + } + } + $form->addElement('hidden', 'nb_answers'); + + //$temp_scenario = []; + if ($nb_answers < 1) { + $nb_answers = 1; + echo Display::return_message( + get_lang('YouHaveToCreateAtLeastOneAnswer') + ); + } + + for ($i = 1; $i <= $nb_answers; $i++) { + $form->addHtml(''); + if (isset($answer) && is_object($answer)) { + $defaults['answer['.$i.']'] = isset($answer->answer[$i]) ? $answer->answer[$i] : ''; + $defaults['ptest_category['.$i.']'] = isset($answer->ptest_category[$i]) ? $answer->ptest_category[$i] : 0; + } + + $renderer = $form->defaultRenderer(); + $renderer->setElementTemplate( + '', + 'counter['.$i.']' + ); + $renderer->setElementTemplate( + '', + 'answer['.$i.']' + ); + $renderer->setElementTemplate( + '', + 'ptest_category['.$i.']' + ); + $answer_number = $form->addElement( + 'text', + 'counter['.$i.']', + null, + ' value = "'.$i.'"' + ); + $answer_number->freeze(); + + $form->addHtmlEditor('answer['.$i.']', null, null, false, $editor_config); + + $form->addRule( + 'answer['.$i.']', + get_lang('ThisFieldIsRequired'), + 'required' + ); + + $form->addSelect( + 'ptest_category['.$i.']', + null, + $categoriesOptions + ); + + $form->addHtml(''); + } + + $form->addHtml(''); + $form->addHtml('
'.get_lang('Number').''.get_lang('Answer').''.get_lang('PtestCategory').'
{error}
{element}
{error}
{element}
{error}
{element}
'); + + global $text; + $buttonGroup = []; + + if ($obj_ex->edit_exercise_in_lp == true || + (empty($this->exerciseList) && empty($obj_ex->id)) + ) { + //setting the save button here and not in the question class.php + $buttonGroup[] = $form->addButtonDelete(get_lang('LessAnswer'), 'lessAnswers', true); + $buttonGroup[] = $form->addButtonCreate(get_lang('PlusAnswer'), 'moreAnswers', true); + $buttonGroup[] = $form->addButton( + 'submitQuestion', + $text, + 'check', + 'primary', + 'default', + null, + ['id' => 'submit-question'], + true + ); + $form->addGroup($buttonGroup); + } + + if (!empty($this->id)) { + $form->setDefaults($defaults); + } else { + if ($this->isContent == 1) { + // Default sample content. + $form->setDefaults($defaults); + } + } + $form->setConstants(['nb_answers' => $nb_answers]); + } + + public function setDirectOptions($i, FormValidator $form, $renderer, $select_lp_id, $select_question) + { + $editor_config = [ + 'ToolbarSet' => 'TestProposedAnswer', + 'Width' => '100%', + 'Height' => '125', + ]; + + $form->addHtmlEditor( + 'comment['.$i.']', + null, + null, + false, + $editor_config + ); + // Direct feedback + //Adding extra feedback fields + $group = []; + $group['try'.$i] = $form->createElement( + 'checkbox', + 'try'.$i, + null, + get_lang('TryAgain') + ); + $group['lp'.$i] = $form->createElement( + 'select', + 'lp'.$i, + get_lang('SeeTheory').': ', + $select_lp_id + ); + $group['destination'.$i] = $form->createElement( + 'select', + 'destination'.$i, + get_lang('GoToQuestion').': ', + $select_question + ); + $group['url'.$i] = $form->createElement( + 'text', + 'url'.$i, + get_lang('Other').': ', + [ + 'class' => 'col-md-2', + 'placeholder' => get_lang('Other'), + ] + ); + $form->addGroup($group, 'scenario'); + + $renderer->setElementTemplate( + '{error}
{element}', + 'scenario' + ); + } + + /** + * {@inheritdoc} + */ + public function processAnswersCreation($form, $exercise) + { + $objAnswer = new Answer($this->id); + $nb_answers = $form->getSubmitValue('nb_answers'); + + for ($i = 1; $i <= $nb_answers; $i++) { + $answer = trim($form->getSubmitValue('answer['.$i.']')); + $goodAnswer = false; + $comment = ''; + $weighting = 0; + $ptestCategory = (int) $form->getSubmitValue('ptest_category['.$i.']'); + $dest = ''; + + $objAnswer->createAnswer( + $answer, + $goodAnswer, + $comment, + $weighting, + $i, + null, + null, + $dest, + $ptestCategory + ); + } + + // saves the answers into the data base + $objAnswer->save(); + + $this->save($exercise); + } + + /** + * {@inheritdoc} + */ + public function return_header(Exercise $exercise, $counter = null, $score = []) + { + $header = parent::return_header($exercise, $counter); //, $score); + $header .= ''; + + $header .= ''; + $header .= ''; + $header .= ''; + + return $header; + } + + /** + * Saves one answer to the database. + * + * @param int $id The ID of the answer (has to be calculated for this course) + * @param int $question_id The question ID (to which the answer is attached) + * @param string $title The text of the answer + * @param string $comment The feedback for the answer + * @param float $score The score you get when picking this answer + * @param int $correct Whether this answer is considered *the* correct one (this is the unique answer type) + */ + public function addAnswer( + $id, + $question_id, + $title, + $comment, + $score = 0.0, + $correct = 0 + ) { + $em = Database::getManager(); + $tbl_quiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER); + $tbl_quiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION); + $course_id = api_get_course_int_id(); + $question_id = intval($question_id); + $score = floatval($score); + $correct = intval($correct); + $title = Database::escape_string($title); + $comment = Database::escape_string($comment); + // Get the max position. + $sql = "SELECT max(position) as max_position + FROM $tbl_quiz_answer + WHERE + c_id = $course_id AND + question_id = $question_id"; + $rs_max = Database::query($sql); + $row_max = Database::fetch_object($rs_max); + $position = $row_max->max_position + 1; + + // Insert a new answer + $quizAnswer = new CQuizAnswer(); + $quizAnswer + ->setCId($course_id) + ->setId($id) + ->setQuestionId($question_id) + ->setAnswer($title) + ->setCorrect($correct) + ->setComment($comment) + ->setPonderation($score) + ->setPosition($position) + ->setDestination('0@@0@@0@@0'); + + $em->persist($quizAnswer); + $em->flush(); + + $id = $quizAnswer->getIid(); + + if ($id) { + $quizAnswer + ->setId($id); + + $em->merge($quizAnswer); + $em->flush(); + } + + if ($correct) { + $sql = "UPDATE $tbl_quiz_question + SET ponderation = (ponderation + $score) + WHERE c_id = $course_id AND id = ".$question_id; + Database::query($sql); + } + } +} diff --git a/main/exercise/ptest_category.class.php b/main/exercise/ptest_category.class.php new file mode 100644 index 00000000000..88ac8d738b9 --- /dev/null +++ b/main/exercise/ptest_category.class.php @@ -0,0 +1,1145 @@ +name = ''; + $this->description = ''; + $this->exercise_id = 0; + } + + /** + * return the PTestCategory object with id=in_id. + * + * @param int $id + * @param int $courseId + * + * @return PTestCategory + */ + public function getCategory($id, $courseId = 0) + { + $table = Database::get_course_table(TABLE_QUIZ_CATEGORY_PTEST); + $id = (int) $id; + $exerciseId = (int) $exerciseId; + $courseId = empty($courseId) ? api_get_course_int_id() : (int) $courseId; + $sql = "SELECT * FROM $table + WHERE id = $id AND c_id = ".$courseId; + $res = Database::query($sql); + + if (Database::num_rows($res)) { + $row = Database::fetch_array($res); + + $this->id = $row['id']; + $this->name = $row['title']; + $this->description = $row['description']; + $this->exercise_id = $row['exercise_id']; + + return $this; + } + + return false; + } + + /** + * Save PTestCategory in the database if name doesn't exists. + * + * @param int $exerciseId + * @param int $courseId + * + * @return bool + */ + public function save($exerciseId, $courseId = 0) + { + $exerciseId = (int) $exerciseId; + $courseId = empty($courseId) ? api_get_course_int_id() : (int) $courseId; + $courseInfo = api_get_course_info_by_id($courseId); + if (empty($courseInfo)) { + return false; + } + + $table = Database::get_course_table(TABLE_QUIZ_CATEGORY_PTEST); + + // check if name already exists + $sql = "SELECT count(*) AS nb FROM $table + WHERE + title = '".Database::escape_string($this->name)."' AND + c_id = $courseId AND + exercise_id = $exerciseId"; + $result = Database::query($sql); + $row = Database::fetch_array($result); + // lets add in BDD if not the same name + if ($row['nb'] <= 0) { + $params = [ + 'c_id' => $courseId, + 'exercise_id' => $exerciseId, + 'title' => $this->name, + 'description' => $this->description, + 'session_id' => api_get_session_id(), + ]; + $newId = Database::insert($table, $params); + + if ($newId) { + api_item_property_update( + $courseInfo, + TOOL_PTEST_CATEGORY, + $newId, + 'TestCategoryAdded', + api_get_user_id() + ); + } + + return $newId; + } else { + return false; + } + } + + /** + * Removes the category from the database + * if there were question in this category, the link between question and category is removed. + * + * @param int $id + * + * @return bool + */ + public function removeCategory($id) + { + $table = Database::get_course_table(TABLE_QUIZ_CATEGORY_PTEST); + $id = (int) $id; + $course_id = api_get_course_int_id(); + $category = $this->getCategory($id); + + if ($category) { + $sql = "DELETE FROM $table + WHERE id= $id AND c_id=".$course_id; + Database::query($sql); + + return true; + } + + return false; + } + + /** + * Modify category name or description of category with id=in_id. + * + * @param int $courseId + * + * @return bool + */ + public function modifyCategory($courseId = 0) + { + $table = Database::get_course_table(TABLE_QUIZ_CATEGORY_PTEST); + $id = (int) $this->id; + $name = Database::escape_string($this->name); + $description = Database::escape_string($this->description); + $cat = $this->getCategory($id, $courseId); + $courseId = empty($courseId) ? api_get_course_int_id() : (int) $courseId; + $courseInfo = api_get_course_info_by_id($courseId); + if (empty($courseInfo)) { + return false; + } + + if ($cat) { + $sql = "UPDATE $table SET + title = '$name', + description = '$description' + WHERE id = $id AND c_id = ".$courseId; + Database::query($sql); + + // item_property update + api_item_property_update( + $courseInfo, + TOOL_PTEST_CATEGORY, + $this->id, + 'TestCategoryModified', + api_get_user_id() + ); + + return true; + } + + return false; + } + + /** + * Gets the number of categories of exercise id=in_id. + * + * @param int $exerciseId + * + * @return int + */ + public function getCategoriesExerciseNumber($exerciseId) + { + $table = Database::get_course_table(TABLE_QUIZ_CATEGORY_PTEST); + $exerciseId = (int) $exerciseId; + $sql = "SELECT count(*) AS nb + FROM $table + WHERE exercise_id = $exerciseId AND c_id=".api_get_course_int_id(); + $res = Database::query($sql); + $row = Database::fetch_array($res); + + return $row['nb']; + } + + /** + * Return an array of all Category objects of exercise in the database + * If $field=="" Return an array of all category objects in the database + * Otherwise, return an array of all in_field value + * in the database (in_field = id or name or description). + * + * @param int $exerciseId + * @param string $field + * @param int $courseId + * + * @return array + */ + public static function getCategoryListInfo($exerciseId, $field = '', $courseId = 0) + { + $exerciseId = (int) $exerciseId; + $courseId = empty($courseId) ? api_get_course_int_id() : (int) $courseId; + + $table = Database::get_course_table(TABLE_QUIZ_CATEGORY_PTEST); + $categories = []; + if (empty($field)) { + $sql = "SELECT id FROM $table + WHERE c_id = $courseId AND exercise_id = $exerciseId + ORDER BY title ASC"; + $res = Database::query($sql); + while ($row = Database::fetch_array($res)) { + $category = new PTestCategory(); + $categories[] = $category->getCategory($row['id'], $courseId); + } + } else { + $field = Database::escape_string($field); + $sql = "SELECT $field FROM $table + WHERE c_id = $courseId AND exercise_id = $exerciseId + ORDER BY $field ASC"; + $res = Database::query($sql); + while ($row = Database::fetch_array($res)) { + $categories[] = $row[$field]; + } + } + + return $categories; + } + + /** + * --------Return the PTestCategory id for question with question_id = $questionId + * In this version, a question has only 1 PTestCategory. + * Return the PTestCategory id, 0 if none. + * + * @param int $questionId + * @param int $courseId + * + * @return int + */ + public static function getCategoryForQuestion($questionId, $courseId = 0) + { + $courseId = (int) $courseId; + $questionId = (int) $questionId; + + if (empty($courseId)) { + $courseId = api_get_course_int_id(); + } + + if (empty($courseId) || empty($questionId)) { + return 0; + } + + $table = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY); + $sql = "SELECT category_id + FROM $table + WHERE question_id = $questionId AND c_id = $courseId"; + $res = Database::query($sql); + $result = 0; + if (Database::num_rows($res) > 0) { + $data = Database::fetch_array($res); + $result = (int) $data['category_id']; + } + + return $result; + } + + /** + * ---------- Return the category name for question with question_id = $questionId + * In this version, a question has only 1 category. + * + * @param $questionId + * @param int $courseId + * + * @return string + */ + public static function getCategoryNameForQuestion($questionId, $courseId = 0) + { + if (empty($courseId)) { + $courseId = api_get_course_int_id(); + } + $courseId = (int) $courseId; + $categoryId = self::getCategoryForQuestion($questionId, $courseId); + $table = Database::get_course_table(TABLE_QUIZ_CATEGORY_PTEST); + $sql = "SELECT title + FROM $table + WHERE id = $categoryId AND c_id = $courseId"; + $res = Database::query($sql); + $data = Database::fetch_array($res); + $result = ''; + if (Database::num_rows($res) > 0) { + $result = $data['title']; + } + + return $result; + } + + /** + * Return the list of differents categories ID for a test in the current course + * input : test_id + * return : array of category id (integer) + * hubert.borderiou 07-04-2011. + * + * @param int $exerciseId + * @param int $courseId + * + * @return array + */ + public static function getListOfCategoriesIDForTest($exerciseId, $courseId = 0) + { + // parcourir les questions d'un test, recup les categories uniques dans un tableau + $exercise = new Exercise($courseId); + $exercise->read($exerciseId, false); + $categoriesInExercise = $exercise->getQuestionWithCategories(); + // the array given by selectQuestionList start at indice 1 and not at indice 0 !!! ??? + $categories = []; + if (!empty($categoriesInExercise)) { + foreach ($categoriesInExercise as $category) { + $categories[$category['id']] = $category; + } + } + + return $categories; + } + + /** + * @param Exercise $exercise + * + * @return array + */ + public static function getListOfCategoriesIDForTestObject(Exercise $exercise) + { + // parcourir les questions d'un test, recup les categories uniques dans un tableau + $categories_in_exercise = []; + $question_list = $exercise->getQuestionOrderedListByName(); + + // the array given by selectQuestionList start at indice 1 and not at indice 0 !!! ??? + foreach ($question_list as $questionInfo) { + $question_id = $questionInfo['question_id']; + $category_list = self::getCategoryForQuestion($question_id); + if (is_numeric($category_list)) { + $category_list = [$category_list]; + } + + if (!empty($category_list)) { + $categories_in_exercise = array_merge($categories_in_exercise, $category_list); + } + } + if (!empty($categories_in_exercise)) { + $categories_in_exercise = array_unique(array_filter($categories_in_exercise)); + } + + return $categories_in_exercise; + } + + /** + * Return the list of different categories NAME for a test. + * + * @param int $exerciseId + * @param bool + * + * @return array + * + * @author function rewrote by jmontoya + */ + public static function getListOfCategoriesNameForTest($exerciseId, $grouped_by_category = true) + { + $result = []; + $categories = self::getListOfCategoriesIDForTest($exerciseId); + + foreach ($categories as $catInfo) { + $categoryId = $catInfo['id']; + if (!empty($categoryId)) { + $result[$categoryId] = [ + 'title' => $catInfo['title'], + //'parent_id' => $catInfo['parent_id'], + 'parent_id' => '', + 'c_id' => $catInfo['c_id'], + ]; + } + } + + return $result; + } + + /** + * @param Exercise $exercise + * + * @return array + */ + public static function getListOfCategoriesForTest(Exercise $exercise) + { + $result = []; + $categories = self::getListOfCategoriesIDForTestObject($exercise); + foreach ($categories as $cat_id) { + $cat = new PTestCategory(); + $cat = (array) $cat->getCategory($cat_id); + $cat['iid'] = $cat['id']; + $cat['title'] = $cat['name']; + $result[$cat['id']] = $cat; + } + + return $result; + } + + /** + * return the number of question of a category id in a test. + * + * @param int $exerciseId + * @param int $categoryId + * + * @return int + * + * @author hubert.borderiou 07-04-2011 + */ + public static function getNumberOfQuestionsInCategoryForTest($exerciseId, $categoryId) + { + $nbCatResult = 0; + $quiz = new Exercise(); + $quiz->read($exerciseId); + $questionList = $quiz->selectQuestionList(); + // the array given by selectQuestionList start at indice 1 and not at indice 0 !!! ? ? ? + for ($i = 1; $i <= count($questionList); $i++) { + if (self::getCategoryForQuestion($questionList[$i]) == $categoryId) { + $nbCatResult++; + } + } + + return $nbCatResult; + } + + /** + * return the number of question for a test using random by category + * input : test_id, number of random question (min 1). + * + * @param int $exerciseId + * @param int $random + * + * @return int + * hubert.borderiou 07-04-2011 + * question without categories are not counted + */ + public static function getNumberOfQuestionRandomByCategory($exerciseId, $random) + { + $count = 0; + $categories = self::getListOfCategoriesIDForTest($exerciseId); + foreach ($categories as $category) { + if (empty($category['id'])) { + continue; + } + + $nbQuestionInThisCat = self::getNumberOfQuestionsInCategoryForTest( + $exerciseId, + $category['id'] + ); + + if ($nbQuestionInThisCat > $random) { + $count += $random; + } else { + $count += $nbQuestionInThisCat; + } + } + + return $count; + } + + /** + * Return an array (id=>name) + * array[0] = get_lang('NoCategory');. + * + * @param int $courseId + * + * @return array + */ + public static function getCategoriesIdAndName($courseId = 0) + { + if (empty($courseId)) { + $courseId = api_get_course_int_id(); + } + $categories = self::getCategoryListInfo('', $courseId); + $result = ['0' => get_lang('NoCategorySelected')]; + for ($i = 0; $i < count($categories); $i++) { + $result[$categories[$i]->id] = $categories[$i]->name; + } + + return $result; + } + + /** + * Returns an array of question ids for each category + * $categories[1][30] = 10, array with category id = 1 and question_id = 10 + * A question has "n" categories. + * + * @param int $exerciseId + * @param array $check_in_question_list + * @param array $categoriesAddedInExercise + * + * @return array + */ + public static function getQuestionsByCat( + $exerciseId, + $check_in_question_list = [], + $categoriesAddedInExercise = [] + ) { + $tableQuestion = Database::get_course_table(TABLE_QUIZ_QUESTION); + $TBL_EXERCICE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); + $TBL_QUESTION_REL_CATEGORY = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY); + $categoryTable = Database::get_course_table(TABLE_QUIZ_CATEGORY_PTEST); + $exerciseId = (int) $exerciseId; + $courseId = api_get_course_int_id(); + + $sql = "SELECT DISTINCT qrc.question_id, qrc.category_id + FROM $TBL_QUESTION_REL_CATEGORY qrc + INNER JOIN $TBL_EXERCICE_QUESTION eq + ON (eq.question_id = qrc.question_id AND qrc.c_id = eq.c_id) + INNER JOIN $categoryTable c + ON (c.id = qrc.category_id AND c.c_id = eq.c_id) + INNER JOIN $tableQuestion q + ON (q.id = qrc.question_id AND q.c_id = eq.c_id) + WHERE + exercice_id = $exerciseId AND + qrc.c_id = $courseId + "; + + $res = Database::query($sql); + $categories = []; + while ($data = Database::fetch_array($res)) { + if (!empty($check_in_question_list)) { + if (!in_array($data['question_id'], $check_in_question_list)) { + continue; + } + } + + if (!isset($categories[$data['category_id']]) || + !is_array($categories[$data['category_id']]) + ) { + $categories[$data['category_id']] = []; + } + + $categories[$data['category_id']][] = $data['question_id']; + } + + if (!empty($categoriesAddedInExercise)) { + $newCategoryList = []; + foreach ($categoriesAddedInExercise as $category) { + $categoryId = $category['category_id']; + if (isset($categories[$categoryId])) { + $newCategoryList[$categoryId] = $categories[$categoryId]; + } + } + + $checkQuestionsWithNoCategory = false; + foreach ($categoriesAddedInExercise as $category) { + if (empty($category['category_id'])) { + // Check + $checkQuestionsWithNoCategory = true; + break; + } + } + + // Select questions that don't have any category related + if ($checkQuestionsWithNoCategory) { + $originalQuestionList = $check_in_question_list; + foreach ($originalQuestionList as $questionId) { + $categoriesFlatten = array_flatten($categories); + if (!in_array($questionId, $categoriesFlatten)) { + $newCategoryList[0][] = $questionId; + } + } + } + $categories = $newCategoryList; + } + + return $categories; + } + + /** + * Returns an array of $numberElements from $array. + * + * @param array + * @param int + * + * @return array + */ + public static function getNElementsFromArray($array, $numberElements) + { + $list = $array; + shuffle($list); + if ($numberElements < count($list)) { + $list = array_slice($list, 0, $numberElements); + } + + return $list; + } + + /** + * @param int $questionId + * @param int $displayCategoryName + */ + public static function displayCategoryAndTitle($questionId, $displayCategoryName = 1) + { + echo self::returnCategoryAndTitle($questionId, $displayCategoryName); + } + + /** + * @param int $questionId + * @param int $in_display_category_name + * + * @return string|null + */ + public static function returnCategoryAndTitle($questionId, $in_display_category_name = 1) + { + $is_student = !(api_is_allowed_to_edit(null, true) || api_is_session_admin()); + $objExercise = Session::read('objExercise'); + if (!empty($objExercise)) { + $in_display_category_name = $objExercise->display_category_name; + } + $content = null; + if (self::getCategoryNameForQuestion($questionId) != '' && + ($in_display_category_name == 1 || !$is_student) + ) { + $content .= '"; + } + + return $content; + } + + /** + * sortTabByBracketLabel ($tabCategoryQuestions) + * key of $tabCategoryQuestions are the category id (0 for not in a category) + * value is the array of question id of this category + * Sort question by Category. + */ + public static function sortTabByBracketLabel($in_tab) + { + $tabResult = []; + $tabCatName = []; // tab of category name + foreach ($in_tab as $cat_id => $tabquestion) { + $category = new PTestCategory(); + $category = $category->getCategory($cat_id); + $tabCatName[$cat_id] = $category->name; + } + reset($in_tab); + // sort table by value, keeping keys as they are + asort($tabCatName); + // keys of $tabCatName are keys order for $in_tab + foreach ($tabCatName as $key => $val) { + $tabResult[$key] = $in_tab[$key]; + } + + return $tabResult; + } + + /** + * Return the number max of question in a category + * count the number of questions in all categories, and return the max. + * + * @param int $exerciseId + * + * @author - hubert borderiou + * + * @return int + */ + public static function getNumberMaxQuestionByCat($exerciseId) + { + $res_num_max = 0; + // foreach question + $categories = self::getListOfCategoriesIDForTest($exerciseId); + foreach ($categories as $category) { + if (empty($category['id'])) { + continue; + } + + $nbQuestionInThisCat = self::getNumberOfQuestionsInCategoryForTest( + $exerciseId, + $category['id'] + ); + + if ($nbQuestionInThisCat > $res_num_max) { + $res_num_max = $nbQuestionInThisCat; + } + } + + return $res_num_max; + } + + /** + * Returns a category summary report. + * + * @param int $exerciseId + * @param array $category_list + * pre filled array with the category_id, score, and weight + * example: array(1 => array('score' => '10', 'total' => 20)); + * + * @return string + */ + public static function get_stats_table_by_attempt( + $exerciseId, + $category_list = [] + ) { + if (empty($category_list)) { + return null; + } + $category_name_list = self::getListOfCategoriesNameForTest($exerciseId); + + $table = new HTML_Table(['class' => 'table table-bordered', 'id' => 'category_results']); + $table->setHeaderContents(0, 0, get_lang('Categories')); + $table->setHeaderContents(0, 1, get_lang('AbsoluteScore')); + $table->setHeaderContents(0, 2, get_lang('RelativeScore')); + $row = 1; + + $none_category = []; + if (isset($category_list['none'])) { + $none_category = $category_list['none']; + unset($category_list['none']); + } + + $total = []; + if (isset($category_list['total'])) { + $total = $category_list['total']; + unset($category_list['total']); + } + if (count($category_list) > 1) { + foreach ($category_list as $category_id => $category_item) { + $table->setCellContents($row, 0, $category_name_list[$category_id]); + $table->setCellContents( + $row, + 1, + ExerciseLib::show_score( + $category_item['score'], + $category_item['total'], + false + ) + ); + $table->setCellContents( + $row, + 2, + ExerciseLib::show_score( + $category_item['score'], + $category_item['total'], + true, + false, + true + ) + ); + $row++; + } + + if (!empty($none_category)) { + $table->setCellContents($row, 0, get_lang('None')); + $table->setCellContents( + $row, + 1, + ExerciseLib::show_score( + $none_category['score'], + $none_category['total'], + false + ) + ); + $table->setCellContents( + $row, + 2, + ExerciseLib::show_score( + $none_category['score'], + $none_category['total'], + true, + false, + true + ) + ); + $row++; + } + if (!empty($total)) { + $table->setCellContents($row, 0, get_lang('Total')); + $table->setCellContents( + $row, + 1, + ExerciseLib::show_score( + $total['score'], + $total['total'], + false + ) + ); + $table->setCellContents( + $row, + 2, + ExerciseLib::show_score( + $total['score'], + $total['total'], + true, + false, + true + ) + ); + } + + return $table->toHtml(); + } + + return ''; + } + + /** + * @param Exercise $exercise + * @param int $courseId + * @param string $order + * @param bool $shuffle + * @param bool $excludeCategoryWithNoQuestions + * + * @return array + */ + public function getCategoryExerciseTree( + $exercise, + $courseId, + $order = null, + $shuffle = false, + $excludeCategoryWithNoQuestions = true + ) { + if (empty($exercise)) { + return []; + } + + $courseId = (int) $courseId; + $table = Database::get_course_table(TABLE_QUIZ_REL_CATEGORY); + $categoryTable = Database::get_course_table(TABLE_QUIZ_CATEGORY_PTEST); + $exercise->id = (int) $exercise->id; + + $sql = "SELECT * FROM $table qc + LEFT JOIN $categoryTable c + ON (qc.c_id = c.c_id AND c.id = qc.category_id) + WHERE qc.c_id = $courseId AND exercise_id = {$exercise->id} "; + + if (!empty($order)) { + $order = Database::escape_string($order); + $sql .= "ORDER BY $order"; + } + + $categories = []; + $result = Database::query($sql); + if (Database::num_rows($result)) { + while ($row = Database::fetch_array($result, 'ASSOC')) { + if ($excludeCategoryWithNoQuestions) { + if ($row['count_questions'] == 0) { + continue; + } + } + if (empty($row['title']) && empty($row['category_id'])) { + $row['title'] = get_lang('NoCategory'); + } + $categories[$row['category_id']] = $row; + } + } + + if ($shuffle) { + shuffle_assoc($categories); + } + + return $categories; + } + + /** + * @param FormValidator $form + * @param string $action + */ + public function getForm(&$form, $action = 'new') + { + switch ($action) { + case 'new': + $header = get_lang('AddACategory'); + $submit = get_lang('AddTestCategory'); + break; + case 'edit': + $header = get_lang('EditCategory'); + $submit = get_lang('ModifyCategory'); + break; + } + + // Setting the form elements + $form->addElement('header', $header); + $form->addElement('hidden', 'category_id'); + $form->addElement( + 'text', + 'category_name', + get_lang('CategoryName'), + ['class' => 'span6'] + ); + $form->add_html_editor( + 'category_description', + get_lang('CategoryDescription'), + false, + false, + [ + 'ToolbarSet' => 'test_category', + 'Width' => '90%', + 'Height' => '200', + ] + ); + $category_parent_list = []; + + $options = [ + '1' => get_lang('Visible'), + '0' => get_lang('Hidden'), + ]; + $form->addElement( + 'select', + 'visibility', + get_lang('Visibility'), + $options + ); + $script = null; + if (!empty($this->parent_id)) { + $parent_cat = new PTestCategory(); + $parent_cat = $parent_cat->getCategory($this->parent_id); + $category_parent_list = [$parent_cat->id => $parent_cat->name]; + $script .= ''; + } + $form->addElement('html', $script); + + $form->addElement('select', 'parent_id', get_lang('Parent'), $category_parent_list, ['id' => 'parent_id']); + $form->addElement('style_submit_button', 'SubmitNote', $submit, 'class="add"'); + + // setting the defaults + $defaults = []; + $defaults["category_id"] = $this->id; + $defaults["category_name"] = $this->name; + $defaults["category_description"] = $this->description; + $defaults["parent_id"] = $this->parent_id; + $defaults["visibility"] = $this->visibility; + $form->setDefaults($defaults); + + // setting the rules + $form->addRule('category_name', get_lang('ThisFieldIsRequired'), 'required'); + } + + /** + * Returns the category form. + * + * @param Exercise $exercise + * + * @return string + */ + public function returnCategoryForm(Exercise $exercise) + { + $categories = $this->getListOfCategoriesForTest($exercise); + $saved_categories = $exercise->getCategoriesInExercise(); + $return = null; + + if (!empty($categories)) { + $nbQuestionsTotal = $exercise->getNumberQuestionExerciseCategory(); + $exercise->setCategoriesGrouping(true); + $real_question_count = count($exercise->getQuestionList()); + + $warning = null; + if ($nbQuestionsTotal != $real_question_count) { + $warning = Display::return_message( + get_lang('CheckThatYouHaveEnoughQuestionsInYourCategories'), + 'warning' + ); + } + + $return .= $warning; + $return .= '
'.get_lang('Choice').''.get_lang('Answer').'
'; + $return .= ''; + $return .= ''; + $return .= ''; + + $emptyCategory = [ + 'id' => '0', + 'name' => get_lang('NoCategory'), + 'description' => '', + 'iid' => '0', + 'title' => get_lang('NoCategory'), + ]; + + $categories[] = $emptyCategory; + + foreach ($categories as $category) { + $cat_id = $category['iid']; + $return .= ''; + $return .= ''; + $return .= ''; + $return .= ''; + } + + $return .= '
'.get_lang('Categories').''.get_lang('Number').'
'; + $return .= Display::div($category['name']); + $return .= ''; + $value = isset($saved_categories) && isset($saved_categories[$cat_id]) ? $saved_categories[$cat_id]['count_questions'] : -1; + $return .= ''; + $return .= '
'; + $return .= get_lang('ZeroMeansNoQuestionWillBeSelectedMinusOneMeansThatAllQuestionsWillBeSelected'); + } + + return $return; + } + + /** + * Return true if a category already exists with the same name. + * + * @param string $name + * @param int $courseId + * + * @return bool + */ + public static function categoryTitleExists($name, $courseId = 0) + { + $categories = self::getCategoryListInfo('title', $courseId); + foreach ($categories as $title) { + if ($title == $name) { + return true; + } + } + + return false; + } + + /** + * Return the id of the test category with title = $in_title. + * + * @param string $title + * @param int $courseId + * + * @return int is id of test category + */ + public static function get_category_id_for_title($title, $courseId = 0) + { + $out_res = 0; + if (empty($courseId)) { + $courseId = api_get_course_int_id(); + } + $courseId = (int) $courseId; + $tbl_cat = Database::get_course_table(TABLE_QUIZ_CATEGORY_PTEST); + $sql = "SELECT id FROM $tbl_cat + WHERE c_id = $courseId AND title = '".Database::escape_string($title)."'"; + $res = Database::query($sql); + if (Database::num_rows($res) > 0) { + $data = Database::fetch_array($res); + $out_res = $data['id']; + } + + return $out_res; + } + + /** + * @param int $exerciseId + * @param int $courseId + * @param int $sessionId + * + * @return array + */ + public function getCategories($exerciseId, $courseId, $sessionId = 0) + { + $table = Database::get_course_table(TABLE_QUIZ_CATEGORY_PTEST); + $itemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY); + $sessionId = (int) $sessionId; + $courseId = (int) $courseId; + $exerciseId = (int) $exerciseId; + + $sessionCondition = api_get_session_condition( + $sessionId + ); + + if (empty($courseId)) { + return []; + } + + $sql = "SELECT * FROM $table + WHERE + exercise_id = $exerciseId AND + c_id = $courseId + $sessionCondition + ORDER BY title"; + error_log($sql); + $result = Database::query($sql); + + return Database::store_result($result, 'ASSOC'); + } + + /** + * @param int $courseId + * @param int $sessionId + * + * @return string + */ + public function displayCategories($exerciseId, $courseId, $sessionId = 0) + { + $exerciseId = (int) $exerciseId; + $sessionId = (int) $sessionId; + $categories = $this->getCategories($exerciseId, $courseId, $sessionId); + $html = ''; + foreach ($categories as $category) { + $tmpobj = new PTestCategory(); + $tmpobj = $tmpobj->getCategory($category['id']); + $rowname = self::protectJSDialogQuote($category['title']); + $content .= '
'; + $content .= $category['description']; + $content .= '
'; + $links = ''; + + $links .= ''. + Display::return_icon('edit.png', get_lang('Edit'), [], ICON_SIZE_SMALL).''; + $links .= ' '; + $links .= Display::return_icon('delete.png', get_lang('Delete'), [], ICON_SIZE_SMALL).''; + + $html .= Display::panel($content, $category['title'].$links); + } + + return $html; + } + + /** + * To allowed " in javascript dialog box without bad surprises + * replace " with two '. + * + * @param string $text + * + * @return mixed + */ + public function protectJSDialogQuote($text) + { + $res = $text; + $res = str_replace("'", "\'", $res); + // super astuce pour afficher les " dans les boite de dialogue + $res = str_replace('"', "\'\'", $res); + + return $res; + } +} diff --git a/main/exercise/ptest_category_ranking.class.php b/main/exercise/ptest_category_ranking.class.php new file mode 100644 index 00000000000..97989e73ab9 --- /dev/null +++ b/main/exercise/ptest_category_ranking.class.php @@ -0,0 +1,296 @@ +type = QUESTION_PT_TYPE_CATEGORY_RANKING; + $this->isContent = $this->getIsContent(); + } + + /** + * {@inheritdoc} + */ + public function createAnswersForm($form) + { + // Getting the exercise list + /** @var Exercise $obj_ex */ + $obj_ex = Session::read('objExercise'); + + $editor_config = [ + 'ToolbarSet' => 'TestProposedAnswer', + 'Width' => '100%', + 'Height' => '125', + ]; + + // Categories options select + $category = new PTestCategory(); + $categoriesList = $category->getCategoryListInfo($obj_ex->selectId()); + $categoriesOptions = [null => get_lang('None')]; + foreach ($categoriesList as $categoryItem) { + $categoriesOptions[$categoryItem->id] = (string) $categoryItem->name; + } + + //this line defines how many questions by default appear when creating a choice question + // The previous default value was 2. See task #1759. + $nb_answers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : count($categoriesList); + $nb_answers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0)); + + $html = ' + + + + + + + + '; + + $form->addHeader(get_lang('Answers')); + $form->addHtml($html); + + $defaults = []; + if (!empty($this->id)) { + $answer = new Answer($this->id); + $answer->read(); + if ($answer->nbrAnswers > 0 && !$form->isSubmitted()) { + $nb_answers = $answer->nbrAnswers; + } + } + $form->addElement('hidden', 'nb_answers'); + + //$temp_scenario = []; + if ($nb_answers < 1) { + $nb_answers = 1; + echo Display::return_message( + get_lang('YouHaveToCreateAtLeastOneAnswer') + ); + } + + for ($i = 1; $i <= $nb_answers; $i++) { + $form->addHtml(''); + if (isset($answer) && is_object($answer)) { + $defaults['answer['.$i.']'] = isset($answer->answer[$i]) ? $answer->answer[$i] : ''; + $defaults['ptest_category['.$i.']'] = isset($answer->ptest_category[$i]) ? $answer->ptest_category[$i] : 0; + } + + $renderer = $form->defaultRenderer(); + + $renderer->setElementTemplate( + '', + 'counter['.$i.']' + ); + $renderer->setElementTemplate( + '', + 'answer['.$i.']' + ); + $renderer->setElementTemplate( + '', + 'ptest_category['.$i.']' + ); + + $answer_number = $form->addElement( + 'text', + 'counter['.$i.']', + null, + ' value = "'.$i.'"' + ); + $answer_number->freeze(); + + $form->addHtmlEditor('answer['.$i.']', null, null, false, $editor_config); + + $form->addRule( + 'answer['.$i.']', + get_lang('ThisFieldIsRequired'), + 'required' + ); + + $form->addSelect( + 'ptest_category['.$i.']', + null, + $categoriesOptions + ); + + $form->addHtml(''); + } + + $form->addHtml(''); + $form->addHtml('
'.get_lang('Number').''.get_lang('Answer').''.get_lang('PtestCategory').'
{error}
{element}
{error}
{element}
{error}
{element}
'); + + global $text; + $buttonGroup = []; + + if ($obj_ex->edit_exercise_in_lp == true || + (empty($this->exerciseList) && empty($obj_ex->id)) + ) { + //setting the save button here and not in the question class.php + $buttonGroup[] = $form->addButtonDelete(get_lang('LessAnswer'), 'lessAnswers', true); + $buttonGroup[] = $form->addButtonCreate(get_lang('PlusAnswer'), 'moreAnswers', true); + $buttonGroup[] = $form->addButton( + 'submitQuestion', + $text, + 'check', + 'primary', + 'default', + null, + ['id' => 'submit-question'], + true + ); + $form->addGroup($buttonGroup); + } + + + if (!empty($this->id)) { + $form->setDefaults($defaults); + } else { + if ($this->isContent == 1) { + // Default sample content. + $form->setDefaults($defaults); + } + } + $form->setConstants(['nb_answers' => $nb_answers]); + } + + /** + * {@inheritdoc} + */ + public function processAnswersCreation($form, $exercise) + { + $objAnswer = new Answer($this->id); + $nb_answers = $form->getSubmitValue('nb_answers'); + + for ($i = 1; $i <= $nb_answers; $i++) { + $answer = trim($form->getSubmitValue('answer['.$i.']')); + $goodAnswer = false; + $comment = ''; + $weighting = 0; + $ptestCategory = (int) $form->getSubmitValue('ptest_category['.$i.']'); + $dest = ''; + + $objAnswer->createAnswer( + $answer, + $goodAnswer, + $comment, + $weighting, + $i, + null, + null, + $dest, + $ptestCategory + ); + } + + // saves the answers into the data base + $objAnswer->save(); + + $this->save($exercise); + } + + /** + * {@inheritdoc} + */ + public function return_header(Exercise $exercise, $counter = null, $score = []) + { + $header = parent::return_header($exercise, $counter); //, $score); + $header .= ''; + + $header .= ''; + $header .= ''; + $header .= ''; + + return $header; + } + + /** + * Saves one answer to the database. + * + * @param int $id The ID of the answer (has to be calculated for this course) + * @param int $question_id The question ID (to which the answer is attached) + * @param string $title The text of the answer + * @param string $comment The feedback for the answer + * @param float $score The score you get when picking this answer + * @param int $correct Whether this answer is considered *the* correct one (this is the unique answer type) + */ + public function addAnswer( + $id, + $question_id, + $title, + $comment, + $score = 0.0, + $correct = 0 + ) { + $em = Database::getManager(); + $tbl_quiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER); + $tbl_quiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION); + $course_id = api_get_course_int_id(); + $question_id = intval($question_id); + $score = floatval($score); + $correct = intval($correct); + $title = Database::escape_string($title); + $comment = Database::escape_string($comment); + // Get the max position. + $sql = "SELECT max(position) as max_position + FROM $tbl_quiz_answer + WHERE + c_id = $course_id AND + question_id = $question_id"; + $rs_max = Database::query($sql); + $row_max = Database::fetch_object($rs_max); + $position = $row_max->max_position + 1; + + // Insert a new answer + $quizAnswer = new CQuizAnswer(); + $quizAnswer + ->setCId($course_id) + ->setId($id) + ->setQuestionId($question_id) + ->setAnswer($title) + ->setCorrect($correct) + ->setComment($comment) + ->setPonderation($score) + ->setPosition($position) + ->setDestination('0@@0@@0@@0'); + + $em->persist($quizAnswer); + $em->flush(); + + $id = $quizAnswer->getIid(); + + if ($id) { + $quizAnswer + ->setId($id); + + $em->merge($quizAnswer); + $em->flush(); + } + + if ($correct) { + $sql = "UPDATE $tbl_quiz_question + SET ponderation = (ponderation + $score) + WHERE c_id = $course_id AND id = ".$question_id; + Database::query($sql); + } + } +} diff --git a/main/exercise/ptest_exercise_report.php b/main/exercise/ptest_exercise_report.php new file mode 100644 index 00000000000..51347d1cd10 --- /dev/null +++ b/main/exercise/ptest_exercise_report.php @@ -0,0 +1,504 @@ +read($exercise_id); + +$actions = null; +if ($is_allowedToEdit && $origin != 'learnpath') { + // the form + if (api_is_platform_admin() || api_is_course_admin() || + api_is_course_tutor() || api_is_session_general_coach() + ) { + $actions .= ''. + Display::return_icon('back.png', get_lang('GoBackToQuestionList'), '', ICON_SIZE_MEDIUM).''; + $actions .= ''. + Display::return_icon('statistics.png', get_lang('ReportByQuestion'), '', ICON_SIZE_MEDIUM).''; + $actions .= ''. + Display::return_icon('survey_reporting_question.png', get_lang('ExerciseGraph'), '', ICON_SIZE_MEDIUM).''; + // clean result before a selected date icon + $actions .= Display::url( + Display::return_icon( + 'clean_before_date.png', + get_lang('CleanStudentsResultsBeforeDate'), + '', + ICON_SIZE_MEDIUM + ), + '#', + ['onclick' => 'javascript:display_date_picker()'] + ); + // clean result before a selected date datepicker popup + $actions .= Display::span( + Display::input( + 'input', + 'datepicker_start', + get_lang('SelectADateOnTheCalendar'), + [ + 'onmouseover' => 'datepicker_input_mouseover()', + 'id' => 'datepicker_start', + 'onchange' => 'datepicker_input_changed()', + 'readonly' => 'readonly', + ] + ). + Display::button( + 'delete', + get_lang('Delete'), + ['onclick' => 'submit_datepicker()'] + ), + ['style' => 'display:none', 'id' => 'datepicker_span'] + ); + } +} else { + $actions .= ''. + Display::return_icon( + 'back.png', + get_lang('GoBackToQuestionList'), + '', + ICON_SIZE_MEDIUM + ). + ''; +} + +// Deleting an attempt +if (($is_allowedToEdit || $is_tutor || api_is_coach()) && + isset($_GET['delete']) && $_GET['delete'] === 'delete' && + !empty($_GET['did']) && $locked == false +) { + $exe_id = (int) $_GET['did']; + if (!empty($exe_id)) { + $sql = 'DELETE FROM '.$TBL_TRACK_EXERCISES.' WHERE exe_id = '.$exe_id; + Database::query($sql); + $sql = 'DELETE FROM '.$TBL_TRACK_ATTEMPT.' WHERE exe_id = '.$exe_id; + Database::query($sql); + + Event::addEvent( + LOG_EXERCISE_ATTEMPT_DELETE, + LOG_EXERCISE_ATTEMPT, + $exe_id, + api_get_utc_datetime() + ); + header('Location: ptest_exercise_report.php?'.api_get_cidreq().'&exerciseId='.$exercise_id); + exit; + } +} + +if ($is_allowedToEdit || $is_tutor) { + $interbreadcrumb[] = [ + 'url' => 'exercise.php?'.api_get_cidreq(), + 'name' => get_lang('Exercises'), + ]; + + $nameTools = get_lang('Stats'); + if ($exerciseExists) { + $interbreadcrumb[] = [ + 'url' => '#', + 'name' => $objExerciseTmp->selectTitle(true), + ]; + } +} else { + $interbreadcrumb[] = [ + 'url' => 'exercise.php?'.api_get_cidreq(), + 'name' => get_lang('Exercises'), + ]; + if ($exerciseExists) { + $nameTools = get_lang('Results').': '.$objExerciseTmp->selectTitle(true); + } +} + +if (($is_allowedToEdit || $is_tutor || api_is_coach()) && + isset($_GET['a']) && $_GET['a'] === 'close' && + !empty($_GET['id']) && $locked == false +) { + // Close the user attempt otherwise left pending + $exe_id = (int) $_GET['id']; + $sql = "UPDATE $TBL_TRACK_EXERCISES SET status = '' + WHERE exe_id = $exe_id AND status = 'incomplete'"; + Database::query($sql); +} + +Display::display_header($nameTools); + +// Clean all results for this test before the selected date +if (($is_allowedToEdit || $is_tutor || api_is_coach()) && + isset($_GET['delete_before_date']) && $locked == false +) { + // ask for the date + $check = Security::check_token('get'); + if ($check) { + $objExerciseTmp = new Exercise(); + if ($objExerciseTmp->read($exercise_id)) { + $count = $objExerciseTmp->cleanResults( + true, + $_GET['delete_before_date'].' 23:59:59' + ); + echo Display::return_message( + sprintf(get_lang('XResultsCleaned'), $count), + 'confirm' + ); + } + } +} + +// Security token to protect deletion +$token = Security::get_token(); +$actions = Display::div($actions, ['class' => 'actions']); + +echo $actions; +$url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_ptest_exercise_results&exerciseId='.$exercise_id.'&filter_by_user='.$filter_user.'&'.api_get_cidreq(); +$action_links = ''; +// Generating group list +$group_list = GroupManager::get_group_list(); +$group_parameters = [ + 'group_all:'.get_lang('All'), + 'group_none:'.get_lang('None'), +]; + +foreach ($group_list as $group) { + $group_parameters[] = $group['id'].':'.$group['name']; +} +if (!empty($group_parameters)) { + $group_parameters = implode(';', $group_parameters); +} + +$officialCodeInList = api_get_setting('show_official_code_exercise_result_list'); + +if ($is_allowedToEdit || $is_tutor) { + // The order is important you need to check the the $column variable in the model.ajax.php file + $columns = [ + get_lang('FirstName'), + get_lang('LastName'), + get_lang('LoginName'), + get_lang('Group'), + get_lang('Duration').' ('.get_lang('MinMinute').')', + get_lang('StartDate'), + get_lang('EndDate'), + get_lang('IP'), + get_lang('ToolLearnpath'), + get_lang('Actions'), + ]; + + if ($officialCodeInList === 'true') { + $columns = array_merge([get_lang('OfficialCode')], $columns); + } + + // Column config + $column_model = [ + ['name' => 'firstname', 'index' => 'firstname', 'width' => '50', 'align' => 'left', 'search' => 'true'], + ['name' => 'lastname', 'index' => 'lastname', 'width' => '50', 'align' => 'left', 'formatter' => 'action_formatter', 'search' => 'true'], + [ + 'name' => 'login', + 'index' => 'username', + 'width' => '40', + 'align' => 'left', + 'search' => 'true', + 'hidden' => api_get_configuration_value('exercise_attempts_report_show_username') ? 'false' : 'true', + ], + [ + 'name' => 'group_name', + 'index' => 'group_id', + 'width' => '40', + 'align' => 'left', + 'search' => 'true', + 'stype' => 'select', + //for the bottom bar + 'searchoptions' => [ + 'defaultValue' => 'group_all', + 'value' => $group_parameters, + ], + //for the top bar + 'editoptions' => ['value' => $group_parameters], + ], + ['name' => 'duration', 'index' => 'exe_duration', 'width' => '30', 'align' => 'left', 'search' => 'true'], + ['name' => 'start_date', 'index' => 'start_date', 'width' => '60', 'align' => 'left', 'search' => 'true'], + ['name' => 'exe_date', 'index' => 'exe_date', 'width' => '60', 'align' => 'left', 'search' => 'true'], + ['name' => 'ip', 'index' => 'user_ip', 'width' => '40', 'align' => 'center', 'search' => 'true'], + ['name' => 'lp', 'index' => 'orig_lp_id', 'width' => '60', 'align' => 'left', 'search' => 'false'], + ['name' => 'actions', 'index' => 'actions', 'width' => '60', 'align' => 'left', 'search' => 'false', 'sortable' => 'false'], + ]; + + if ($officialCodeInList === 'true') { + $officialCodeRow = ['name' => 'official_code', 'index' => 'official_code', 'width' => '50', 'align' => 'left', 'search' => 'true']; + $column_model = array_merge([$officialCodeRow], $column_model); + } + + $action_links = ' + // add username as title in lastname filed - ref 4226 + function action_formatter(cellvalue, options, rowObject) { + // rowObject is firstname,lastname,login,... get the third word + var loginx = "'.api_htmlentities(sprintf(get_lang('LoginX'), ':::'), ENT_QUOTES).'"; + var tabLoginx = loginx.split(/:::/); + // tabLoginx[0] is before and tabLoginx[1] is after ::: + // may be empty string but is defined + return ""+cellvalue+""; + }'; +} + +$extra_params['autowidth'] = 'true'; +$extra_params['height'] = 'auto'; +$extra_params['gridComplete'] = " + defaultGroupId = Cookies.get('default_group_".$exercise_id."'); + if (typeof defaultGroupId !== 'undefined') { + $('#gs_group_name').val(defaultGroupId); + } +"; + +$extra_params['beforeRequest'] = " +var defaultGroupId = $('#gs_group_name').val(); + +// Load from group menu +if (typeof defaultGroupId !== 'undefined') { + Cookies.set('default_group_".$exercise_id."', defaultGroupId); +} else { + // get from cookies + defaultGroupId = Cookies.get('default_group_".$exercise_id."'); + $('#gs_group_name').val(defaultGroupId); +} + +if (typeof defaultGroupId !== 'undefined') { + var posted_data = $(\"#results\").jqGrid('getGridParam', 'postData'); + var extraFilter = ',{\"field\":\"group_id\",\"op\":\"eq\",\"data\":\"'+ defaultGroupId +'\"}]}'; + var filters = posted_data.filters; + var stringObj = new String(filters); + stringObj.replace(']}', extraFilter); + + posted_data['group_id_in_toolbar'] = defaultGroupId; + $(this).jqGrid('setGridParam', 'postData', posted_data); +} +"; + +$gridJs = Display::grid_js( + 'results', + $url, + $columns, + $column_model, + $extra_params, + [], + $action_links, + true +); + +?> + + + + + + + +read($exercise_id); +} + +// Only users can see their own results +if (!$is_allowedToEdit) { + if ($student_id != $currentUserId) { + api_not_allowed($printHeaders); + } +} + +$js = ''; +$htmlHeadXtra[] = $js; + +if (api_is_in_gradebook()) { + $interbreadcrumb[] = [ + 'url' => Category::getUrl(), + 'name' => get_lang('ToolGradebook'), + ]; +} + +$interbreadcrumb[] = [ + 'url' => 'exercise.php?'.api_get_cidreq(), + 'name' => get_lang('Exercises'), +]; +$interbreadcrumb[] = [ + 'url' => 'ptest_exercise_report.php?exerciseId='.$exercise_id.'&'.api_get_cidreq(), + 'name' => $objExercise->selectTitle(true), +]; +$interbreadcrumb[] = ['url' => '#', 'name' => get_lang('Result')]; + +$this_section = SECTION_COURSES; + +$htmlHeadXtra[] = api_get_js('chartjs/Chart.min.js'); + +if ($objExercise->selectPtType() == EXERCISE_PT_TYPE_PTEST) { + Display::display_header(''); + + $message = Session::read('attempt_remaining'); + Session::erase('attempt_remaining'); + + ExerciseLib::displayQuestionListByAttempt( + $objExercise, + $id, + false, + $message + ); + Display::display_footer(); + exit; +} diff --git a/main/exercise/ptest_stats.php b/main/exercise/ptest_stats.php new file mode 100644 index 00000000000..5a79e451a94 --- /dev/null +++ b/main/exercise/ptest_stats.php @@ -0,0 +1,258 @@ +read($exerciseId); + +if (!$result) { + api_not_allowed(true); +} + +$sessionId = api_get_session_id(); +$courseCode = api_get_course_id(); + +if (empty($sessionId)) { + $students = CourseManager:: get_student_list_from_course_code( + $courseCode, + false + ); +} else { + $students = CourseManager:: get_student_list_from_course_code( + $courseCode, + true, + $sessionId + ); +} +$count_students = count($students); +$question_list = $objExercise->get_validated_question_list(); +$content = ''; + +if (!empty($question_list)) { + $id = 0; + $counterLabel = 0; + foreach ($question_list as $question_id) { + $counterLabel++; + $data = []; + $questionObj = Question::read($question_id, null, null, true); + $exercise_stats = ExerciseLib::get_student_stats_by_question( + $question_id, + $exerciseId, + $courseCode, + $sessionId + ); + $content .= Display::page_subheader2($counterLabel.'. '.$questionObj->question); + $content .= '

'.get_lang('QuestionType').': '.$questionObj->get_question_type_name(true).'

'; + + $answer = new Answer($question_id); + $answer_count = $answer->selectNbrAnswers(); + + for ($answer_id = 1; $answer_id <= $answer_count; $answer_id++) { + $answer_info = $answer->selectAnswer($answer_id); + $real_answer_id = $answer->selectAutoId($answer_id); + + // Overwriting values depending of the question + switch ($questionObj->type) { + case QUESTION_PT_TYPE_CATEGORY_RANKING: + $headers = [ + get_lang('Answer'), + get_lang('NumberStudentWhoSelectedIt'), + ]; + + $data[$id]['answer'] = $answer_info; + $count = ExerciseLib::get_number_students_answer_count( + $real_answer_id, + $question_id, + $exerciseId, + $courseCode, + $sessionId, + $questionObj->type + ); + $percentage = 0; + if (!empty($count_students)) { + $percentage = $count / $count_students * 100; + } + $data[$id]['attempts'] = Display::bar_progress( + $percentage, + false, + $count.' / '.$count_students + ); + break; + case QUESTION_PT_TYPE_AGREE_OR_DISAGREE: + $headers = [ + get_lang('Answer'), + get_lang('MostAgree'), + get_lang('LeastAgree'), + ]; + $data[$id]['answer'] = $real_answer_id.' - '.$answer_info; + $count = ExerciseLib::get_number_students_answer_count( + $real_answer_id, + $question_id, + $exerciseId, + $courseCode, + $sessionId, + $questionObj->type + ); + $percentageAgree = 0; + $percentageDisagree = 0; + + if (!empty($count_students)) { + $percentageAgree = $count[0] / $count_students * 100; + $percentageDisagree = $count[1] / $count_students * 100; + } + $data[$id]['agree'] = Display::bar_progress( + $percentageAgree, + false, + $count[0].' / '.$count_students + ); + $data[$id]['disagree'] = Display::bar_progress( + $percentageDisagree, + false, + $count[1].' / '.$count_students + ); + break; + case QUESTION_PT_TYPE_AGREE_SCALE: + $headers = [ + get_lang('Answer'), + get_lang('AverageScore'), + ]; + + $data[$id]['answer'] = $answer_info; + $count = ExerciseLib::get_number_students_answer_count( + $real_answer_id, + $question_id, + $exerciseId, + $courseCode, + $sessionId, + $questionObj->type + ); + $percentage = 0; + if (!empty($count_students)) { + $percentage = $count / 5 * 100; + } + $data[$id]['attempts'] = Display::bar_progress( + $percentage, + false, + $count.' / 5' + ); + break; + case QUESTION_PT_TYPE_AGREE_REORDER: + $headers = [ + get_lang('Answer'), + get_lang('AverageScore'), + ]; + + $data[$id]['answer'] = $answer_info; + $count = ExerciseLib::get_number_students_answer_count( + $real_answer_id, + $question_id, + $exerciseId, + $courseCode, + $sessionId, + $questionObj->type + ); + $percentage = 0; + if (!empty($count_students)) { + $percentage = $count / 5 * 100; + } + $data[$id]['attempts'] = Display::bar_progress( + $percentage, + false, + $count.' / 5' + ); + break; + default: + if ($answer_id == 1) { + $data[$id]['name'] = cut($questionObj->question, 100); + } else { + $data[$id]['name'] = '-'; + } + $data[$id]['answer'] = $answer_info; + $data[$id]['correct'] = $correct_answer; + + $count = ExerciseLib::get_number_students_answer_count( + $real_answer_id, + $question_id, + $exerciseId, + $courseCode, + $sessionId + ); + $percentage = 0; + if (!empty($count_students)) { + $percentage = $count / $count_students * 100; + } + $data[$id]['attempts'] = Display::bar_progress( + $percentage, + false, + $count.' / '.$count_students + ); + } + $id++; + } + + // Format A table + $table = new HTML_Table(['class' => 'data_table']); + $row = 0; + $column = 0; + foreach ($headers as $header) { + $table->setHeaderContents($row, $column, $header); + $column++; + } + $row++; + foreach ($data as $row_table) { + $column = 0; + foreach ($row_table as $cell) { + $table->setCellContents($row, $column, $cell); + $table->updateCellAttributes($row, $column, 'align="center"'); + $column++; + } + $table->updateRowAttributes($row, $row % 2 ? 'class="row_even"' : 'class="row_odd"', true); + $row++; + } + $content .= $table->toHtml(); + + } +} + + + +$interbreadcrumb[] = [ + "url" => "exercise.php?".api_get_cidreq(), + "name" => get_lang('Exercises'), +]; +$interbreadcrumb[] = [ + "url" => "admin.php?exerciseId=$exerciseId&".api_get_cidreq(), + "name" => $objExercise->selectTitle(true), +]; + +$tpl = new Template(get_lang('ReportByQuestion')); +$actions = ''. + Display:: return_icon( + 'back.png', + get_lang('GoBackToQuestionList'), + '', + ICON_SIZE_MEDIUM + ) + .''; +$actions = Display::div($actions, ['class' => 'actions']); +$content = $actions.$content; +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/main/exercise/ptest_stats_graph.php b/main/exercise/ptest_stats_graph.php new file mode 100644 index 00000000000..0df77b0c636 --- /dev/null +++ b/main/exercise/ptest_stats_graph.php @@ -0,0 +1,223 @@ +read($exerciseId); + +if (!$result) { + api_not_allowed(true); +} + +$sessionId = api_get_session_id(); +$courseCode = api_get_course_id(); + +if (empty($sessionId)) { + $students = CourseManager:: get_student_list_from_course_code( + $courseCode, + false + ); +} else { + $students = CourseManager:: get_student_list_from_course_code( + $courseCode, + true, + $sessionId + ); +} +$count_students = count($students); +$question_list = $objExercise->get_validated_question_list(); + +$cList = PTestCategory::getCategoryListInfo($objExercise->id); + +$categoryList = []; +foreach ($cList as $item) { + $categoryList[$item->id]['label'] = $item->name; + $categoryList[$item->id]['num'] = 0; +} + +if (!empty($question_list)) { + foreach ($question_list as $question_id) { + $questionObj = Question::read($question_id, null, null, true); + + $answer = new Answer($question_id); + $answer_count = $answer->selectNbrAnswers(); + + for ($answer_id = 1; $answer_id <= $answer_count; $answer_id++) { + $real_answer_id = $answer->selectAutoId($answer_id); + $categoryId = $answer->selectPtCategory($answer_id); + + // Overwriting values depending of the question + switch ($questionObj->type) { + case QUESTION_PT_TYPE_CATEGORY_RANKING: + $count = ExerciseLib::getNumberStudentsAnswerCountGraph( + $real_answer_id, + $question_id, + $exerciseId, + $courseCode, + $sessionId, + $questionObj->type + ); + $categoryList[$categoryId]['num'] += $count; + break; + case QUESTION_PT_TYPE_AGREE_OR_DISAGREE: + $count = ExerciseLib::getNumberStudentsAnswerCountGraph( + $real_answer_id, + $question_id, + $exerciseId, + $courseCode, + $sessionId, + $questionObj->type + ); + $categoryList[$categoryId]['num'] += $count[0]; + $categoryList[$categoryId]['num'] -= $count[1]; + break; + case QUESTION_PT_TYPE_AGREE_SCALE: + $count = ExerciseLib::getNumberStudentsAnswerCountGraph( + $real_answer_id, + $question_id, + $exerciseId, + $courseCode, + $sessionId, + $questionObj->type + ); + $categoryList[$categoryId]['num'] += $count; + break; + case QUESTION_PT_TYPE_AGREE_REORDER: + $count = ExerciseLib::getNumberStudentsAnswerCountGraph( + $real_answer_id, + $question_id, + $exerciseId, + $courseCode, + $sessionId, + $questionObj->type + ); + $categoryList[$categoryId]['num'] += $count; + break; + } + } + } +} + +$labels = []; +$num = []; +foreach ($categoryList as $item) { + $labels[] = $item['label']; + $data[] = (int) $item['num']; +} + +$html = ''; +$html .= '
'; +$html .= '
'; +$html .= ''; +$html .= '
'; +$html .= '
'; +$html .= ''; +$html .= '
'; +$html .= '

'; + +$html .= ''; + +$interbreadcrumb[] = [ + "url" => "exercise.php?".api_get_cidreq(), + "name" => get_lang('Exercises'), +]; +$interbreadcrumb[] = [ + "url" => "admin.php?exerciseId=$exerciseId&".api_get_cidreq(), + "name" => $objExercise->selectTitle(true), +]; + +$htmlHeadXtra[] = api_get_js('chartjs/Chart.min.js'); + +$tpl = new Template(get_lang('ReportByQuestion')); +$actions = ''. + Display:: return_icon( + 'back.png', + get_lang('GoBackToQuestionList'), + '', + ICON_SIZE_MEDIUM + ) + .''; +$actions = Display::div($actions, ['class' => 'actions']); +$content = $actions.$html; +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/main/exercise/ptests_category.php b/main/exercise/ptests_category.php new file mode 100644 index 00000000000..c1fa64d8f88 --- /dev/null +++ b/main/exercise/ptests_category.php @@ -0,0 +1,252 @@ + + function confirmDelete(in_txt, in_id) { + var oldbgcolor = document.getElementById(in_id).style.backgroundColor; + document.getElementById(in_id).style.backgroundColor="#AAFFB0"; + if (confirm(in_txt)) { + return true; + } else { + document.getElementById(in_id).style.backgroundColor = oldbgcolor; + return false; + } + } +'; + +$nameTools = ''; + +require_once __DIR__.'/../inc/global.inc.php'; + +$this_section = SECTION_COURSES; + +api_protect_course_script(true); + +if (!api_is_allowed_to_edit()) { + api_not_allowed(true); +} + +$category = new PTestCategory(); +$courseId = api_get_course_int_id(); +$sessionId = api_get_session_id(); +$exerciseId = (int) $_GET['exerciseId']; + +// get from session +$objExercise = Session::read('objExercise'); + +// Exercise object creation. +if (!is_object($objExercise) || $objExercise->selectPtType() != EXERCISE_PT_TYPE_PTEST) { + // construction of the Exercise object + $objExercise = new Exercise(); + + // creation of a new exercise if wrong or not specified exercise ID + if ($exerciseId) { + $parseQuestionList = $showPagination > 0 ? false : true; + if ($editQuestion) { + $parseQuestionList = false; + $showPagination = true; + } + $objExercise->read($exerciseId, $parseQuestionList); + } + // saves the object into the session + Session::write('objExercise', $objExercise); +} + +// Exercise can be edited in their course. +if ($objExercise->sessionId != $sessionId) { + api_not_allowed(true); +} + + +// breadcrumbs +$interbreadcrumb[] = [ + "url" => "ptest_admin.php?exercise_id=".$objExercise->selectId()."&".api_get_cidreq(), + "name" => get_lang('Exercises'), +]; + +$action = isset($_GET['action']) ? $_GET['action'] : ''; +$content = ''; + +switch ($action) { + case 'addcategory': + $content = add_category_form('addcategory'); + break; + case 'editcategory': + $content = edit_category_form('editcategory'); + break; + case 'deletecategory': + delete_category_form(); + break; +} + +Display::display_header(get_lang('Category')); +displayActionBar(); +echo $content; +echo $category->displayCategories($exerciseId, $courseId, $sessionId); +Display::display_footer(); + + + +/** + * Form to edit a category. + * + * @todo move to PTestCategory.class.php + * + * @param string $action + */ +function edit_category_form($action) +{ + $exerciseId = (int) $_GET['exerciseId']; + $action = Security::remove_XSS($action); + if (isset($_GET['category_id']) && is_numeric($_GET['category_id'])) { + $category_id = intval($_GET['category_id']); + $objcat = new PTestCategory(); + $objcat = $objcat->getCategory($category_id); + $form = new FormValidator( + 'note', + 'post', + api_get_self().'?exerciseId='.$exerciseId.'&action='.$action.'&category_id='.$category_id.'&'.api_get_cidreq() + ); + + // Setting the form elements + $form->addElement('header', get_lang('EditCategory')); + $form->addElement('hidden', 'category_id'); + $form->addElement('text', 'category_name', get_lang('PtestCategoryName'), ['size' => '95']); + $form->addHtmlEditor( + 'category_description', + get_lang('PtestCategoryDescription'), + false, + false, + ['ToolbarSet' => 'TestQuestionDescription', 'Height' => '200'] + ); + $form->addButtonCreate(get_lang('ModifyPTestFeature'), 'SubmitNote'); + + // setting the rules + $form->addRule('category_name', get_lang('ThisFieldIsRequired'), 'required'); + + // setting the defaults + $defaults = []; + $defaults['category_id'] = $objcat->id; + $defaults['category_name'] = $objcat->name; + $defaults['category_description'] = $objcat->description; + $form->setDefaults($defaults); + + // The validation or display + if ($form->validate()) { + $check = Security::check_token('post'); + if ($check) { + $values = $form->exportValues(); + $category = new PTestCategory(); + $category = $category->getCategory($values['category_id']); + + if ($category) { + $category->name = $values['category_name']; + $category->description = $values['category_description']; + $category->modifyCategory(); + Display::addFlash(Display::return_message(get_lang('Updated'))); + } else { + Display::addFlash(Display::return_message(get_lang('ModifyCategoryError'), 'error')); + } + } + Security::clear_token(); + } else { + $token = Security::get_token(); + $form->addElement('hidden', 'sec_token'); + $form->setConstants(['sec_token' => $token]); + + return $form->returnForm(); + } + } else { + Display::addFlash( + Display::return_message(get_lang('CannotEditCategory'), 'error') + ); + } +} + +// process to delete a category +function delete_category_form() +{ + if (isset($_GET['category_id']) && is_numeric($_GET['category_id'])) { + $category = new PTestCategory(); + if ($category->removeCategory($_GET['category_id'])) { + Display::addFlash(Display::return_message(get_lang('DeleteCategoryDone'))); + } else { + Display::addFlash(Display::return_message(get_lang('CannotDeleteCategoryError'), 'error')); + } + } else { + Display::addFlash(Display::return_message(get_lang('CannotDeleteCategoryError'), 'error')); + } +} + +/** + * form to add a category. + * + * @todo move to PTestCategory.class.php + * + * @param string $action + */ +function add_category_form($action) +{ + $exerciseId = (int) $_GET['exerciseId']; + $action = Security::remove_XSS($action); + // initiate the object + $form = new FormValidator('note', 'post', api_get_self().'?exerciseId='.$exerciseId.'&action='.$action.'&'.api_get_cidreq()); + // Setting the form elements + $form->addElement('header', get_lang('AddACategory')); + $form->addElement('text', 'category_name', get_lang('PtestCategoryName'), ['size' => '95']); + $form->addHtmlEditor( + 'category_description', + get_lang('PtestCategoryDescription'), + false, + false, + ['ToolbarSet' => 'TestQuestionDescription', 'Height' => '200'] + ); + $form->addButtonCreate(get_lang('AddPTestFeature'), 'SubmitNote'); + // setting the rules + $form->addRule('category_name', get_lang('ThisFieldIsRequired'), 'required'); + // The validation or display + if ($form->validate()) { + $check = Security::check_token('post'); + if ($check) { + $values = $form->exportValues(); + $category = new PTestCategory(); + $category->name = $values['category_name']; + $category->description = $values['category_description']; + if ($category->save($exerciseId)) { + Display::addFlash(Display::return_message(get_lang('AddCategoryDone'))); + } else { + Display::addFlash(Display::return_message(get_lang('AddCategoryNameAlreadyExists'), 'warning')); + } + } + Security::clear_token(); + } else { + $token = Security::get_token(); + $form->addElement('hidden', 'sec_token'); + $form->setConstants(['sec_token' => $token]); + + return $form->returnForm(); + } +} + +// Display add category button +function displayActionBar() +{ + $exerciseId = (int) $_GET['exerciseId']; + echo ''; + echo "
"; + echo "
".get_lang('PtestCategoryList')."
"; +} diff --git a/main/exercise/question.class.php b/main/exercise/question.class.php index 918db6483d7..b90a8b8835a 100755 --- a/main/exercise/question.class.php +++ b/main/exercise/question.class.php @@ -69,6 +69,12 @@ abstract class Question ANNOTATION => ['Annotation.php', 'Annotation'], READING_COMPREHENSION => ['ReadingComprehension.php', 'ReadingComprehension'], ]; + public static $questionPtTypes = [ + QUESTION_PT_TYPE_CATEGORY_RANKING => ['ptest_category_ranking.class.php', 'PtestCategoryRanking'], + QUESTION_PT_TYPE_AGREE_OR_DISAGREE => ['ptest_agree_disagree.class.php', 'PtestAgreeOrDisagree'], + QUESTION_PT_TYPE_AGREE_SCALE => ['ptest_agree_scale.class.php', 'PtestAgreeScale'], + QUESTION_PT_TYPE_AGREE_REORDER => ['ptest_agree_reorder.class.php', 'PtestAgreeReorder'], + ]; /** * constructor of the class. @@ -130,7 +136,7 @@ public function getIsContent() * * @author Olivier Brouckaert */ - public static function read($id, $course_info = [], $getExerciseList = true) + public static function read($id, $course_info = [], $getExerciseList = true, $ptest = false) { $id = (int) $id; if (empty($course_info)) { @@ -152,7 +158,7 @@ public static function read($id, $course_info = [], $getExerciseList = true) // if the question has been found if ($object = Database::fetch_object($result)) { - $objQuestion = self::getInstance($object->type); + $objQuestion = self::getInstance($object->type, $ptest); if (!empty($objQuestion)) { $objQuestion->id = (int) $id; $objQuestion->iid = (int) $object->iid; @@ -1525,10 +1531,24 @@ public function duplicate($courseInfo = []) /** * @return string */ - public function get_question_type_name() + public function get_question_type_name($ptest = false) { - $key = self::$questionTypes[$this->type]; + if ($ptest) { + $key = self::$questionPtTypes[$this->type]; + } else { + $key = self::$questionTypes[$this->type]; + } + + return get_lang($key[1]); + } + /** + * @return string + */ + public function get_question_ptest_type_name() + { + $key = self::$questionPtTypes[$this->type]; + return get_lang($key[1]); } @@ -1544,6 +1564,14 @@ public static function get_question_type($type) return self::$questionTypes[$type]; } + /** + * @param string $type + */ + public static function get_question_ptest_type($type) + { + return self::$questionPtTypes[$type]; + } + /** * @return array */ @@ -1561,6 +1589,14 @@ public static function getQuestionTypeList() return self::$questionTypes; } + /** + * @return array + */ + public static function getQuestionPtTypeList() + { + return self::$questionPtTypes; + } + /** * Returns an instance of the class corresponding to the type. * @@ -1568,10 +1604,14 @@ public static function getQuestionTypeList() * * @return $this instance of a Question subclass (or of Questionc class by default) */ - public static function getInstance($type) + public static function getInstance($type, $ptest = false) { if (!is_null($type)) { - list($fileName, $className) = self::get_question_type($type); + if ($ptest) { + list($fileName, $className) = self::get_question_ptest_type($type); + } else { + list($fileName, $className) = self::get_question_type($type); + } if (!empty($fileName)) { include_once $fileName; if (class_exists($className)) { @@ -1765,6 +1805,112 @@ public function createForm(&$form, $exercise) }*/ } + /** + * Creates the form to create / edit a question + * A subclass can redefine this function to add fields... + * + * @param FormValidator $form + * @param Exercise $exercise + */ + public function createPtForm(&$form, $exercise) + { + echo ''; + + // question name + if (api_get_configuration_value('save_titles_as_html')) { + $editorConfig = ['ToolbarSet' => 'TitleAsHtml']; + $form->addHtmlEditor( + 'questionName', + get_lang('Question'), + false, + false, + $editorConfig, + true + ); + } else { + $form->addElement('text', 'questionName', get_lang('Question')); + } + + $form->addRule('questionName', get_lang('GiveQuestion'), 'required'); + + // default content + $isContent = isset($_REQUEST['isContent']) ? (int) $_REQUEST['isContent'] : null; + + // Question type + $answerType = isset($_REQUEST['answerType']) ? (int) $_REQUEST['answerType'] : null; + $form->addElement('hidden', 'answerType', $answerType); + + // html editor + $editorConfig = [ + 'ToolbarSet' => 'TestQuestionDescription', + 'Height' => '150', + ]; + + if (!api_is_allowed_to_edit(null, true)) { + $editorConfig['UserStatus'] = 'student'; + } + + $form->addButtonAdvancedSettings('advanced_params'); + $form->addElement('html', ''); + + // default values + $defaults = []; + $defaults['questionName'] = $this->question; + $defaults['questionDescription'] = $this->description; + $defaults['questionLevel'] = $this->level; + $defaults['questionCategory'] = $this->category; + + // Came from he question pool + if (isset($_GET['fromExercise'])) { + $form->setDefaults($defaults); + } + + if (!isset($_GET['newQuestion']) || $isContent) { + $form->setDefaults($defaults); + } + } + /** * function which process the creation of questions. * @@ -1895,6 +2041,54 @@ public static function displayTypeMenu($objExercise) echo ''; } + /** + * Displays the menu of question types. + * + * @param Exercise $objExercise + */ + public static function displayPtTypeMenu($objExercise) + { + // 1. by default we show all the question types + $questionTypeList = self::getQuestionPtTypeList(); + + echo '
'; + echo '
'; + echo ''; + echo '
'; + echo '
'; + } + /** * @param int $question_id * @param string $name diff --git a/main/exercise/question_list_ptest_admin.inc.php b/main/exercise/question_list_ptest_admin.inc.php new file mode 100644 index 00000000000..a9aa6447b6b --- /dev/null +++ b/main/exercise/question_list_ptest_admin.inc.php @@ -0,0 +1,315 @@ +delete($exerciseId); + + // if the question has been removed from the exercise + if ($objExercise->removeFromList($deleteQuestion)) { + $nbrQuestions--; + } + } + // destruction of the Question object + unset($objQuestionTmp); +} +$ajax_url = api_get_path(WEB_AJAX_PATH).'exercise.ajax.php?'.api_get_cidreq().'&exercise_id='.intval($exerciseId); +?> + + +'; +$token = Security::get_token(); +//deletes a session when using don't know question type (ugly fix) +Session::erase('less_answer'); + +// Define categories for question +$category = new PTestCategory(); +if ($category->getCategoriesExerciseNumber($objExercise->selectId()) == 0) { + echo Display::return_message(get_lang('NoDefinedCategories'), 'warning'); +} + +// If we are in a test +$inATest = isset($exerciseId) && $exerciseId > 0; +if (!$inATest) { + echo Display::return_message(get_lang('ChoiceQuestionType'), 'warning'); +} else { + if ($nbrQuestions) { + // In the building exercise mode show question list ordered as is. + $objExercise->setCategoriesGrouping(false); + + // In building mode show all questions not render by teacher order. + $objExercise->questionSelectionType = EX_Q_SELECTION_ORDERED; + $allowQuestionOrdering = true; + $showPagination = api_get_configuration_value('show_question_pagination'); + if (!empty($showPagination) && $nbrQuestions > $showPagination) { + $length = api_get_configuration_value('question_pagination_length'); + $url = api_get_self().'?'.api_get_cidreq(); + // Use pagination for exercise with more than 200 questions. + $allowQuestionOrdering = false; + $start = ($page - 1) * $length; + $questionList = $objExercise->getQuestionForTeacher($start, $length); + $paginator = new Knp\Component\Pager\Paginator(); + $pagination = $paginator->paginate([]); + + $pagination->setTotalItemCount($nbrQuestions); + $pagination->setItemNumberPerPage($length); + $pagination->setCurrentPageNumber($page); + $pagination->renderer = function ($data) use ($url) { + $render = '
    '; + for ($i = 1; $i <= $data['pageCount']; $i++) { + $pageContent = '
  • '.$i.'
  • '; + if ($data['current'] == $i) { + $pageContent = '
  • '.$i.'
  • '; + } + $render .= $pageContent; + } + $render .= '
'; + + return $render; + }; + echo $pagination; + } else { + // Classic order + $questionList = $objExercise->selectQuestionList(true, true); + } + + echo ' + +
+ '; + + $category_list = TestCategory::getListOfCategoriesNameForTest($objExercise->id, false); + + if (is_array($questionList)) { + foreach ($questionList as $id) { + // To avoid warning messages. + if (!is_numeric($id)) { + continue; + } + /** @var Question $objQuestionTmp */ + $objQuestionTmp = Question::read($id, null, true, true); + + if (empty($objQuestionTmp)) { + continue; + } + + $edit_link = Display::url( + Display::return_icon( + 'edit.png', + get_lang('Modify'), + [], + ICON_SIZE_TINY + ), + api_get_self().'?exerciseId='.$objExercise->id.'&'.api_get_cidreq().'&' + .http_build_query([ + 'type' => $objQuestionTmp->selectType(), + 'editQuestion' => $id, + 'page' => $page, + ]), + ['class' => 'btn btn-default btn-sm'] + ); + $delete_link = null; + if ($objExercise->edit_exercise_in_lp == true) { + $delete_link = Display::url( + Display::return_icon( + 'delete.png', + get_lang('RemoveFromTest'), + [], + ICON_SIZE_TINY + ), + api_get_self().'?exerciseId='.$objExercise->id.'&'.api_get_cidreq().'&' + .http_build_query([ + 'exerciseId' => $exerciseId, + 'deleteQuestion' => $id, + 'page' => $page, + ]), + [ + 'id' => "delete_$id", + 'class' => 'opener btn btn-default btn-sm', + ] + ); + } + + if ($limitTeacherAccess && !api_is_platform_admin()) { + $delete_link = ''; + } + + $btnActions = implode( + PHP_EOL, + [$edit_link, $delete_link] + ); + + $title = Security::remove_XSS($objQuestionTmp->selectTitle()); + $title = strip_tags($title); + $move = ' '; + if ($allowQuestionOrdering) { + $move = Display::returnFontAwesomeIcon('arrows moved', 1, true); + } + + // Question name + $questionName = + ' + '.$move.' '.cut($title, 42).' + '; + + // Question type + $typeImg = $objQuestionTmp->getTypePicture(); + $typeExpl = $objQuestionTmp->getExplanation(); + + $questionType = Display::return_icon($typeImg, get_lang($typeExpl)); + + // Question category + $txtQuestionCat = Security::remove_XSS( + TestCategory::getCategoryNameForQuestion($objQuestionTmp->id) + ); + if (empty($txtQuestionCat)) { + $txtQuestionCat = '-'; + } + + // Question level + $txtQuestionLevel = $objQuestionTmp->getLevel(); + if (empty($objQuestionTmp->level)) { + $txtQuestionLevel = '-'; + } + $questionLevel = $txtQuestionLevel; + + echo '
+
+
+
' + .$questionName.' +
+
+ '.get_lang('Type').' ' + .$questionType.' +
+
+ '.get_lang('Category').' ' + .cut($txtQuestionCat, 42).' +
+
+ '.get_lang('Difficulty').' ' + .$questionLevel.' +
+
+
'.$btnActions.'
+
+
+
+
+
+
+ '; + unset($objQuestionTmp); + } + } + + echo '
'; //question list div + } else { + echo Display::return_message(get_lang('NoQuestion'), 'warning'); + } +} diff --git a/main/exercise/question_ptest_admin.inc.php b/main/exercise/question_ptest_admin.inc.php new file mode 100644 index 00000000000..d18a1be88b8 --- /dev/null +++ b/main/exercise/question_ptest_admin.inc.php @@ -0,0 +1,91 @@ +id.'&'.api_get_cidreq().'&modifyQuestion='.$modifyQuestion.'&editQuestion='.$objQuestion->id.'&page='.$page; +} else { + $objQuestion = Question::getInstance($_REQUEST['answerType'], true); + $action = api_get_self().'?exerciseId='.$objExercise->id.'&'.api_get_cidreq().'&modifyQuestion='.$modifyQuestion.'&newQuestion='.$newQuestion; +} + +if (is_object($objQuestion)) { + // FORM CREATION + $form = new FormValidator('question_admin_form', 'post', $action); + if (isset($_GET['editQuestion'])) { + $class = 'btn btn-default'; + $text = get_lang('ModifyQuestion'); + $type = isset($_GET['type']) ? Security::remove_XSS($_GET['type']) : null; + } else { + $class = 'btn btn-default'; + $text = get_lang('AddQuestionToExercise'); + $type = $_REQUEST['answerType']; + } + + $typesInformation = Question::getQuestionPtTypeList(); + $form_title_extra = isset($typesInformation[$type][1]) ? get_lang($typesInformation[$type][1]) : null; + + $code = ''; + if (isset($objQuestion->code) && !empty($objQuestion->code)) { + $code = ' ('.$objQuestion->code.')'; + } + + // form title + $form->addHeader($text.': '.$form_title_extra.$code); + + // question form elements + $objQuestion->createPtForm($form, $objExercise); + + // answer form elements + $objQuestion->createAnswersForm($form); + + // this variable $show_quiz_edition comes from admin.php blocks the exercise/quiz modifications + if (!empty($objExercise->id) && $objExercise->edit_exercise_in_lp == false) { + $form->freeze(); + } + + // FORM VALIDATION + if (isset($_POST['submitQuestion']) && $form->validate()) { + // Question + $objQuestion->processCreation($form, $objExercise); + $objQuestion->processAnswersCreation($form, $objExercise); + + if (isset($_GET['editQuestion'])) { + if (empty($exerciseId)) { + Display::addFlash(Display::return_message(get_lang('ItemUpdated'))); + $url = 'ptest_admin.php?exerciseId='.$exerciseId.'&'.api_get_cidreq().'&editQuestion='.$objQuestion->id; + echo ''; + exit; + } + echo ''; + } else { + // New question + $page = 1; + $length = api_get_configuration_value('question_pagination_length'); + if (!empty($length)) { + $page = round($objExercise->getQuestionCount() / $length); + } + echo ''; + } + } else { + if (isset($questionName)) { + echo '

'.$questionName.'

'; + } + if (!empty($pictureName)) { + echo ''; + } + if (!empty($msgErr)) { + echo Display::return_message($msgErr); + } + // display the form + $form->display(); + } +} diff --git a/main/exercise/result.php b/main/exercise/result.php index 91611f62f1c..9874d27f007 100755 --- a/main/exercise/result.php +++ b/main/exercise/result.php @@ -57,6 +57,7 @@ $htmlHeadXtra[] = ''; $htmlHeadXtra[] = ''; $htmlHeadXtra[] = ''; +$htmlHeadXtra[] = api_get_js('chartjs/Chart.min.js'); if (!empty($objExercise->getResultAccess())) { $htmlHeadXtra[] = api_get_css(api_get_path(WEB_LIBRARY_PATH).'javascript/epiclock/renderers/minute/epiclock.minute.css'); diff --git a/main/img/icons/22/new_personality_test.png b/main/img/icons/22/new_personality_test.png new file mode 100644 index 0000000000000000000000000000000000000000..6e80bd0995e26c87ceffd71632ed4ff373268192 GIT binary patch literal 791 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EW)6k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+7<~hLLR^80!y_Y=l$44~%7LJxqhtSp1MlCzU%GUule5d> z#fyuIi+=z99T^=H5*iv16r7TpcJ|!4($dn<(9l(@SNHe#hebsA1q3uUHL0p=aB_3A zad5hOc+Q$V+t}FH)ZAP|Ok6@rdey2`9v+_7wzi)>eTqv+`0(Mw?AddSOw2N}vfjOW zucWG$os+YC`O3(s=$SKTc6N7f+_*6*IeEv+I^1l9r8&i+}g- zT~1zpc}4Y#6)U!F+b$#`nv|SUUS2(S-aKm?TVqqR$y25-U%p&dPv6VOH!UqaHa@%*n&Mea8+TAK!KB*C(f> z{Qv)dj)wPTV1VYA1o;I6xpV{!|H8_y0EaktaVzP>VJ?XT9{0e%8il5|CW+>( zb-ZT6>7ugPDTxbxtT~ijtQZx~q#kBA);aAds;4uvV}tJj3AHsU%qkI*5)x0&eEM|5 zdFO&dLc0W7?R*Y77~K}vl9A;$ZRlk6=s4=oI#u~VlTN}#n|}%&1yL$j_}LP=j3sr> zyvyx-lV5*MWy6N<5UwLvdDbWdUF3M=V0Qk1OiNOG$qxOWDGwJ(&gw9D$lcC;{HJ1z zFVdQ&MBb@00y~;{Qv*} literal 0 HcmV?d00001 diff --git a/main/img/icons/22/ptaod.png b/main/img/icons/22/ptaod.png new file mode 100644 index 0000000000000000000000000000000000000000..cf09a853906c4c9aa86c7d9cf563e80c63239679 GIT binary patch literal 897 zcmc(d`%jW_6vp4wn$lcu<*iyP?FTQ_D%aI&Wm2NeIm3pnnmAF!Tvpg^)kwi4wY&u8 z%9T^Rl$c30O+`gO@YRA01p^HgAqbza1+8tT2xP0o!2d|H;}ps5SBxT&rziwyaz>XD_L4l33G+r(H%a zcd@Cyz2hT1+4@0AvOmAkKB8;Z*%1J0#AEg)P%7E-%kkw$aj2tkTesMTu=YA4cGdKZ z$Var`mqDO851uA+#bZcqu6ow|%{X5xQ2}AuKn`;>v)Gc#;`4%ex2$**`IlUu)o;*UI;8rQlLj$8x z6XVaK>;DvVaCKY;+KaWg=o9!`MrPu&It+GbL;6Tj%>YC*Dpp_~1*~mHeaxg%1t${Kw&{ws5c5Fc{t&6GZ2T+A1*k zz$%vVV4OKxSFoFm?>IzBt6xna6GWN43}5xkGe!WXDzoL7@rG@{A10%E8=@ z+5S6I)Q`^^bjm_QGFSm|QmIIo6hS7}XHp8UdXULg1FGO;rdx`T7!*XHCwE1KcXaKc zE7xkT-^I!qFmh2`R78EP6Z%v>)wk6b@zw<6A0GF{$*(IuJxQh0FdT9t>%#uBP`2hs nLT|QbZpyt&$L?%!_XvR2r_-}SKA*dYMg-xo@zE_&DS3YZhkHIx literal 0 HcmV?d00001 diff --git a/main/img/icons/22/ptorder.png b/main/img/icons/22/ptorder.png new file mode 100644 index 0000000000000000000000000000000000000000..5533565c2c3421e0fa55849d8388b08a985f0d8c GIT binary patch literal 509 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EW)6k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XRs{Hjxc={nnY`!X|KkjE4qa{DeERpMssE!z|6gVJpDg_P z)Ay@4@1##zS+(i(|2+)oh>DMj$`t$d~BiFA! zeQVve+P!x}_1a_qYh3$wo;Ph;Vc{3sx$W%b%a;@9>|S#0#{aj^ox9iXIegrA!e+yU zW&fYuD_wQu|Br7|r%wHzB=GocI6 zElPs?f`MFe0E2am;3=RP%ROBjLn?0dTs$e(q`=Uc*lr}XRB6*mqqR9u0yno;2f8W0Tb75!;VR+)h zc5GF6O7oogrWzr5hsjVXK1{r~@8ziwIcrc)QM-pE?K|Igol|9@WHa^b#xX!4h2 z{^OggTMUw`p`VEnp1{{P_Iud_T`SGj4J`UnH&`Kif<-Uww)0*>eivyAIl) z@L&JCBrIc})ZLd6FR%L~FVkPDWdEJ-V56_ZeWpip^XE7(SM)LS3{BOz^d)Uc_&)yJ z|1}$19%}pR)lLYW&iCZ*=F%?-IWp0+;#wD6k$ZH~Q=oKLnE&L(iyQwfJ?q}U$gpy; W{ok7pZIXd*WbkzLb6Mw<&;$U+#`O6B literal 0 HcmV?d00001 diff --git a/main/img/icons/32/new_personality_test.png b/main/img/icons/32/new_personality_test.png new file mode 100644 index 0000000000000000000000000000000000000000..4578986febefc973e3eeef534eadaac0497e969f GIT binary patch literal 1336 zcmc&z`&W_&0R5UZJw8sl*=A);Lrm;JvzAt~NohVqe1)Q!rlc7@LQJ7q8Z)VMwxn{N z(2nH-)XZ1KP**CMh2=y=@lmJ*BBUWNd41!**gfaod+x9Io_h_2479V}WDNj-U2u?p zq}jKa>0)JJ)(Hj~X||1ofLKCQ_8CHcG9C*cQnOEEU4k={)3A})E!!|^J4hy9=TjTbgzob9aJb3!&J|gRD@`8Q7o4D`XR=L_*$JVJ2y8kFvb9aNvBh`LEL^VybOXx-HB0>7cWM7 z`=zZ8e1X7V0BQGXc_X9Bg{6TT52ZAy(P$KXR)C=YGGCZ*GO@Y&$@r(`Ta}fP#U&@_ ztq0wmH5$#}%ig)U`N_%2kkGJIy`i|c*tiPjojpHQ@e2r|H7fOU7RxYOy1b&i8F*Zw zQq`xU&CPS%qw39r6%itlQReu60`OTEng#%0^#uDPq6-Akyj3*M&)#yG+fKdd#70mS z5CWGH)Xu*WVm$FbFknYurGX8NU!AZGW}Nskdn*4~iAJf9QXKws!tKzGgtnqNeKicS z?zsux^Aqrg#fQ8?GKZkJHtCXc`((dkoOAB-zUirRlxd%8i?DLQDm)Y>=l3-ELS@9Q z+nf$O)&Yn+k(hu~yFqGe|K@lMQs#jzU`pJd#CBI5s(_F@GF~=aeOqjMDKrAJ$>|-d zFXrxANe0X1*8L1`v5Xnc(K^?O=(qe}=(aK(*`4b>%HXw92sCetxotPOPS%M7rbZ?ad~I#{9Q38J9mllx;& zO=Wwn!}Pi@_@XP@iP&*4Q7GNZg*4Y^;c>h6jnU#H*& zNgnav@cMGoGH~*&bYkIi@n_xT#@s2Bf(N)VR15`j}_<8kvgwd~(Wx3{(WYvX__ z%FM-fM@*u1q0e_$_{F=igpa*bzOaG*iU(}DNYXq+Zkndi3S#l&PrvpSn(xJ}K*ht>y34`^<8&tz3V3B7R8dJoW|rmr9T TC;ycBSwL_A(*N<1q$~dck5L4# literal 0 HcmV?d00001 diff --git a/main/img/icons/64/ptaod.png b/main/img/icons/64/ptaod.png new file mode 100644 index 0000000000000000000000000000000000000000..aea3374df10cc1c55d0ca77a4d07ce6e387d55fe GIT binary patch literal 2718 zcmV;P3Ssq$P)(_`g8%^e{{R4h=l}px2mk>USO5SzmjD14Z`WEM zkN^M*BuPX;RCwC#n|X9p^%ciI@4eYFAu}NfNemD|5(tES3%hIqBnSc;m#Wxm#d@q* zL~y~1P!W-071tiCqU{kykR|MtO_U{ol7uW^Ac=&qWuGOpz4!XZK*F%J!2$yz_ndd; z%$?u;y>q|6`}^MeyKkPLsw%g#h}`1R2*9llfaXdDKl{zDq<{e3Y(-JJsH&>Hp&Sv# zlP-_EB+wJ|#>~`2SAFe-`jX(3rMo8t=%jy+?cB;38ybXGx^aR>1uE?>HsuxD3(BmA zj=BnaElEw4>u0R0^}41lPp>E_tyXVzS+MaZ&+pz}I-|aR)oU4_{dQlhK_hBnLW6Gj zJ>4%pO2T1#soRk8!B>q01?2ERy+*znNG(vWK_3uk^q`KsrL%y55_5~EK~ZR|1N;nt zr@<9M7s`{L&%k8NqD01-;oaL) zSh|hqu+E&Z?jXD={E)qZl(*c*!*47?r;%!HrhffBvCT{zeCcJ&)oMA5_Z+SrZ*{uaaIlEEclV=pn289JvGz)cY|1NRlSs=b z@VUpbefgyJif3fUXd=xIY=xrXu=(rDSb)yp& z0kE-f6n4*g8Z-&ITp~xR*7MV;B*G1ejE-Bxnj^oZ)R}{*XEW%ReJopsZ4d)T*u)k z+nWwBqGPnrJ;rp6VcEWXvWm-m#yi|H`wLIACbN(c9isgc0rQ`l%cvKMD2-o5R8%ED zc$O2Of#W5C?AkGu7ao~O>oAj#&Ain9OkeT?vsa~GP_zo%(Ww;+?i=8v?;RV-qXQEe z`sQ*dGR<CPR_Et)_??B=Gc%Cw zb|ixVO~=k8{^=Ql6WS_gTY}Pc2S8R-CVjqzuhW0NsDcaBYnhzfg_(ExZjJ4Sj`Pfy z-;q;N?z4YoMI|XVf)5@Z>_ckBYuA!L<7E;;Lom4Ar~w9)8YeXw2iSGjc<#tOcv=vI zNWVnD+RQ?BA34e4xepT+9LR;Iz5#QW@<8vlw2iE5&Aqg27Z3GK;MtMgd;oBIWd8i^ zZf37aXW3IJ-UCuz9z`=|&?71e+rfiWm6xG%_e%p$FVlCW8{Fx6Ol7A36T9vg4ar%m6a$S7lPS@Ha7O+I>c;7b~%u2 zRSgpWeNYgBB2%*`onym>fuf+Iq9`h=qEd4(3q!MJ$N^3K1AtMdq0H*I?C%zb8~u5y zP^Xd5h$3ZH$0bvq#oM$h@`tx%2I`OEyKPkLq5l3vSnr*aZv7p86cow?z?XHlqjvs?&+f zpeNVnpz66-=+L*P|2W|BK^=H}P=|VtZyjdhqlrWKXyTB1jc=ZkLQvbb99_7O>OFgK zRac`84#wQKFa0M^#uOj#|5Er`izz;yt_v4lv-umJ3^fAK2tXqMjQ}(PaHUfGGi+Z9 z*2-J3C1$t4wEobd23E9(zoj?u2uKzpM(p{xP3?DU1(;R1-3n!O4GdHjP8Bt<2uKz} z(UCR$*dl%SmTCjTmw221f&e<;Tw}*!gJ}bxvIvGe29M5fAQ9ju921iGfNbhuU908e zDIkho$Mxqr7k01nEps5L0*?IHfVRMPH5Uu3)lH&JH`XSQ`zb7X(QBmxJRX=n5Mpzo zl?!%-!5=vYUeWdbK_-5C-K$Y>;P_hz4j~4*D1>&-yy%Sb9g_l=B%Y+8P0V#qW zD6^&W;nnFd}4-s=Ror|U?~T^5p;%|7=U3y9D2Uz zHbg|YFZXq=(-5RVBY7A72Z|X;(ZV$;%&}_0*{t?Bf*LH^8}Fg&P@z}%tn5soTF#IiW;p`7i2bur~(r! zy?=oesOwOEWd`@z|HP<3wG8+C<|f_(Th%;93avR#2r8Z>$y0ldIQ-3WV?>FB>#UOk za1@9Ff>f}zgDIbS?F~ir5SOt3!4 z8}18%6+tkp8k%IlCsQFR)^E-OoT#!>ezN2$rNMgzi9mJ>1C@4C7dP(3L15|&4viOp zs7Q#s7gm{|uLabtFz0T+IbUjZ@bBDTD9Ou45GeWeTcA!PpgAqks*8o{ol$qyoVcw= z_=JT5C8xG07*qoM6N<$f+wRWz5oCK literal 0 HcmV?d00001 diff --git a/main/img/icons/64/ptorder.png b/main/img/icons/64/ptorder.png new file mode 100644 index 0000000000000000000000000000000000000000..3c9c45e42c4ac9f560237e5821d60638b1b27b71 GIT binary patch literal 1470 zcmc&z`#aNn0RC)_2s@TbsH8gRN^%|@BuC-&dmssxc zM6O|Mu0_jbbD6tmX^b}P%=SIbUvb{&dEfVWe|mp-pEuD3ZHG`epa1|s*rRM*cirkg z%FFC(ZbS69UDFD;y%z39#D|lxw{hT-AJG?g#GZil$GPILe)qz9aTWl;$SzLqw)v%1 zP%1}!t@h0*GV@LD0|wkJ85MnOY95VEC7G zN~KarD%q8+{33|+<#s7;M&G|K<;&piy>19RF;Ly9_87Tw>C1tO! z@t4*FGqdy19c6)VlR|AE`&^%OR)=otd??InXro2uRe$=thg1srFz<{_=>IGC`)az^as$rU>)acy2ZZz!1t@imYKh4-L`Yts| zB>O?nwfxY`%g>6jm3l`KGajGfz+ud6MeI6 zbG{Sa_#T)sITuQXM`SJ|%B*Wy5fiRjR%JHZ3LE66^k~HBMg{ZaE7-7AwHv}|Bb%>o z?Sv#4VldzIMwl}*4AP7JRX@-s}ht8`)Q zhpZ+G%9s5rDZYQ`6}t3zTfthg{!#a>(pW5Z`DMRMj{Lhg!8g$jS87t3#Fa7_1eU1> zlQfmI<4wp$VJ^WJHSU(t-ea~DPzU?}tc4@h+l6FH)IhgZvb%~Fl~PoWSA!eqr+sq3 zt@P1rmW601rTacH2@r!IQ$vqZ@c+ zuE6hr;DHapdvc1IzqWmT?}J}|W|=zPr0f@w_vti8A&A{hHzB-TFl}H3DqtRHcr;%`5^G~Cyv*gp=4*wn4Xp|3rXKRr4=kFe9>BBX2{ss78`WuEZ*I(<*6)aVNuLgMFLM51Jnt{jXEM0XB_&$oji!#qb7)1WUFBa_1H$>3 o#-#_jxtKb;{(Cgn?VD=N*sC|$8ezD$Lf@A=kh&DZ7YkhaP#x>J%X z%}PS(I2jA&>sa(nHu;{IP-xrdeb--cpXa<@&+|OzbMlnd z33A8%9A!oMF0Q}yMQ&KKlMmS=CWK50j3)ty;FvQcLzlCGp(Fw+Fqj(migW+~6bebg1K0rI8h{rakWd|1eZ8=-kV>UM0q;TA2iO6+ zgfs3IDx$e1cvY!~32L)Y+86)_0rb;>XmBgprM9*nR-mdcC_rN%3kPs+stXUum2GQY zDl#gDUjZ1;1ud2U`Th+q&LfT>%M*Bqoga5a(}F-d+k5`oDnAOG58FLF`?WNe2xa09 zH2~wZzOPB*T*0NMJ#b?9c_qN*JlyL_oLySE-Ylr=d<~1qG6V@EdI?8Ht`wC($!baa z+u2rt+)HVCGhWct3lSMaq+%I_QfWp_*Si^ULKZVRrf}4jt z{+|N-l>Q0@Kyj0cql4$g_ro{CJ>KItWT~JVG_{*DaPfnWRZP?9xoNp{Rk~y#VQ}c- zKZ9O=esj)eVS+XvTj^`H#-EUJgRO1*Sc$!fy|Lzpj&&}kqMDXM$BuFkGoemg;*X<`t> zQCNtItTav?Hqs7Q5Wu4ctnp}lp@2gmAAJui2l)`tM!w4@MBtQ&JS z!s&&#fqDL!rX8DeTq{s$@-r*D0Z{RSTRN7`+iC}-Xa4+{T~&TSB1r)H`J7wpPP&f` z-%;ZDl!e>WXs~tDb}4ZgHoBPdY$?LkOtz#OJX`i35{i!kg3jZ2LUEqN<3Z2QeS$(~9UMtbZ87TdgwYniMs#hhi&#<| zciUe?y>pI35dLBY5}0`lN%SeByUO`~pUtAh0e15nr1sOQpylwJz1J`_>Sm0SV1{g6 zW>en(dDvt?7Ni5q0Ure60z!*9?4|vHro9 z0IR7mif6P=we{rk^~j6)YPd$UZ@$HNTFV7r+sS$hYx@@Au3;^jeR=hOXEMI`_fK7v zWRV_CIiO^C<8K6B4u>}ypu=kT8&mc)jH4PB&4Ll!+uL!irFi`| zkn!ZljoFpXz~Xa*o(E2$+wcP$1=id8RjK)uITl!f*sgW!7VlGWb(_pe4`LdOFHZzU zwI9*qjdm(2;57}WFe~;_WedmS+X_sN9$o_*$9A)_$F|7`7&Fho9p}v3$R=l zMnMlw{kcD$@GHoDdCHWtak}-Bim23zL8D{rPWe1B1G-DNMO1hF(G)(GPIz;*RhX>T zq2J6db+)6ii}$D5Sj6P5KnD{@=$t+Syn6X!selectPtType() == EXERCISE_PT_TYPE_PTEST) { + $ptest = true; + } + // Getting information of the current exercise. $exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId); $exercise_id = $exercise_stat_info['exe_exo_id']; @@ -498,7 +503,7 @@ $total_weight = 0; if ($type == 'simple') { foreach ($question_list as $my_question_id) { - $objQuestionTmp = Question::read($my_question_id, $objExercise->course); + $objQuestionTmp = Question::read($my_question_id, $objExercise->course, true, $ptest); $total_weight += $objQuestionTmp->selectWeighting(); } } @@ -521,7 +526,7 @@ } // Creates a temporary Question object - $objQuestionTmp = Question::read($my_question_id, $objExercise->course); + $objQuestionTmp = Question::read($my_question_id, $objExercise->course, true, $ptest); $myChoiceDegreeCertainty = null; if ($objQuestionTmp->type === MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY) { @@ -612,6 +617,58 @@ $objExercise->selectPropagateNeg(), $hotspot_delineation_result ); + } else if ($objQuestionTmp->type === QUESTION_PT_TYPE_AGREE_OR_DISAGREE) { + $myChoiceTmp = []; + $myChoiceTmp['agree'] = isset($_REQUEST['choice-agree'][$my_question_id]) && !empty($_REQUEST['choice-agree'][$my_question_id]) + ? (int) $_REQUEST['choice-agree'][$my_question_id] + : 0; + $myChoiceTmp['disagree'] = isset($_REQUEST['choice-disagree'][$my_question_id]) && !empty($_REQUEST['choice-disagree'][$my_question_id]) + ? (int) $_REQUEST['choice-disagree'][$my_question_id] + : 0; + + $result = $objExercise->manage_answer( + $exeId, + $my_question_id, + $myChoiceTmp, + 'exercise_result', + $hot_spot_coordinates, + true, + false, + false, + $objExercise->selectPropagateNeg(), + $hotspot_delineation_result + ); + } else if ($objQuestionTmp->type === QUESTION_PT_TYPE_AGREE_SCALE) { + $result = $objExercise->manage_answer( + $exeId, + $my_question_id, + json_encode($my_choice), + 'exercise_result', + $hot_spot_coordinates, + true, + false, + false, + $objExercise->selectPropagateNeg(), + $hotspot_delineation_result + ); + } else if ($objQuestionTmp->type === QUESTION_PT_TYPE_AGREE_REORDER) { + $myChoiceTmp = []; + foreach ($_REQUEST['choice'][$my_question_id] as $key => $value) { + $myChoiceTmp[$key] = $value; + } + + $result = $objExercise->manage_answer( + $exeId, + $my_question_id, + json_encode($myChoiceTmp), + 'exercise_result', + $hot_spot_coordinates, + true, + false, + false, + $objExercise->selectPropagateNeg(), + $hotspot_delineation_result + ); } else { $result = $objExercise->manage_answer( $exeId, diff --git a/main/inc/ajax/model.ajax.php b/main/inc/ajax/model.ajax.php index 332a6ecb8d5..6d3fe297f1b 100755 --- a/main/inc/ajax/model.ajax.php +++ b/main/inc/ajax/model.ajax.php @@ -43,6 +43,7 @@ $action, [ 'get_exercise_results', + 'get_ptest_exercise_results', 'get_exercise_results_report', 'get_work_student_list_overview', 'get_hotpotatoes_exercise_results', @@ -591,6 +592,7 @@ function getWhereClause($col, $oper, $val) ); break; case 'get_exercise_results': + case 'get_ptest_exercise_results': $exercise_id = $_REQUEST['exerciseId']; if (isset($_GET['filter_by_user']) && !empty($_GET['filter_by_user'])) { @@ -1389,6 +1391,47 @@ function getWhereClause($col, $oper, $val) $whereCondition ); break; + case 'get_ptest_exercise_results': + $is_allowedToEdit = api_is_allowed_to_edit(null, true) || + api_is_drh() || + api_is_student_boss() || + api_is_session_admin(); + if ($is_allowedToEdit || api_is_student_boss()) { + $columns = [ + 'firstname', + 'lastname', + 'username', + 'group_name', + 'exe_duration', + 'start_date', + 'exe_date', + 'user_ip', + 'lp', + 'actions', + ]; + $officialCodeInList = api_get_setting('show_official_code_exercise_result_list'); + if ($officialCodeInList === 'true') { + $columns = array_merge(['official_code'], $columns); + } + } + + $result = ExerciseLib::get_exam_results_data( + $start, + $limit, + $sidx, + $sord, + $exercise_id, + $whereCondition, + false, + null, + false, + false, + [], + false, + false, + true + ); + break; case 'get_exercise_results_report': $columns = [ 'firstname', @@ -2313,6 +2356,7 @@ function getWhereClause($col, $oper, $val) 'get_session_progress', 'get_exercise_progress', 'get_exercise_results', + 'get_ptest_exercise_results', 'get_exercise_results_report', 'get_work_student_list_overview', 'get_hotpotatoes_exercise_results', diff --git a/main/inc/lib/api.lib.php b/main/inc/lib/api.lib.php index 17d8c7340fa..aec34e5a693 100644 --- a/main/inc/lib/api.lib.php +++ b/main/inc/lib/api.lib.php @@ -123,6 +123,7 @@ define('TOOL_DROPBOX', 'dropbox'); define('TOOL_QUIZ', 'quiz'); define('TOOL_TEST_CATEGORY', 'test_category'); +define('TOOL_PTEST_CATEGORY', 'ptest_category'); define('TOOL_USER', 'user'); define('TOOL_GROUP', 'group'); define('TOOL_BLOGS', 'blog_management'); @@ -468,6 +469,9 @@ define('EXERCISE_FEEDBACK_TYPE_EXAM', 2); // NoFeedback - Show score only define('EXERCISE_FEEDBACK_TYPE_POPUP', 3); // Popup BT#15827 +define('EXERCISE_PT_TYPE_CLASSIC', 0); // Default quiz +define('EXERCISE_PT_TYPE_PTEST', 1); // Personality test quiz + define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS', 0); //show score and expected answers define('RESULT_DISABLE_NO_SCORE_AND_EXPECTED_ANSWERS', 1); //Do not show score nor answers define('RESULT_DISABLE_SHOW_SCORE_ONLY', 2); //Show score only @@ -477,6 +481,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_PT_TYPE_PTEST', 9); define('EXERCISE_MAX_NAME_SIZE', 80); @@ -505,6 +510,15 @@ define('READING_COMPREHENSION', 21); define('MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY', 22); +// Question Personality Test types +define('QUESTION_PT_TYPE_CATEGORY_RANKING', 23); // count how many times the user clicked in different categories +define('QUESTION_PT_TYPE_AGREE_OR_DISAGREE', 24); // Select two options out of 5 (most agree and least agree) +define('QUESTION_PT_TYPE_AGREE_SCALE', 25); // On a scale of 5 set a value - Most agree = 5, least agree is 1 +define('QUESTION_PT_TYPE_AGREE_REORDER', 26); // Re-order these 5 words by most agree to least agree. + +define('ANSWER_AGREE', 1); +define('ANSWER_DISAGREE', 2); + define('EXERCISE_CATEGORY_RANDOM_SHUFFLED', 1); define('EXERCISE_CATEGORY_RANDOM_ORDERED', 2); define('EXERCISE_CATEGORY_RANDOM_DISABLED', 0); diff --git a/main/inc/lib/database.constants.inc.php b/main/inc/lib/database.constants.inc.php index cb1757009f9..10528c7751f 100755 --- a/main/inc/lib/database.constants.inc.php +++ b/main/inc/lib/database.constants.inc.php @@ -197,6 +197,7 @@ define('TABLE_QUIZ_QUESTION_CATEGORY', 'quiz_question_category'); define('TABLE_QUIZ_QUESTION_REL_CATEGORY', 'quiz_question_rel_category'); define('TABLE_QUIZ_REL_CATEGORY', 'quiz_rel_category'); +define('TABLE_QUIZ_CATEGORY_PTEST', 'quiz_category_ptest'); // New SCORM tables define('TABLE_LP_MAIN', 'lp'); diff --git a/main/inc/lib/exercise.lib.php b/main/inc/lib/exercise.lib.php index e452d0b0cc0..f0a7db22283 100644 --- a/main/inc/lib/exercise.lib.php +++ b/main/inc/lib/exercise.lib.php @@ -55,10 +55,14 @@ public static function showQuestion( } $course = $exercise->course; + $ptest = false; + if ($exercise->selectPtType() == EXERCISE_PT_TYPE_PTEST) { + $ptest = true; + } // Change false to true in the following line to enable answer hinting $debug_mark_answer = $show_answers; // Reads question information - if (!$objQuestionTmp = Question::read($questionId, $course)) { + if (!$objQuestionTmp = Question::read($questionId, $course, true, $ptest)) { // Question not found return false; } @@ -472,6 +476,68 @@ function handleRadioRow(event, question_id, answer_id) { ); } + if ($answerType == QUESTION_PT_TYPE_AGREE_OR_DISAGREE) { + $header = Display::tag( + 'th', + get_lang('Option'), + ['class' => 'text-center', 'style' => 'width:1px;white-space:nowrap;'] + ); + $header .= Display::tag('th', get_lang('Answer')); + + $s .= '
'.get_lang('Choice').''.get_lang('Answer').'
'; + $s .= Display::tag( + 'tr', + $header, + ['style' => 'text-align:left;'] + ); + + $optionsList = []; + $agreeOption = 0; + $disagreeOption = 0; + } + + if ($answerType == QUESTION_PT_TYPE_AGREE_SCALE) { + $dataList = ' + + + + + + '; + + $header = Display::tag( + 'th', + get_lang('Answer') + ); + $header .= Display::tag('th', get_lang('QuestionWeighting')); + + $s .= '
'; + $s .= Display::tag( + 'tr', + $header, + ['style' => 'text-align:left;'] + ); + } + + if ($answerType == QUESTION_PT_TYPE_AGREE_REORDER) { + $header = Display::tag( + 'th', + get_lang('Option'), + ['class' => 'text-center', 'style' => 'width:1px;white-space:nowrap;'] + ); + $header .= Display::tag('th', get_lang('Answer')); + + $s .= '
'; + $s .= Display::tag( + 'tr', + $header, + ['style' => 'text-align:left;'] + ); + + $optionsList = []; + $selectValue = []; + } + for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) { $answer = $objAnswerTmp->selectAnswer($answerId); $answerCorrect = $objAnswerTmp->isCorrect($answerId); @@ -484,6 +550,7 @@ function handleRadioRow(event, question_id, answer_id) { case UNIQUE_ANSWER_NO_OPTION: case UNIQUE_ANSWER_IMAGE: case READING_COMPREHENSION: + case QUESTION_PT_TYPE_CATEGORY_RANKING: $input_id = 'choice-'.$questionId.'-'.$answerId; if (isset($user_choice[0]['answer']) && $user_choice[0]['answer'] == $numAnswer) { $attributes = [ @@ -1326,6 +1393,72 @@ class="window window_left_question window{$questionId}_question"> } $matching_correct_answer++; } + break; + case QUESTION_PT_TYPE_AGREE_OR_DISAGREE: + $optionsList[$numAnswer] = $answerId; + + if (isset($user_choice[0]['answer'])) { + $answerOption = explode(',', $user_choice[0]['answer']); + if ($answerOption[0] == $numAnswer) { + $agreeOption = $numAnswer; + } + if ($answerOption[1] == $numAnswer) { + $disagreeOption = $numAnswer; + } + } + + $answer = Security::remove_XSS($answer, STUDENT); + + $s.= ''; + + break; + case QUESTION_PT_TYPE_AGREE_SCALE: + $input_id = 'choice-'.$questionId.'-'.$answerId; + $value = 3; + if (!empty($user_choice[0]['answer'])) { + $answerList = json_decode($user_choice[0]['answer'], true); + $value = $answerList[$numAnswer]; + } + + $attributes = []; + $attributes['id'] = $input_id; + $attributes['min'] = '1'; + $attributes['max'] = '5'; + $attributes['step'] = '1'; + $attributes['style'] = 'max-width:70%; margin:0 15px; display:inline;'; + $attributes['list'] = 'steplist'; + + $answer_input = Display::input( + 'range', + 'choice['.$questionId.']['.$numAnswer.']', + $value, + $attributes + ); + + $answer = Security::remove_XSS($answer, STUDENT); + + $s .= ''; + $s .= ''; + $s .= ''; + $s .= ''; + + break; + case QUESTION_PT_TYPE_AGREE_REORDER: + $optionsList[$numAnswer] = $answerId; + + if (isset($user_choice[0]['answer'])) { + $answerOption = json_decode($user_choice[0]['answer'], true); + for ($i = 1; $i <= 5; $i++) { + if ($answerOption[$i] == $numAnswer) { + $selectValue[$i] = $numAnswer; + } + } + } + + $answer = Security::remove_XSS($answer, STUDENT); + + $s.= ''; + break; } } @@ -1341,10 +1474,74 @@ class="window window_left_question window{$questionId}_question"> MULTIPLE_ANSWER_TRUE_FALSE, MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE, MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY, + QUESTION_PT_TYPE_AGREE_OR_DISAGREE, + QUESTION_PT_TYPE_AGREE_SCALE, + QUESTION_PT_TYPE_AGREE_REORDER, ] )) { $s .= '
'.$answerId.''.$answer.'
'.$answer.''.$answer_input.'
'.$answerId.''.$answer.'
'; } + + if ($answerType == QUESTION_PT_TYPE_AGREE_OR_DISAGREE) { + $s .= '
'; + $s .= '
'; + $s .= ''; + $s .= '
'; + $s .= ''; + $s .= '
'; + $s .= '
'; + $s .= '
'; + + $s .= '
'; + $s .= ''; + $s .= '
'; + $s .= ''; + $s .= '
'; + $s .= '
'; + $s .= '
'; + $s .= '
'; + } + + if ($answerType == QUESTION_PT_TYPE_AGREE_REORDER) { + $s .= '
'; + for ($i = 5; $i > 0; $i--) { + $s .= '
'; + $s .= ''; + $s .= '
'; + $s .= ''; + $s .= '
'; + $s .= '
'; + $s .= '
'; + } + $s .= '
'; + } if ($answerType == DRAGGABLE) { $isVertical = $objQuestionTmp->extra == 'v'; @@ -1970,7 +2167,8 @@ public static function get_exam_results_data( $showExerciseCategories = false, $userExtraFieldsToAdd = [], $useCommaAsDecimalPoint = false, - $roundValues = false + $roundValues = false, + $ptest = false ) { //@todo replace all this globals global $filter; @@ -2389,67 +2587,76 @@ public static function get_exam_results_data( $actions .= Display::return_icon('teacher.png', get_lang('Teacher')); } } - $revisedLabel = ''; - switch ($revised) { - case 0: - $actions .= "". - Display:: return_icon( - 'quiz.png', - get_lang('Qualify') - ); - $actions .= ''; - $revisedLabel = Display::label( - get_lang('NotValidated'), - 'info' + if ($ptest) { + $actions .= "". + Display:: return_icon( + 'quiz.png', + get_lang('Qualify') ); - break; - case 1: - $actions .= "". - Display:: return_icon( - 'edit.png', - get_lang('Edit'), - [], - ICON_SIZE_SMALL + $actions .= ''; + } else { + $revisedLabel = ''; + switch ($revised) { + case 0: + $actions .= "". + Display:: return_icon( + 'quiz.png', + get_lang('Qualify') + ); + $actions .= ''; + $revisedLabel = Display::label( + get_lang('NotValidated'), + 'info' ); - $actions .= ''; - $revisedLabel = Display::label( - get_lang('Validated'), - 'success' - ); - break; - case 2: //finished but not marked as such - $actions .= ''. - Display:: return_icon( - 'lock.png', - get_lang('MarkAttemptAsClosed'), + break; + case 1: + $actions .= "". + Display:: return_icon( + 'edit.png', + get_lang('Edit'), + [], + ICON_SIZE_SMALL + ); + $actions .= ''; + $revisedLabel = Display::label( + get_lang('Validated'), + 'success' + ); + break; + case 2: //finished but not marked as such + $actions .= ''. + Display:: return_icon( + 'lock.png', + get_lang('MarkAttemptAsClosed'), + [], + ICON_SIZE_SMALL + ); + $actions .= ''; + $revisedLabel = Display::label( + get_lang('Unclosed'), + 'warning' + ); + break; + case 3: //still ongoing + $actions .= Display:: return_icon( + 'clock.png', + get_lang('AttemptStillOngoingPleaseWait'), [], ICON_SIZE_SMALL ); - $actions .= ''; - $revisedLabel = Display::label( - get_lang('Unclosed'), - 'warning' - ); - break; - case 3: //still ongoing - $actions .= Display:: return_icon( - 'clock.png', - get_lang('AttemptStillOngoingPleaseWait'), - [], - ICON_SIZE_SMALL - ); - $actions .= ''; - $revisedLabel = Display::label( - get_lang('Ongoing'), - 'danger' - ); - break; + $actions .= ''; + $revisedLabel = Display::label( + get_lang('Ongoing'), + 'danger' + ); + break; + } } if ($filter == 2) { @@ -2471,23 +2678,25 @@ public static function get_exam_results_data( .Display::return_icon('info.png', $ip) .''; - $recalculateUrl = api_get_path(WEB_CODE_PATH).'exercise/recalculate.php?'. - api_get_cidreq().'&'. - http_build_query([ - 'id' => $id, - 'exercise' => $exercise_id, - 'user' => $results[$i]['exe_user_id'], - ]); - $actions .= Display::url( - Display::return_icon('reload.png', get_lang('RecalculateResults')), - $recalculateUrl, - [ - 'data-exercise' => $exercise_id, - 'data-user' => $results[$i]['exe_user_id'], - 'data-id' => $id, - 'class' => 'exercise-recalculate', - ] - ); + if (!$ptest) { + $recalculateUrl = api_get_path(WEB_CODE_PATH).'exercise/recalculate.php?'. + api_get_cidreq().'&'. + http_build_query([ + 'id' => $id, + 'exercise' => $exercise_id, + 'user' => $results[$i]['exe_user_id'], + ]); + $actions .= Display::url( + Display::return_icon('reload.png', get_lang('RecalculateResults')), + $recalculateUrl, + [ + 'data-exercise' => $exercise_id, + 'data-user' => $results[$i]['exe_user_id'], + 'data-id' => $id, + 'class' => 'exercise-recalculate', + ] + ); + } $filterByUser = isset($_GET['filter_by_user']) ? (int) $_GET['filter_by_user'] : 0; $delete_link = ' 0) { + return round(($sum/$count),1); + } + + return 0; + break; + case QUESTION_PT_TYPE_AGREE_REORDER: + $sum = 0; + $count = 0; + $userListUse = []; + while ($row = Database::fetch_array($result, 'ASSOC')) { + if (in_array($row['exe_user_id'], $userListUse)) { + // It is not the last attempt + continue; + } + $userListUse[] = $row['exe_user_id']; + $answerValue = json_decode($row['answer'], true); + foreach ($answerValue as $key => $value) { + if ($value == $answer_id) { + $sum += $key; + $count++; + break; + } + } + } + + if ($count > 0) { + return round(($sum/$count),1); + } + + return 0; + break; case MATCHING: case MATCHING_DRAGGABLE: default: @@ -4116,6 +4419,166 @@ public static function get_number_students_answer_count( return $return; } + /** + * @param int $answer_id + * @param int $question_id + * @param int $exercise_id + * @param string $course_code + * @param int $session_id + * @param string $question_type + * @param string $correct_answer + * @param string $current_answer + * + * @return int + */ + public static function getNumberStudentsAnswerCountGraph( + $answer_id, + $question_id, + $exercise_id, + $course_code, + $session_id, + $question_type = null, + $correct_answer = null, + $current_answer = null + ) { + $track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); + $track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT); + $courseTable = Database::get_main_table(TABLE_MAIN_COURSE); + $courseUser = Database::get_main_table(TABLE_MAIN_COURSE_USER); + $courseUserSession = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER); + + $question_id = (int) $question_id; + $answer_id = (int) $answer_id; + $exercise_id = (int) $exercise_id; + $courseId = api_get_course_int_id($course_code); + $session_id = (int) $session_id; + $orderByCondition = ''; + + switch ($question_type) { + case QUESTION_PT_TYPE_CATEGORY_RANKING: + case QUESTION_PT_TYPE_AGREE_OR_DISAGREE: + case QUESTION_PT_TYPE_AGREE_SCALE: + case QUESTION_PT_TYPE_AGREE_REORDER: + $answer_condition = ''; + $select_condition = ' DISTINCT exe_user_id, e.exe_id, answer '; + $orderByCondition = ' ORDER BY a.tms DESC'; + break; + } + + if (empty($session_id)) { + $courseCondition = " + INNER JOIN $courseUser cu + ON cu.c_id = c.id AND cu.user_id = exe_user_id"; + $courseConditionWhere = " AND relation_type <> 2 AND cu.status = ".STUDENT; + } else { + $courseCondition = " + INNER JOIN $courseUserSession cu + ON cu.c_id = a.c_id AND cu.user_id = exe_user_id"; + $courseConditionWhere = ' AND cu.status = 0 '; + } + + $sql = "SELECT $select_condition + FROM $track_exercises e + INNER JOIN $track_attempt a + ON ( + a.exe_id = e.exe_id AND + e.c_id = a.c_id AND + e.session_id = a.session_id + ) + INNER JOIN $courseTable c + ON c.id = a.c_id + $courseCondition + WHERE + exe_exo_id = $exercise_id AND + a.c_id = $courseId AND + e.session_id = $session_id AND + $answer_condition + question_id = $question_id AND + e.status = '' + $courseConditionWhere + $orderByCondition"; + + $result = Database::query($sql); + $return = 0; + if ($result) { + switch ($question_type) { + case QUESTION_PT_TYPE_CATEGORY_RANKING: + $return = 0; + $userListUse = []; + while ($row = Database::fetch_array($result, 'ASSOC')) { + if (in_array($row['exe_user_id'], $userListUse)) { + // It is not the last attempt + continue; + } + $userListUse[] = $row['exe_user_id']; + if ($row['answer'] == $answer_id) { + $return++; + } + } + + return $return; + break; + case QUESTION_PT_TYPE_AGREE_OR_DISAGREE: + $return = [0, 0]; + $userListUse = []; + while ($row = Database::fetch_array($result, 'ASSOC')) { + if (in_array($row['exe_user_id'], $userListUse)) { + // It is not the last attempt + continue; + } + $userListUse[] = $row['exe_user_id']; + $answerValue = explode(',', $row['answer']); + if ($answerValue[0] == $answer_id) { + $return[0]++; + } + if ($answerValue[1] == $answer_id) { + $return[1]++; + } + } + + return $return; + break; + case QUESTION_PT_TYPE_AGREE_SCALE: + $sum = 0; + $userListUse = []; + while ($row = Database::fetch_array($result, 'ASSOC')) { + if (in_array($row['exe_user_id'], $userListUse)) { + // It is not the last attempt + continue; + } + $userListUse[] = $row['exe_user_id']; + $answerValue = json_decode($row['answer'], true); + $sum += $answerValue[$answer_id]; + } + + return $sum; + break; + case QUESTION_PT_TYPE_AGREE_REORDER: + $sum = 0; + $userListUse = []; + while ($row = Database::fetch_array($result, 'ASSOC')) { + if (in_array($row['exe_user_id'], $userListUse)) { + // It is not the last attempt + continue; + } + $userListUse[] = $row['exe_user_id']; + $answerValue = json_decode($row['answer'], true); + foreach ($answerValue as $key => $value) { + if ($value == $answer_id) { + $sum += $key; + break; + } + } + } + + return $sum; + break; + } + } + + return $return; + } + /** * @param array $answer * @param string $user_answer @@ -4366,11 +4829,18 @@ public static function displayQuestionListByAttempt( ) { $origin = api_get_origin(); $courseCode = api_get_course_id(); + $courseId = api_get_course_int_id(); $sessionId = api_get_session_id(); // Getting attempt info $exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId); + // Get type + $ptest = false; + if ($objExercise->selectPtType() == EXERCISE_PT_TYPE_PTEST) { + $ptest = true; + } + // Getting question list $question_list = []; $studentInfo = []; @@ -4421,6 +4891,7 @@ public static function displayQuestionListByAttempt( RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER, RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS, RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING, + RESULT_DISABLE_PT_TYPE_PTEST, ] )) { $show_results = true; @@ -4516,6 +4987,10 @@ public static function displayQuestionListByAttempt( } } + if ($objExercise->selectPtType() == EXERCISE_PT_TYPE_PTEST) { + echo $objExercise->showPtestCharts($exeId, $courseId, $sessionId); + } + // Display text when test is finished #4074 and for LP #4227 $endOfMessage = $objExercise->getTextWhenFinished(); if (!empty($endOfMessage)) { @@ -4550,7 +5025,7 @@ public static function displayQuestionListByAttempt( if (!empty($question_list)) { foreach ($question_list as $questionId) { // Creates a temporary Question object - $objQuestionTmp = Question::read($questionId, $objExercise->course); + $objQuestionTmp = Question::read($questionId, $objExercise->course, true, $ptest); // This variable came from exercise_submit_modal.php ob_start(); $choice = null; @@ -4725,7 +5200,7 @@ public static function displayQuestionListByAttempt( $totalScoreText = null; $certificateBlock = ''; - if (($show_results || $show_only_score) && $showTotalScore) { + if (($show_results || $show_only_score) && $showTotalScore && !$ptest) { if ($result['answer_type'] == MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY) { echo '

'.get_lang('YourResults').'


'; } diff --git a/main/inc/lib/exercise_show_functions.lib.php b/main/inc/lib/exercise_show_functions.lib.php index 6cfc8289e24..5f407bb7a06 100755 --- a/main/inc/lib/exercise_show_functions.lib.php +++ b/main/inc/lib/exercise_show_functions.lib.php @@ -364,7 +364,7 @@ public static function display_unique_or_multiple_answer( break; } - $icon = in_array($answerType, [UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION]) ? 'radio' : 'checkbox'; + $icon = in_array($answerType, [UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION, QUESTION_PT_TYPE_CATEGORY_RANKING]) ? 'radio' : 'checkbox'; $icon .= $studentChoice ? '_on' : '_off'; $icon .= '.png'; $iconAnswer = in_array($answerType, [UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION]) ? 'radio' : 'checkbox'; @@ -424,6 +424,10 @@ public static function display_unique_or_multiple_answer( $showComment = true; } + if ($exercise->selectPtType() == EXERCISE_PT_TYPE_PTEST) { + $showComment = false; + } + if ($showComment) { echo ''; $color = 'black'; @@ -445,6 +449,88 @@ public static function display_unique_or_multiple_answer( echo ''; } + /** + * Display the answers to a ptest question. + * + * @param Exercise $exercise + * @param int $answerType Answer type + * @param int $studentChoice Student choice + * @param string $answer Textual answer + * @param string $answerComment Comment on answer + * @param bool $export + */ + public static function display_ptest_answer( + $exercise, + $answerType, + $studentChoice, + $answer, + $answerComment, + $export = false + ) { + if ($export) { + $answer = strip_tags_blacklist($answer, ['title', 'head']); + // Fix answers that contains this tags + $tags = [ + '', + '', + '', + '', + ]; + $answer = str_replace($tags, '', $answer); + } + + $studentChoiceInt = (int) $studentChoice; + $showComment = false; + + switch ($answerType) { + case QUESTION_PT_TYPE_CATEGORY_RANKING: + $iconOff = ''; + $iconOn = ''; + $icon .= $studentChoice ? $iconOn: $iconOff; + break; + case QUESTION_PT_TYPE_AGREE_OR_DISAGREE: + $icon = ''; + if ($studentChoice == ANSWER_AGREE) { + $icon = ''; + } + + if ($studentChoice == ANSWER_DISAGREE) { + $icon = ''; + } + break; + case QUESTION_PT_TYPE_AGREE_SCALE: + case QUESTION_PT_TYPE_AGREE_REORDER: + $icon = ''; + $color = 'text-primary'; + switch ($studentChoice) { + case 1: + case 2: + $color = 'text-danger'; + break; + case 3: + $color = 'text-warning'; + break; + case 4: + case 5: + $color = 'text-success'; + break; + } + for ($i = 0; $i < 5; $i++) { + if ($i < $studentChoice) { + $icon .= ' '; + } else { + $icon .= ' '; + } + } + break; + } + + echo ''; + echo ''.$icon.''; + echo ''.$answer.''; + echo ''; + } + /** * Display the answers to a multiple choice question. * diff --git a/main/lang/english/trad4all.inc.php b/main/lang/english/trad4all.inc.php index 1ab2d863385..c29e78bc8b2 100644 --- a/main/lang/english/trad4all.inc.php +++ b/main/lang/english/trad4all.inc.php @@ -8446,4 +8446,29 @@ $CompilatioNonToAnalyse = "Your selection contains no jobs to analyze. Only jobs managed by Compilatio and not already scanned can be sent."; $CompilatioComunicationAjaxImpossible = "AJAX communication with the Compilatio server impossible. Please retry later."; $UserClassExplanation = "Information: The list of classes below contains the list of classes you have already registered in your course. If this list is empty, use the + green above to add classes."; +$PtestCategoryRanking = "Choose a word (or sentence) from a set of possible answers"; +$PtestAgreeOrDisagree = "Select the most agree and disagree options"; +$PtestAgreeScale = "Set a value for each category"; +$PtestAgreeReorder = "Re-order words by most agree to least agree"; +$PtestType = "Type of exercise Personality Test"; +$PtCategories = "Define personal characteristics for used in questions"; +$NoDefinedCategories = "No defined personal characteristics for questions"; +$PtestCategory = "Personal characteristics"; +$PtestCategoryList = "Personal characteristics list"; +$PtestCategoryName = "Title"; +$PtestCategoryPonderation = "Points"; +$PtestCategoryDescription = "Description"; +$AddPTestFeature = "Add personal characteristics"; +$ModifyPTestFeature = "Modify personal characteristics"; +$BarGraphRepresentation = "Bar graph representation"; +$RadarPlotRepresentation = "Radar plot representation"; +$MostAgree = "Most Agree"; +$LeastAgree = "Least Agree"; +$AgreeScale5 = "Top"; +$AgreeScale4 = "2nd highest"; +$AgreeScale3 = "Middle"; +$AgreeScale2 = "2nd lowest"; +$AgreeScale1 = "Bottom"; +$AddPT = "New personality test"; +$ExerciseGraph = "Attemps graph"; ?> \ No newline at end of file diff --git a/main/lang/spanish/trad4all.inc.php b/main/lang/spanish/trad4all.inc.php index c2871353145..053361acaab 100644 --- a/main/lang/spanish/trad4all.inc.php +++ b/main/lang/spanish/trad4all.inc.php @@ -8458,4 +8458,29 @@ $EnterYourNewPassword = "Introduzca su nueva contraseña aquí."; $RepeatYourNewPassword = "Introduzca su nueva contraseña una vez más, para reducir la probabilidad de errores."; $UserClassExplanation = "Información: La lista de clases a continuación contiene la lista de clases que ya ha registrado en su clase. Si esta lista está vacía, use el + verde arriba para agregar clases."; +$PtestCategoryRanking = "Elige una palabra (o sentencia) de un conjunto de posibles respuestas"; +$PtestAgreeOrDisagree = "Seleccionar las opciones más de acuerdo y en desacuerdo"; +$PtestAgreeScale = "Establecer un valor para cada categoría"; +$PtestAgreeReorder = "Reordenar sentencias por más de acuerdo a menos de acuerdo"; +$PtestType = "Tipo de ejercicio Test de Personalidad"; +$PtCategories = "Definir característica personal para usar en las preguntas"; +$NoDefinedCategories = "Sin características personales definidas para las preguntas"; +$PtestCategory = "Características personales"; +$PtestCategoryList = "Listado de características personales"; +$PtestCategoryName = "Nombre"; +$PtestCategoryPonderation = "Puntos"; +$PtestCategoryDescription = "Descripción"; +$AddPTestFeature = "Añadir característica personal"; +$ModifyPTestFeature = "Modificar característica personal"; +$BarGraphRepresentation = "Representación en gráfico de barras"; +$RadarPlotRepresentation = "Representación en gráfico de Radar"; +$MostAgree = "Más de acuerdo"; +$LeastAgree = "Menos de acuerdo"; +$AgreeScale5 = "Superior"; +$AgreeScale4 = "2º superior"; +$AgreeScale3 = "Intermedio"; +$AgreeScale2 = "2º inferior"; +$AgreeScale1 = "Inferior"; +$AddPT = "Add personality test"; +$ExerciseGraph = "Gráficos de los intentos"; ?> \ No newline at end of file diff --git a/src/Chamilo/CourseBundle/Entity/CQuizAnswer.php b/src/Chamilo/CourseBundle/Entity/CQuizAnswer.php index 2cc83deb4a0..3f6537cabf1 100644 --- a/src/Chamilo/CourseBundle/Entity/CQuizAnswer.php +++ b/src/Chamilo/CourseBundle/Entity/CQuizAnswer.php @@ -112,6 +112,13 @@ class CQuizAnswer */ protected $destination; + /** + * @var string + * + * @ORM\Column(name="ptest_category", type="text", nullable=true) + */ + protected $ptestCategory; + /** * @var string * @@ -129,6 +136,7 @@ public function __construct() $this->hotspotCoordinates = null; $this->hotspotType = null; $this->destination = null; + $this->ptestCategory = null; $this->answerCode = null; } @@ -372,6 +380,30 @@ public function getDestination() return $this->destination; } + /** + * Set personality test category. + * + * @param string $ptestCategory + * + * @return CQuizAnswer + */ + public function setPtestCategory($ptestCategory) + { + $this->ptestCategory = empty($ptestCategory) ? null : $ptestCategory; + + return $this; + } + + /** + * Get ptestCategory. + * + * @return string + */ + public function getPtestCategory() + { + return $this->ptestCategory; + } + /** * Set answerCode. * From c60313baa7975d0a9b8a39fb14efb7ba00012714 Mon Sep 17 00:00:00 2001 From: Nosolored Date: Fri, 15 Nov 2019 09:12:00 +0100 Subject: [PATCH 2/8] Applied fixed from FlintCI --- main/exercise/exercise.class.php | 248 +++++++++--------- main/exercise/ptest_admin.php | 3 +- main/exercise/ptest_agree_disagree.class.php | 2 +- main/exercise/ptest_agree_reorder.class.php | 4 +- main/exercise/ptest_agree_scale.class.php | 46 ++-- .../exercise/ptest_category_ranking.class.php | 7 +- main/exercise/ptest_stats.php | 10 +- main/exercise/ptest_stats_graph.php | 4 +- main/exercise/ptests_category.php | 9 +- main/exercise/question.class.php | 11 +- main/exercise/question_ptest_admin.inc.php | 2 +- main/inc/ajax/exercise.ajax.php | 8 +- main/inc/lib/api.lib.php | 1 - main/inc/lib/exercise.lib.php | 14 +- main/inc/lib/exercise_show_functions.lib.php | 2 +- 15 files changed, 179 insertions(+), 192 deletions(-) diff --git a/main/exercise/exercise.class.php b/main/exercise/exercise.class.php index 5fc59724265..6daebc03dbc 100755 --- a/main/exercise/exercise.class.php +++ b/main/exercise/exercise.class.php @@ -2448,7 +2448,7 @@ public function createForm($form, $type = 'full') if ($type == 'ptest') { $form->addElement('hidden', 'exercisePtType', EXERCISE_PT_TYPE_PTEST); $form->addElement('hidden', 'results_disabled', RESULT_DISABLE_PT_TYPE_PTEST); - + // Type of questions disposition on page $radios = []; $radios[] = $form->createElement( @@ -2469,81 +2469,81 @@ public function createForm($form, $type = 'full') get_lang('SequentialExercise'), '2', [ - 'onclick' => 'check_per_page_one()', - 'id' => 'option_page_one', + 'onclick' => 'check_per_page_one()', + 'id' => 'option_page_one', ] ); $form->addGroup($radios, null, get_lang('QuestionsPerPage')); $option = [ - EX_Q_SELECTION_ORDERED => get_lang('OrderedByUser'), - // Defined by user - EX_Q_SELECTION_RANDOM => get_lang('Random'), - // 1-10, All - 'per_categories' => '--------'.get_lang('UsingCategories').'----------', - // Base (A 123 {3} B 456 {3} C 789{2} D 0{0}) --> Matrix {3, 3, 2, 0} - EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_ORDERED => get_lang('OrderedCategoriesAlphabeticallyWithQuestionsOrdered'), - // A 123 B 456 C 78 (0, 1, all) - EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED => get_lang('RandomCategoriesWithQuestionsOrdered'), - // C 78 B 456 A 123 - EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_RANDOM => get_lang('OrderedCategoriesAlphabeticallyWithRandomQuestions'), - // A 321 B 654 C 87 - EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM => get_lang('RandomCategoriesWithRandomQuestions'), - // C 87 B 654 A 321 - //EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED_NO_GROUPED => get_lang('RandomCategoriesWithQuestionsOrderedNoQuestionGrouped'), - /* B 456 C 78 A 123 - 456 78 123 - 123 456 78 - */ - //EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM_NO_GROUPED => get_lang('RandomCategoriesWithRandomQuestionsNoQuestionGrouped'), - /* - A 123 B 456 C 78 - B 456 C 78 A 123 - B 654 C 87 A 321 - 654 87 321 - 165 842 73 - */ - //EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_ORDERED => get_lang('OrderedCategoriesByParentWithQuestionsOrdered'), - //EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_RANDOM => get_lang('OrderedCategoriesByParentWithQuestionsRandom'), + EX_Q_SELECTION_ORDERED => get_lang('OrderedByUser'), + // Defined by user + EX_Q_SELECTION_RANDOM => get_lang('Random'), + // 1-10, All + 'per_categories' => '--------'.get_lang('UsingCategories').'----------', + // Base (A 123 {3} B 456 {3} C 789{2} D 0{0}) --> Matrix {3, 3, 2, 0} + EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_ORDERED => get_lang('OrderedCategoriesAlphabeticallyWithQuestionsOrdered'), + // A 123 B 456 C 78 (0, 1, all) + EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED => get_lang('RandomCategoriesWithQuestionsOrdered'), + // C 78 B 456 A 123 + EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_RANDOM => get_lang('OrderedCategoriesAlphabeticallyWithRandomQuestions'), + // A 321 B 654 C 87 + EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM => get_lang('RandomCategoriesWithRandomQuestions'), + // C 87 B 654 A 321 + //EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED_NO_GROUPED => get_lang('RandomCategoriesWithQuestionsOrderedNoQuestionGrouped'), + /* B 456 C 78 A 123 + 456 78 123 + 123 456 78 + */ + //EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM_NO_GROUPED => get_lang('RandomCategoriesWithRandomQuestionsNoQuestionGrouped'), + /* + A 123 B 456 C 78 + B 456 C 78 A 123 + B 654 C 87 A 321 + 654 87 321 + 165 842 73 + */ + //EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_ORDERED => get_lang('OrderedCategoriesByParentWithQuestionsOrdered'), + //EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_RANDOM => get_lang('OrderedCategoriesByParentWithQuestionsRandom'), ]; - + $form->addElement( - 'select', - 'question_selection_type', - [get_lang('QuestionSelection')], - $option, - [ - 'id' => 'questionSelection', - 'onchange' => 'checkQuestionSelection()', - ] - ); - + 'select', + 'question_selection_type', + [get_lang('QuestionSelection')], + $option, + [ + 'id' => 'questionSelection', + 'onchange' => 'checkQuestionSelection()', + ] + ); + $pageConfig = api_get_configuration_value('allow_quiz_results_page_config'); if ($pageConfig) { $group = [ - $form->createElement( - 'checkbox', - 'hide_expected_answer', - null, - get_lang('HideExpectedAnswer') - ), - $form->createElement( - 'checkbox', - 'hide_total_score', - null, - get_lang('HideTotalScore') - ), - $form->createElement( - 'checkbox', - 'hide_question_score', - null, - get_lang('HideQuestionScore') - ), + $form->createElement( + 'checkbox', + 'hide_expected_answer', + null, + get_lang('HideExpectedAnswer') + ), + $form->createElement( + 'checkbox', + 'hide_total_score', + null, + get_lang('HideTotalScore') + ), + $form->createElement( + 'checkbox', + 'hide_question_score', + null, + get_lang('HideQuestionScore') + ), ]; $form->addGroup($group, null, get_lang('ResultsConfigurationPage')); } - + $displayMatrix = 'none'; $displayRandom = 'none'; $selectionType = $this->getQuestionSelectionType(); @@ -2555,33 +2555,33 @@ public function createForm($form, $type = 'full') $displayMatrix = 'block'; break; } - + $form->addElement( - 'html', - '
' - ); + 'html', + '
' + ); // Number of random question. $max = ($this->id > 0) ? $this->getQuestionCount() : 10; $option = range(0, $max); $option[0] = get_lang('No'); $option[-1] = get_lang('AllQuestionsShort'); $form->addElement( - 'select', - 'randomQuestions', - [ - get_lang('RandomQuestions'), - get_lang('RandomQuestionsHelp'), - ], - $option, - ['id' => 'randomQuestions'] - ); + 'select', + 'randomQuestions', + [ + get_lang('RandomQuestions'), + get_lang('RandomQuestionsHelp'), + ], + $option, + ['id' => 'randomQuestions'] + ); $form->addElement('html', '
'); $form->addElement( - 'html', - '
' - ); - + 'html', + '
' + ); + // Category selection. $cat = new TestCategory(); $cat_form = $cat->returnCategoryForm($this); @@ -2600,20 +2600,20 @@ public function createForm($form, $type = 'full') // Category name. $radio_display_cat_name = [ - $form->createElement('radio', 'display_category_name', null, get_lang('Yes'), '1'), - $form->createElement('radio', 'display_category_name', null, get_lang('No'), '0'), + $form->createElement('radio', 'display_category_name', null, get_lang('Yes'), '1'), + $form->createElement('radio', 'display_category_name', null, get_lang('No'), '0'), ]; $form->addGroup($radio_display_cat_name, null, get_lang('QuestionDisplayCategoryName')); - + // Hide question title. $group = [ - $form->createElement('radio', 'hide_question_title', null, get_lang('Yes'), '1'), - $form->createElement('radio', 'hide_question_title', null, get_lang('No'), '0'), + $form->createElement('radio', 'hide_question_title', null, get_lang('Yes'), '1'), + $form->createElement('radio', 'hide_question_title', null, get_lang('No'), '0'), ]; $form->addGroup($group, null, get_lang('HideQuestionTitle')); - + $allow = api_get_configuration_value('allow_quiz_show_previous_button_setting'); - + if ($allow === true) { // Hide question title. $group = [ @@ -2634,11 +2634,11 @@ public function createForm($form, $type = 'full') ]; $form->addGroup($group, null, get_lang('ShowPreviousButton')); } - + // Attempts $attempt_option = range(0, 10); $attempt_option[0] = get_lang('Infinite'); - + $form->addElement( 'select', 'exerciseAttempts', @@ -2646,47 +2646,47 @@ public function createForm($form, $type = 'full') $attempt_option, ['id' => 'exerciseAttempts'] ); - + // Exercise time limit $form->addElement( - 'checkbox', - 'activate_start_date_check', - null, - get_lang('EnableStartTime'), - ['onclick' => 'activate_start_date()'] - ); - + 'checkbox', + 'activate_start_date_check', + null, + get_lang('EnableStartTime'), + ['onclick' => 'activate_start_date()'] + ); + if (!empty($this->start_time)) { $form->addElement('html', '
'); } else { $form->addElement('html', ''); $form->addElement( - 'checkbox', - 'activate_end_date_check', - null, - get_lang('EnableEndTime'), - ['onclick' => 'activate_end_date()'] - ); - + 'checkbox', + 'activate_end_date_check', + null, + get_lang('EnableEndTime'), + ['onclick' => 'activate_end_date()'] + ); + if (!empty($this->end_time)) { $form->addElement('html', '
'); } else { $form->addElement('html', ''); $display = 'block'; - + $form->addElement('html', '
 
'); $form->addElement('checkbox', 'review_answers', null, get_lang('ReviewAnswers')); $form->addElement('html', '
'); - + // Timer control $form->addElement( 'checkbox', @@ -2694,12 +2694,12 @@ public function createForm($form, $type = 'full') null, get_lang('EnableTimerControl'), [ - 'onclick' => 'option_time_expired()', - 'id' => 'enabletimercontrol', - 'onload' => 'check_load_time()', + 'onclick' => 'option_time_expired()', + 'id' => 'enabletimercontrol', + 'onload' => 'check_load_time()', ] ); - + $expired_date = (int) $this->selectExpiredTime(); if (($expired_date != '0')) { @@ -2726,7 +2726,7 @@ public function createForm($form, $type = 'full') false, $editor_config ); - + $allow = api_get_configuration_value('allow_notification_setting_per_exercise'); if ($allow === true) { $settings = ExerciseLib::getNotificationSettings(); @@ -2742,13 +2742,13 @@ public function createForm($form, $type = 'full') } $form->addGroup($group, '', [get_lang('EmailNotifications')]); } - + $form->addCheckBox( 'update_title_in_lps', null, get_lang('UpdateTitleInLps') ); - + $form->addElement('html', '
'); //End advanced setting $form->addElement('html', '
'); @@ -9425,7 +9425,7 @@ public static function exerciseGrid($categoryId, $keyword = '') $embeddableIcon = Display::return_icon('om_integration.png', get_lang('ThisQuizCanBeEmbeddable')); $url .= Display::div($embeddableIcon, ['class' => 'pull-right']); } - + if ($row['pt_type'] == EXERCISE_PT_TYPE_PTEST) { $embeddableIcon = Display::return_icon('new_personality_test.png', get_lang('PtestType')); $url .= Display::div($embeddableIcon, ['class' => 'pull-right']); @@ -9461,7 +9461,7 @@ public static function exerciseGrid($categoryId, $keyword = '') // Exercise results $resultsLink = '
'. Display::return_icon('test_results.png', get_lang('Results'), '', ICON_SIZE_SMALL).''; - + if ($limitTeacherAccess) { if (api_is_platform_admin()) { $actions .= $resultsLink; @@ -9470,7 +9470,7 @@ public static function exerciseGrid($categoryId, $keyword = '') // Exercise results $actions .= $resultsLink; } - + // Auto launch if ($autoLaunchAvailable) { $autoLaunch = $exercise->getAutoLaunch(); @@ -9496,7 +9496,7 @@ public static function exerciseGrid($categoryId, $keyword = '') ); } } - + // Export $actions .= Display::url( Display::return_icon('cd.png', get_lang('CopyExercise')), @@ -9506,7 +9506,7 @@ public static function exerciseGrid($categoryId, $keyword = '') 'href' => 'exercise.php?'.api_get_cidreq().'&choice=copy_exercise&sec_token='.$token.'&exerciseId='.$row['id'], ] ); - + // Clean exercise if ($locked == false) { $clean = Display::url( @@ -9530,7 +9530,7 @@ public static function exerciseGrid($categoryId, $keyword = '') ICON_SIZE_SMALL ); } - + if ($limitTeacherAccess && !api_is_platform_admin()) { $clean = ''; } @@ -9549,7 +9549,7 @@ public static function exerciseGrid($categoryId, $keyword = '') $actions .= $resultsLink; } } - + // Visible / invisible // Check if this exercise was added in a LP if ($exercise->exercise_was_added_in_lp == true) { @@ -9601,11 +9601,11 @@ public static function exerciseGrid($categoryId, $keyword = '') ), 'exercise.php?action=exportqti2&exerciseId='.$row['id'].'&'.api_get_cidreq() ); - + if ($limitTeacherAccess && !api_is_platform_admin()) { $export = ''; } - + $actions .= $export; } } else { @@ -9865,7 +9865,7 @@ public static function exerciseGrid($categoryId, $keyword = '') } if ($my_result_disabled == RESULT_DISABLE_PT_TYPE_PTEST) { $attempt_text = $num.' '.get_lang('Attempts'); - } + } } } diff --git a/main/exercise/ptest_admin.php b/main/exercise/ptest_admin.php index 96687807163..20e557c25ba 100644 --- a/main/exercise/ptest_admin.php +++ b/main/exercise/ptest_admin.php @@ -146,7 +146,7 @@ $zip_folder = new PclZip($temp_zip_file); $zip_folder->add($temp_xml_file, PCLZIP_OPT_REMOVE_ALL_PATH); $name = 'qti2_export_'.$qid.'.zip'; - + DocumentManager::file_send_for_download($temp_zip_file, true, $name); unlink($temp_zip_file); unlink($temp_xml_file); @@ -323,7 +323,6 @@ false ); } - } elseif (isset($_GET['newQuestion'])) { // we are in create a new question from question pool not in a test echo '
'; diff --git a/main/exercise/ptest_agree_disagree.class.php b/main/exercise/ptest_agree_disagree.class.php index bb715af25c3..d3eb1ecfb4e 100644 --- a/main/exercise/ptest_agree_disagree.class.php +++ b/main/exercise/ptest_agree_disagree.class.php @@ -12,7 +12,7 @@ * extending the class question * * @author Jose Angel Ruiz (NOSOLORED) - * + * * @package chamilo.exercise */ class PtestAgreeOrDisagree extends Question diff --git a/main/exercise/ptest_agree_reorder.class.php b/main/exercise/ptest_agree_reorder.class.php index d7556ef6fee..b3da2e24468 100644 --- a/main/exercise/ptest_agree_reorder.class.php +++ b/main/exercise/ptest_agree_reorder.class.php @@ -212,11 +212,11 @@ public function return_header(Exercise $exercise, $counter = null, $score = []) { $header = parent::return_header($exercise, $counter); //, $score); $header .= ''; - + $header .= ''; $header .= ''; $header .= ''; - + return $header; } diff --git a/main/exercise/ptest_agree_scale.class.php b/main/exercise/ptest_agree_scale.class.php index 64618713629..7bffdb38bd5 100644 --- a/main/exercise/ptest_agree_scale.class.php +++ b/main/exercise/ptest_agree_scale.class.php @@ -38,13 +38,13 @@ public function createAnswersForm($form) // Getting the exercise list /** @var Exercise $obj_ex */ $obj_ex = Session::read('objExercise'); - + $editor_config = [ 'ToolbarSet' => 'TestProposedAnswer', 'Width' => '100%', 'Height' => '125', ]; - + // Categories options select $category = new PTestCategory(); $categoriesList = $category->getCategoryListInfo($obj_ex->selectId()); @@ -52,12 +52,12 @@ public function createAnswersForm($form) foreach ($categoriesList as $categoryItem) { $categoriesOptions[$categoryItem->id] = (string) $categoryItem->name; } - + //this line defines how many questions by default appear when creating a choice question // The previous default value was 2. See task #1759. $nb_answers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : count($categoriesList); $nb_answers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0)); - + $html = '
'.get_lang('Choice').''.get_lang('Answer').'
@@ -67,10 +67,10 @@ public function createAnswersForm($form) '; - + $form->addHeader(get_lang('Answers')); $form->addHtml($html); - + $defaults = []; if (!empty($this->id)) { $answer = new Answer($this->id); @@ -80,7 +80,7 @@ public function createAnswersForm($form) } } $form->addElement('hidden', 'nb_answers'); - + //$temp_scenario = []; if ($nb_answers < 1) { $nb_answers = 1; @@ -88,14 +88,14 @@ public function createAnswersForm($form) get_lang('YouHaveToCreateAtLeastOneAnswer') ); } - + for ($i = 1; $i <= $nb_answers; $i++) { $form->addHtml(''); if (isset($answer) && is_object($answer)) { $defaults['answer['.$i.']'] = isset($answer->answer[$i]) ? $answer->answer[$i] : ''; $defaults['ptest_category['.$i.']'] = isset($answer->ptest_category[$i]) ? $answer->ptest_category[$i] : 0; } - + $renderer = $form->defaultRenderer(); $renderer->setElementTemplate( '', @@ -116,30 +116,30 @@ public function createAnswersForm($form) ' value = "'.$i.'"' ); $answer_number->freeze(); - + $form->addHtmlEditor('answer['.$i.']', null, null, false, $editor_config); - + $form->addRule( 'answer['.$i.']', get_lang('ThisFieldIsRequired'), 'required' ); - + $form->addSelect( 'ptest_category['.$i.']', null, $categoriesOptions ); - + $form->addHtml(''); } - + $form->addHtml(''); $form->addHtml('
{error}
{element}
'); - + global $text; $buttonGroup = []; - + if ($obj_ex->edit_exercise_in_lp == true || (empty($this->exerciseList) && empty($obj_ex->id)) ) { @@ -158,7 +158,7 @@ public function createAnswersForm($form) ); $form->addGroup($buttonGroup); } - + if (!empty($this->id)) { $form->setDefaults($defaults); } else { @@ -230,7 +230,7 @@ public function processAnswersCreation($form, $exercise) { $objAnswer = new Answer($this->id); $nb_answers = $form->getSubmitValue('nb_answers'); - + for ($i = 1; $i <= $nb_answers; $i++) { $answer = trim($form->getSubmitValue('answer['.$i.']')); $goodAnswer = false; @@ -238,7 +238,7 @@ public function processAnswersCreation($form, $exercise) $weighting = 0; $ptestCategory = (int) $form->getSubmitValue('ptest_category['.$i.']'); $dest = ''; - + $objAnswer->createAnswer( $answer, $goodAnswer, @@ -251,10 +251,10 @@ public function processAnswersCreation($form, $exercise) $ptestCategory ); } - + // saves the answers into the data base $objAnswer->save(); - + $this->save($exercise); } @@ -265,11 +265,11 @@ public function return_header(Exercise $exercise, $counter = null, $score = []) { $header = parent::return_header($exercise, $counter); //, $score); $header .= ''; - + $header .= ''; $header .= ''; $header .= ''; - + return $header; } diff --git a/main/exercise/ptest_category_ranking.class.php b/main/exercise/ptest_category_ranking.class.php index 97989e73ab9..57569c7d126 100644 --- a/main/exercise/ptest_category_ranking.class.php +++ b/main/exercise/ptest_category_ranking.class.php @@ -44,7 +44,7 @@ public function createAnswersForm($form) 'Width' => '100%', 'Height' => '125', ]; - + // Categories options select $category = new PTestCategory(); $categoriesList = $category->getCategoryListInfo($obj_ex->selectId()); @@ -118,7 +118,7 @@ public function createAnswersForm($form) ' value = "'.$i.'"' ); $answer_number->freeze(); - + $form->addHtmlEditor('answer['.$i.']', null, null, false, $editor_config); $form->addRule( @@ -126,7 +126,7 @@ public function createAnswersForm($form) get_lang('ThisFieldIsRequired'), 'required' ); - + $form->addSelect( 'ptest_category['.$i.']', null, @@ -161,7 +161,6 @@ public function createAnswersForm($form) $form->addGroup($buttonGroup); } - if (!empty($this->id)) { $form->setDefaults($defaults); } else { diff --git a/main/exercise/ptest_stats.php b/main/exercise/ptest_stats.php index 5a79e451a94..645c34fb59e 100644 --- a/main/exercise/ptest_stats.php +++ b/main/exercise/ptest_stats.php @@ -76,7 +76,7 @@ get_lang('Answer'), get_lang('NumberStudentWhoSelectedIt'), ]; - + $data[$id]['answer'] = $answer_info; $count = ExerciseLib::get_number_students_answer_count( $real_answer_id, @@ -159,7 +159,7 @@ get_lang('Answer'), get_lang('AverageScore'), ]; - + $data[$id]['answer'] = $answer_info; $count = ExerciseLib::get_number_students_answer_count( $real_answer_id, @@ -207,7 +207,7 @@ } $id++; } - + // Format A table $table = new HTML_Table(['class' => 'data_table']); $row = 0; @@ -228,12 +228,10 @@ $row++; } $content .= $table->toHtml(); - + } } - - $interbreadcrumb[] = [ "url" => "exercise.php?".api_get_cidreq(), "name" => get_lang('Exercises'), diff --git a/main/exercise/ptest_stats_graph.php b/main/exercise/ptest_stats_graph.php index 0df77b0c636..af01e997d60 100644 --- a/main/exercise/ptest_stats_graph.php +++ b/main/exercise/ptest_stats_graph.php @@ -47,7 +47,7 @@ $cList = PTestCategory::getCategoryListInfo($objExercise->id); -$categoryList = []; +$categoryList = []; foreach ($cList as $item) { $categoryList[$item->id]['label'] = $item->name; $categoryList[$item->id]['num'] = 0; @@ -116,7 +116,7 @@ } } -$labels = []; +$labels = []; $num = []; foreach ($categoryList as $item) { $labels[] = $item['label']; diff --git a/main/exercise/ptests_category.php b/main/exercise/ptests_category.php index c1fa64d8f88..6b6480c60ba 100644 --- a/main/exercise/ptests_category.php +++ b/main/exercise/ptests_category.php @@ -3,10 +3,6 @@ use ChamiloSession as Session; -/** - hubert.borderiou - Manage tests category page - */ $htmlHeadXtra[] = ' "; $header = Display::tag( 'th', get_lang('Option'), @@ -520,6 +558,49 @@ function handleRadioRow(event, question_id, answer_id) { } if ($answerType == QUESTION_PT_TYPE_AGREE_REORDER) { + echo " + "; + $header = Display::tag( 'th', get_lang('Option'), @@ -1448,6 +1529,8 @@ class="window window_left_question window{$questionId}_question"> if (isset($user_choice[0]['answer'])) { $answerOption = json_decode($user_choice[0]['answer'], true); + //error_log("answerOption"); + //error_log(print_r($answerOption,1)); for ($i = 1; $i <= 5; $i++) { if ($answerOption[$i] == $numAnswer) { $selectValue[$i] = $numAnswer; @@ -1487,7 +1570,7 @@ class="window window_left_question window{$questionId}_question"> $s .= '
'; $s .= ''; $s .= '
'; - $s .= ''; $s .= ''; foreach ($optionsList as $key => $value) { if ($key == $agreeOption) { @@ -1504,7 +1587,7 @@ class="window window_left_question window{$questionId}_question"> $s .= '
'; $s .= ''; $s .= '
'; - $s .= ''; $s .= ''; foreach ($optionsList as $key => $value) { if ($key == $disagreeOption) { @@ -1526,8 +1609,12 @@ class="window window_left_question window{$questionId}_question"> $s .= '
'; $s .= ''; $s .= '
'; - $s .= ''; $s .= ''; + //error_log("i: ".$i); + //error_log(print_r($selectValue,1)); + //error_log(print_r($optionsList,1)); + //error_log(print_r($selectValue[$i],1)); foreach ($optionsList as $key => $value) { if ($key == $selectValue[$i]) { $s .= ''; diff --git a/main/inc/lib/tracking.lib.php b/main/inc/lib/tracking.lib.php index cfebb72e0a5..cfbe6d5a1c6 100755 --- a/main/inc/lib/tracking.lib.php +++ b/main/inc/lib/tracking.lib.php @@ -7538,8 +7538,16 @@ public static function get_user_data( ); if ($export_csv) { - $user['first_connection'] = api_get_local_time($user['first_connection']); - $user['last_connection'] = api_get_local_time($user['last_connection']); + if (!empty($user['first_connection'])) { + $user['first_connection'] = api_get_local_time($user['first_connection']); + } else { + $user['first_connection'] = '-'; + } + if (!empty($user['last_connection'])) { + $user['last_connection'] = api_get_local_time($user['last_connection']); + } else { + $user['last_connection'] = '-'; + } } if (empty($session_id)) { diff --git a/main/inc/lib/webservices/WebService.class.php b/main/inc/lib/webservices/WebService.class.php index 46727e2e3d0..9c16a6bcc4e 100644 --- a/main/inc/lib/webservices/WebService.class.php +++ b/main/inc/lib/webservices/WebService.class.php @@ -86,13 +86,13 @@ public static function isValidUser($username, $password) if (empty($username) || empty($password)) { return false; } - +error_log("89"); $user = UserManager::getManager()->findUserByUsername($username); - +error_log("90"); if (!$user) { return false; } - +error_log("95"); return UserManager::isPasswordValid( $user->getPassword(), $password, From 2d2591607d1156c7430d4612cd5895981a867d1b Mon Sep 17 00:00:00 2001 From: Nosolored Date: Mon, 17 Feb 2020 16:30:47 +0100 Subject: [PATCH 5/8] Update language terms --- main/lang/english/trad4all.inc.php | 2 ++ main/lang/spanish/trad4all.inc.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/main/lang/english/trad4all.inc.php b/main/lang/english/trad4all.inc.php index 5c3e000cf1f..3bed62de1bf 100644 --- a/main/lang/english/trad4all.inc.php +++ b/main/lang/english/trad4all.inc.php @@ -8471,6 +8471,8 @@ $AgreeScale1 = "Bottom"; $AddPT = "New personality test"; $ExerciseGraph = "Attemps graph"; +$PtestCategoryPosition = "Position"; +$PtestCategoryColor = "Colour"; $InsertTwoNames = "Insert your two names"; $AddRightLogo = "Add right logo"; $LearnpathUseScoreAsProgress = "Use score as progress"; diff --git a/main/lang/spanish/trad4all.inc.php b/main/lang/spanish/trad4all.inc.php index 9aa1cbedc07..b9c4193e2e9 100644 --- a/main/lang/spanish/trad4all.inc.php +++ b/main/lang/spanish/trad4all.inc.php @@ -8484,4 +8484,6 @@ $AgreeScale1 = "Inferior"; $AddPT = "Add personality test"; $ExerciseGraph = "Gráficos de los intentos"; +$PtestCategoryPosition = "Posición"; +$PtestCategoryColor = "Color"; ?> \ No newline at end of file From 3bb2c61c8592ac90c73549fa2eadbbc5dd1fc82e Mon Sep 17 00:00:00 2001 From: Nosolored SL Date: Mon, 17 Feb 2020 16:34:25 +0100 Subject: [PATCH 6/8] Applied fixes from FlintCI --- main/admin/resource_sequence.php | 2 +- main/admin/user_move_stats.php | 4 +- main/auth/courses_controller.php | 2 +- main/exercise/exercise.class.php | 23 +++-- main/exercise/ptest_category.class.php | 6 -- main/exercise/ptest_stats.php | 1 - main/exercise/question_pool.php | 2 - main/inc/ajax/statistics.ajax.php | 8 +- main/inc/lib/display.lib.php | 2 +- main/inc/lib/exercise.lib.php | 9 +- main/inc/lib/exercise_show_functions.lib.php | 8 +- main/inc/lib/sessionmanager.lib.php | 85 +++++++++---------- main/inc/lib/usergroup.lib.php | 2 +- main/inc/lib/webservices/WebService.class.php | 7 +- main/install/configuration.dist.php | 7 +- main/lp/learnpath.class.php | 82 +++++++++--------- main/lp/lp_ajax_switch_item_toc.php | 2 - main/lp/lp_edit.php | 1 - main/lp/scorm_api.php | 2 +- main/session/resume_session.php | 1 - main/session/session_list.php | 3 +- main/session/session_list_simple.php | 2 +- main/tracking/lp_report.php | 2 - .../Repository/SequenceResourceRepository.php | 56 ++++++------ .../CourseBundle/Entity/CQuizAnswer.php | 4 +- 25 files changed, 155 insertions(+), 168 deletions(-) diff --git a/main/admin/resource_sequence.php b/main/admin/resource_sequence.php index 1c727230f36..12c99493b8e 100644 --- a/main/admin/resource_sequence.php +++ b/main/admin/resource_sequence.php @@ -133,7 +133,7 @@ 'content' => get_lang('Courses'), ]; -$tabs = Display::tabsOnlyLink($headers,$type === SequenceResource::COURSE_TYPE ? 2 : 1); +$tabs = Display::tabsOnlyLink($headers, $type === SequenceResource::COURSE_TYPE ? 2 : 1); $tpl->assign('create_sequence', $formSequence->returnForm()); $tpl->assign('select_sequence', $selectSequence->returnForm()); diff --git a/main/admin/user_move_stats.php b/main/admin/user_move_stats.php index 57de700bf1b..c2ad2827281 100755 --- a/main/admin/user_move_stats.php +++ b/main/admin/user_move_stats.php @@ -270,7 +270,7 @@ function compare_data($result_message) while ($row = Database::fetch_array($res, 'ASSOC')) { // Checking if the LP exist in the new session //if (in_array($row['lp_id'], array_keys($flat_list))) { - $list[$row['id']] = $row; + $list[$row['id']] = $row; //} } @@ -323,7 +323,7 @@ function compare_data($result_message) while ($row = Database::fetch_array($res, 'ASSOC')) { //Checking if the LP exist in the new session //if (in_array($row['lp_id'], array_keys($flat_list))) { - $list[$row['id']] = $row; + $list[$row['id']] = $row; //} } diff --git a/main/auth/courses_controller.php b/main/auth/courses_controller.php index e8e46000fdd..2793343efa9 100755 --- a/main/auth/courses_controller.php +++ b/main/auth/courses_controller.php @@ -361,7 +361,7 @@ public function getSessionIcon($sessionName) /** * Return Session catalog rendered view. * - * @param array $limit + * @param array $limit */ public function sessionList($limit = []) { diff --git a/main/exercise/exercise.class.php b/main/exercise/exercise.class.php index 5938e7bb53a..fa9f7e50cc7 100755 --- a/main/exercise/exercise.class.php +++ b/main/exercise/exercise.class.php @@ -2576,7 +2576,7 @@ public function createForm($form, $type = 'full') ['id' => 'randomQuestions'] ); $form->addElement('html', '
'); - + $form->addElement( 'html', '
' @@ -2680,7 +2680,7 @@ public function createForm($form, $type = 'full') $form->addElement('date_time_picker', 'end_time'); $form->addElement('html', '
'); - + $display = 'block'; $form->addElement('html', '
 
'); @@ -2701,7 +2701,7 @@ public function createForm($form, $type = 'full') ); $expired_date = (int) $this->selectExpiredTime(); - + if (($expired_date != '0')) { $form->addElement('html', '
'); } else { @@ -2751,7 +2751,6 @@ public function createForm($form, $type = 'full') $form->addElement('html', '
'); //End advanced setting $form->addElement('html', '
'); - } // submit @@ -2831,7 +2830,7 @@ public function createForm($form, $type = 'full') $defaults['on_success_message'] = null; $defaults['on_failed_message'] = null; } - } else if ($type == 'ptest') { + } elseif ($type == 'ptest') { // rules $form->addRule('exerciseAttempts', get_lang('Numeric'), 'numeric'); $form->addRule('start_time', get_lang('InvalidDate'), 'datetime'); @@ -5180,7 +5179,7 @@ public function manage_answer( question_id = $questionId"; $result = Database::query($sql); $choice = Database::result($result, 0, 'answer'); - + $studentChoice = $choice == $answerAutoId ? 1 : 0; } else { $studentChoice = $choice == $answerAutoId ? 1 : 0; @@ -6678,7 +6677,7 @@ public function showPtestCharts($exeId, $courseId = 0, $sessionId = 0) $cList = PTestCategory::getCategoryListInfo($this->id); - $categoryList = []; + $categoryList = []; foreach ($cList as $item) { $categoryList[$item->id]['label'] = $item->name; $categoryList[$item->id]['num'] = 0; @@ -6688,7 +6687,7 @@ public function showPtestCharts($exeId, $courseId = 0, $sessionId = 0) $answerTable = Database::get_course_table(TABLE_QUIZ_ANSWER); $questionTable = Database::get_course_table(TABLE_QUIZ_QUESTION); $attemptsTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT); - + // QUESTION_PT_TYPE_CATEGORY_RANKING $sql = "SELECT an.ptest_category AS id, COUNT(*) AS rep FROM $attemptsTable att INNER JOIN $answerTable an ON att.answer = an.id @@ -6748,7 +6747,7 @@ public function showPtestCharts($exeId, $courseId = 0, $sessionId = 0) $categoryList[$ptestCategoryId]['num'] += $value; } } - + // QUESTION_PT_TYPE_AGREE_REORDER $sql = "SELECT att.answer AS answer FROM $attemptsTable att INNER JOIN $questionTable q ON att.question_id = q.id @@ -6769,7 +6768,7 @@ public function showPtestCharts($exeId, $courseId = 0, $sessionId = 0) } } - $labels = []; + $labels = []; $num = []; $backgroundColor = $borderColor = []; foreach ($categoryList as $item) { @@ -6784,7 +6783,7 @@ public function showPtestCharts($exeId, $courseId = 0, $sessionId = 0) $brColor = 'rgb('.$r.', '.$g.', '.$b.')'; $bgColor = 'rgba('.$r.', '.$g.', '.$b.', 0.6)'; } - + $backgroundColor[] = $bgColor; $borderColor[] = $brColor; } @@ -9556,7 +9555,7 @@ public static function exerciseGrid($categoryId, $keyword = '') // Exercise results $resultsLink = ''. Display::return_icon('test_results.png', get_lang('Results'), '', ICON_SIZE_SMALL).''; - + if ($limitTeacherAccess) { if (api_is_platform_admin()) { $actions .= $resultsLink; diff --git a/main/exercise/ptest_category.class.php b/main/exercise/ptest_category.class.php index 1a545696685..9d464babae5 100644 --- a/main/exercise/ptest_category.class.php +++ b/main/exercise/ptest_category.class.php @@ -350,8 +350,6 @@ public static function getListOfCategoriesIDForTest($exerciseId, $courseId = 0) } /** - * @param Exercise $exercise - * * @return array */ public static function getListOfCategoriesIDForTestObject(Exercise $exercise) @@ -410,8 +408,6 @@ public static function getListOfCategoriesNameForTest($exerciseId, $grouped_by_c } /** - * @param Exercise $exercise - * * @return array */ public static function getListOfCategoriesForTest(Exercise $exercise) @@ -963,8 +959,6 @@ public function getForm(&$form, $action = 'new') /** * Returns the category form. * - * @param Exercise $exercise - * * @return string */ public function returnCategoryForm(Exercise $exercise) diff --git a/main/exercise/ptest_stats.php b/main/exercise/ptest_stats.php index 645c34fb59e..ec79f2eab51 100644 --- a/main/exercise/ptest_stats.php +++ b/main/exercise/ptest_stats.php @@ -228,7 +228,6 @@ $row++; } $content .= $table->toHtml(); - } } diff --git a/main/exercise/question_pool.php b/main/exercise/question_pool.php index 0ced93c4c07..7a63c4c007b 100755 --- a/main/exercise/question_pool.php +++ b/main/exercise/question_pool.php @@ -521,8 +521,6 @@ function confirm_your_choice() { y; if ($years >= 16 && $years <= 17) { - $all['16-17'] += 1; + $all['16-17']++; } if ($years >= 18 && $years <= 25) { - $all['18-25'] += 1; + $all['18-25']++; } if ($years >= 26 && $years <= 30) { - $all['26-30'] += 1; + $all['26-30']++; } /*if ($years >= 31) { $all[get_lang('N/A')] += 1; @@ -658,7 +658,7 @@ if (!isset($all[$language])) { $all[$language] = 0; } - $all[$language] += 1; + $all[$language]++; } break; case 'course_in_session': diff --git a/main/inc/lib/display.lib.php b/main/inc/lib/display.lib.php index 5f777b82b26..8724c3b731e 100755 --- a/main/inc/lib/display.lib.php +++ b/main/inc/lib/display.lib.php @@ -2583,7 +2583,7 @@ public static function getVCardUserLink($userId) * @param string $content * @param string $title * @param string $footer - * @param string $type primary|success|info|warning|danger + * @param string $type primary|success|info|warning|danger * @param string $extra * @param string $id * @param string $backgroundColor diff --git a/main/inc/lib/exercise.lib.php b/main/inc/lib/exercise.lib.php index 1f61e4e0ad0..86b60c02c49 100644 --- a/main/inc/lib/exercise.lib.php +++ b/main/inc/lib/exercise.lib.php @@ -600,7 +600,7 @@ function handleRadioRow(event, question_id, answer_id) { }); }); "; - + $header = Display::tag( 'th', get_lang('Option'), @@ -1583,7 +1583,7 @@ class="window window_left_question window{$questionId}_question"> $s .= '
'; $s .= '
'; $s .= '
'; - + $s .= '
'; $s .= ''; $s .= '
'; @@ -4468,11 +4468,10 @@ public static function get_number_students_answer_count( $answerValue = json_decode($row['answer'], true); $sum += $answerValue[$answer_id]; $count++; - } if ($count > 0) { - return round(($sum / $count),1); + return round(($sum / $count), 1); } return 0; @@ -4498,7 +4497,7 @@ public static function get_number_students_answer_count( } if ($count > 0) { - return round(($sum / $count),1); + return round(($sum / $count), 1); } return 0; diff --git a/main/inc/lib/exercise_show_functions.lib.php b/main/inc/lib/exercise_show_functions.lib.php index 1b178ad961d..ceffe1b36b8 100755 --- a/main/inc/lib/exercise_show_functions.lib.php +++ b/main/inc/lib/exercise_show_functions.lib.php @@ -453,10 +453,10 @@ public static function display_unique_or_multiple_answer( * Display the answers to a ptest question. * * @param Exercise $exercise - * @param int $answerType Answer type - * @param int $studentChoice Student choice - * @param string $answer Textual answer - * @param string $answerComment Comment on answer + * @param int $answerType Answer type + * @param int $studentChoice Student choice + * @param string $answer Textual answer + * @param string $answerComment Comment on answer * @param bool $export */ public static function display_ptest_answer( diff --git a/main/inc/lib/sessionmanager.lib.php b/main/inc/lib/sessionmanager.lib.php index 3bd64a7f6bb..6aa2ee40d08 100755 --- a/main/inc/lib/sessionmanager.lib.php +++ b/main/inc/lib/sessionmanager.lib.php @@ -129,27 +129,27 @@ public static function fetch($id) * * @author Carlos Vargas , from existing code * - * @param string $name - * @param string $startDate (YYYY-MM-DD hh:mm:ss) - * @param string $endDate (YYYY-MM-DD hh:mm:ss) - * @param string $displayStartDate (YYYY-MM-DD hh:mm:ss) - * @param string $displayEndDate (YYYY-MM-DD hh:mm:ss) - * @param string $coachStartDate (YYYY-MM-DD hh:mm:ss) - * @param string $coachEndDate (YYYY-MM-DD hh:mm:ss) - * @param mixed $coachId If int, this is the session coach id, - * if string, the coach ID will be looked for from the user table - * @param int $sessionCategoryId ID of the session category in which this session is registered - * @param int $visibility Visibility after end date (0 = read-only, 1 = invisible, 2 = accessible) - * @param bool $fixSessionNameIfExists - * @param string $duration - * @param string $description Optional. The session description - * @param int $showDescription Optional. Whether show the session description - * @param array $extraFields - * @param int $sessionAdminId Optional. If this sessions was created by a session admin, assign it to him - * @param bool $sendSubscriptionNotification Optional. - * Whether send a mail notification to users being subscribed - * @param int $accessUrlId Optional. - * @param int $status + * @param string $name + * @param string $startDate (YYYY-MM-DD hh:mm:ss) + * @param string $endDate (YYYY-MM-DD hh:mm:ss) + * @param string $displayStartDate (YYYY-MM-DD hh:mm:ss) + * @param string $displayEndDate (YYYY-MM-DD hh:mm:ss) + * @param string $coachStartDate (YYYY-MM-DD hh:mm:ss) + * @param string $coachEndDate (YYYY-MM-DD hh:mm:ss) + * @param mixed $coachId If int, this is the session coach id, + * if string, the coach ID will be looked for from the user table + * @param int $sessionCategoryId ID of the session category in which this session is registered + * @param int $visibility Visibility after end date (0 = read-only, 1 = invisible, 2 = accessible) + * @param bool $fixSessionNameIfExists + * @param string $duration + * @param string $description Optional. The session description + * @param int $showDescription Optional. Whether show the session description + * @param array $extraFields + * @param int $sessionAdminId Optional. If this sessions was created by a session admin, assign it to him + * @param bool $sendSubscriptionNotification Optional. + * Whether send a mail notification to users being subscribed + * @param int $accessUrlId Optional. + * @param int $status * * @return mixed Session ID on success, error message otherwise * @@ -9355,6 +9355,27 @@ public static function convertSessionDateToString($startDate, $endDate, $showTim return $result; } + public static function getStatusList() + { + return [ + self::STATUS_PLANNED => get_lang('Planned'), + self::STATUS_PROGRESS => get_lang('InProgress'), + self::STATUS_FINISHED => get_lang('Finished'), + self::STATUS_CANCELLED => get_lang('Cancelled'), + ]; + } + + public static function getStatusLabel($status) + { + $list = self::getStatusList(); + + if (!isset($list[$status])) { + return get_lang('NoStatus'); + } + + return $list[$status]; + } + /** * @param int $id * @@ -9492,26 +9513,4 @@ private static function compareByCourse($listA, $listB) return -1; } } - - public static function getStatusList() - { - return [ - self::STATUS_PLANNED => get_lang('Planned'), - self::STATUS_PROGRESS => get_lang('InProgress'), - self::STATUS_FINISHED => get_lang('Finished'), - self::STATUS_CANCELLED => get_lang('Cancelled'), - ]; - } - - public static function getStatusLabel($status) - { - $list = self::getStatusList(); - - if (!isset($list[$status])) { - - return get_lang('NoStatus'); - } - - return $list[$status]; - } } diff --git a/main/inc/lib/usergroup.lib.php b/main/inc/lib/usergroup.lib.php index 23d0cde9d94..ff26430907f 100755 --- a/main/inc/lib/usergroup.lib.php +++ b/main/inc/lib/usergroup.lib.php @@ -125,7 +125,7 @@ public function getUserGroupUsers($id, $getCount = false, $start = 0, $limit = 0 $limitCondition = " LIMIT $start, $limit"; } - $sql.= $limitCondition; + $sql .= $limitCondition; $result = Database::query($sql); diff --git a/main/inc/lib/webservices/WebService.class.php b/main/inc/lib/webservices/WebService.class.php index 9c16a6bcc4e..5acab5e71f5 100644 --- a/main/inc/lib/webservices/WebService.class.php +++ b/main/inc/lib/webservices/WebService.class.php @@ -86,13 +86,14 @@ public static function isValidUser($username, $password) if (empty($username) || empty($password)) { return false; } -error_log("89"); + error_log("89"); $user = UserManager::getManager()->findUserByUsername($username); -error_log("90"); + error_log("90"); if (!$user) { return false; } -error_log("95"); + error_log("95"); + return UserManager::isPasswordValid( $user->getPassword(), $password, diff --git a/main/install/configuration.dist.php b/main/install/configuration.dist.php index fdf056e4a51..fc26293b9d1 100755 --- a/main/install/configuration.dist.php +++ b/main/install/configuration.dist.php @@ -952,8 +952,8 @@ // menu // $_configuration['disable_gdpr'] = true; -// GDPR requires users to be informed of the Data Protection Officer name and -// contact point. These can only be defined here for now, but will be moved to +// GDPR requires users to be informed of the Data Protection Officer name and +// contact point. These can only be defined here for now, but will be moved to // web settings in the future. // Name of the person or organization that is responsible for the treatment of // personal info @@ -1349,7 +1349,6 @@ // LP view menu location. Options: "left" or "right" // $_configuration['lp_menu_location'] = 'left'; - // Show personality test // CREATE TABLE c_quiz_category_ptest (id INT AUTO_INCREMENT NOT NULL, c_id INT NOT NULL, exercise_id INT NOT NULL, title VARCHAR(255) NOT NULL, description LONGTEXT, session_id INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB; // ALTER TABLE c_quiz ADD COLUMN pt_type INT DEFAULT 0 NOT NULL; @@ -1385,7 +1384,7 @@ // Catalog search settings visibility //$_configuration['catalog_settings'] = ['sessions' => ['by_title' => true, 'by_date' => true, 'by_tag' => true, 'show_session_info' => true, 'show_session_date' => true]]; -// Enable learning paths with only one SCO item to use the score returned by +// Enable learning paths with only one SCO item to use the score returned by // the SCO as an indicator of progress of the whole learning path // $_configuration['lp_score_as_progress_enable'] = false; diff --git a/main/lp/learnpath.class.php b/main/lp/learnpath.class.php index 9d642571feb..ae6de20da45 100755 --- a/main/lp/learnpath.class.php +++ b/main/lp/learnpath.class.php @@ -2571,6 +2571,7 @@ public function get_progress_bar_text($mode = '', $add = 0) $percentage = $score; $text = '/'.$maxScore; } + return [$percentage, $text]; } } @@ -13671,6 +13672,48 @@ public function getItemsForForm($addParentCondition = false) return $arrLP; } + /** + * Gets whether this SCORM learning path has been marked to use the score + * as progress. Takes into account whether the learnpath matches (SCORM + * content + less than 2 items). + * + * @return bool True if the score should be used as progress, false otherwise + */ + public function getUseScoreAsProgress() + { + // If not a SCORM, we don't care about the setting + if ($this->get_type() != 2) { + return false; + } + // If more than one step in the SCORM, we don't care about the setting + if ($this->get_total_items_count() > 1) { + return false; + } + $extraFieldValue = new ExtraFieldValue('lp'); + $doUseScore = false; + $useScore = $extraFieldValue->get_values_by_handler_and_field_variable($this->get_id(), 'use_score_as_progress'); + if (!empty($useScore) && isset($useScore['value'])) { + $doUseScore = $useScore['value']; + } + + return $doUseScore; + } + + /** + * Get the user identifier (user_id or username + * Depends on scorm_api_username_as_student_id in app/config/configuration.php. + * + * @return string + */ + public function getUserIdentifierForExternalServices() + { + if (api_get_configuration_value('scorm_api_username_as_student_id')) { + return api_get_user_info(api_get_user_id())['username']; + } else { + return api_get_user_id(); + } + } + /** * Get the depth level of LP item. * @@ -13775,43 +13818,4 @@ private function getSavedFinalItem() return ''; } - /** - * Gets whether this SCORM learning path has been marked to use the score - * as progress. Takes into account whether the learnpath matches (SCORM - * content + less than 2 items). - * @return bool True if the score should be used as progress, false otherwise - */ - public function getUseScoreAsProgress() - { - // If not a SCORM, we don't care about the setting - if ($this->get_type() != 2) { - return false; - } - // If more than one step in the SCORM, we don't care about the setting - if ($this->get_total_items_count() > 1) { - return false; - } - $extraFieldValue = new ExtraFieldValue('lp'); - $doUseScore = false; - $useScore = $extraFieldValue->get_values_by_handler_and_field_variable($this->get_id(), 'use_score_as_progress'); - if (!empty($useScore) && isset($useScore['value'])) { - $doUseScore = $useScore['value']; - } - - return $doUseScore; - } - /** - * Get the user identifier (user_id or username - * Depends on scorm_api_username_as_student_id in app/config/configuration.php - * - * @return string - */ - public function getUserIdentifierForExternalServices() - { - if (api_get_configuration_value('scorm_api_username_as_student_id')) { - return api_get_user_info(api_get_user_id())['username']; - } else { - return api_get_user_id(); - } - } } diff --git a/main/lp/lp_ajax_switch_item_toc.php b/main/lp/lp_ajax_switch_item_toc.php index 65b18fc313d..e1159daccf1 100755 --- a/main/lp/lp_ajax_switch_item_toc.php +++ b/main/lp/lp_ajax_switch_item_toc.php @@ -168,8 +168,6 @@ function switch_item_toc($lpId, $userId, $viewId, $currentItem, $nextItem) $return .= "update_progress_bar('$completedItems','$totalItems','$progressMode');"; } - - $myLP->set_error_msg(''); $myLP->prerequisites_match(); // Check the prerequisites are all complete. if ($debug > 1) { diff --git a/main/lp/lp_edit.php b/main/lp/lp_edit.php index 64f9167680c..43cfb9e2600 100755 --- a/main/lp/lp_edit.php +++ b/main/lp/lp_edit.php @@ -273,7 +273,6 @@ function activate_end_date() { $skillList = Skill::addSkillsToForm($form, ITEM_TYPE_LEARNPATH, $lpId); - // Submit button $form->addButtonSave(get_lang('SaveLPSettings')); diff --git a/main/lp/scorm_api.php b/main/lp/scorm_api.php index 372784b82e0..a2f763cc2df 100755 --- a/main/lp/scorm_api.php +++ b/main/lp/scorm_api.php @@ -54,7 +54,7 @@ ?>var scorm_logs=scorm_debug) or (!api_is_course_admin() && !api_is_platform_admin())) ? '0' : '3'; ?>; //debug log level for SCORM. 0 = none, 1=light, 2=a lot, 3=all - displays logs in log frame var lms_logs = 0; //debug log level for LMS actions. 0=none, 1=light, 2=a lot, 3=all -var score_as_progress = getUseScoreAsProgress())? 'false':'true'); ?>; +var score_as_progress = getUseScoreAsProgress()) ? 'false' : 'true'; ?>; // API Object initialization (eases access later on) function APIobject() { diff --git a/main/session/resume_session.php b/main/session/resume_session.php index a5b9dcb8821..8e4b1646076 100644 --- a/main/session/resume_session.php +++ b/main/session/resume_session.php @@ -12,7 +12,6 @@ /** * @author Bart Mollet, Julio Montoya lot of fixes */ - $cidReset = true; require_once __DIR__.'/../inc/global.inc.php'; diff --git a/main/session/session_list.php b/main/session/session_list.php index 8ea89394f15..b6169cc5304 100644 --- a/main/session/session_list.php +++ b/main/session/session_list.php @@ -293,7 +293,8 @@ function show_cols(grid, added_cols) { // Sortable rows grid.jqGrid('sortableRows', options); - + grid.jqGrid('navGrid','#sessions_pager', {edit:false,add:false,del:false}, diff --git a/main/session/session_list_simple.php b/main/session/session_list_simple.php index 0e63d8152d3..793445071c8 100644 --- a/main/session/session_list_simple.php +++ b/main/session/session_list_simple.php @@ -249,7 +249,7 @@ function show_cols(grid, added_cols) { original_cols = grid.jqGrid('getGridParam', 'colModel'); + ?> options = { update: function (e, ui) { var rowNum = jQuery("#sessions").getGridParam('rowNum'); diff --git a/main/tracking/lp_report.php b/main/tracking/lp_report.php index 14b4ca75dbc..bbf1a2a8e87 100644 --- a/main/tracking/lp_report.php +++ b/main/tracking/lp_report.php @@ -25,7 +25,6 @@ }); '; - $lps = learnpath::getLpList($courseId); Session::write('lps', $lps); @@ -274,7 +273,6 @@ function getCount() } return $count; - } /** diff --git a/src/Chamilo/CoreBundle/Entity/Repository/SequenceResourceRepository.php b/src/Chamilo/CoreBundle/Entity/Repository/SequenceResourceRepository.php index 6037e5a6afb..1acf1cfb158 100644 --- a/src/Chamilo/CoreBundle/Entity/Repository/SequenceResourceRepository.php +++ b/src/Chamilo/CoreBundle/Entity/Repository/SequenceResourceRepository.php @@ -9,7 +9,7 @@ use Fhaculty\Graph\Vertex; /** - * Class SequenceResourceRepository + * Class SequenceResourceRepository. */ class SequenceResourceRepository extends EntityRepository { @@ -206,33 +206,6 @@ public function getRequirementsAndDependenciesWithinSequences($resourceId, $type ]; } - /** - * Get sessions from vertices. - * - * @param Vertices $verticesEdges The vertices - * - * @return array - */ - protected function findSessionFromVerticesEdges(Vertices $verticesEdges) - { - $sessionVertices = []; - foreach ($verticesEdges as $supVertex) { - $vertexId = $supVertex->getId(); - $session = $this->getEntityManager()->getReference( - 'ChamiloCoreBundle:Session', - $vertexId - ); - - if (empty($session)) { - continue; - } - - $sessionVertices[$vertexId] = $session; - } - - return $sessionVertices; - } - /** * Check if the ser has completed the requirements for the sequences. * @@ -354,4 +327,31 @@ public function checkSequenceAreCompleted(array $sequences) return false; } + + /** + * Get sessions from vertices. + * + * @param Vertices $verticesEdges The vertices + * + * @return array + */ + protected function findSessionFromVerticesEdges(Vertices $verticesEdges) + { + $sessionVertices = []; + foreach ($verticesEdges as $supVertex) { + $vertexId = $supVertex->getId(); + $session = $this->getEntityManager()->getReference( + 'ChamiloCoreBundle:Session', + $vertexId + ); + + if (empty($session)) { + continue; + } + + $sessionVertices[$vertexId] = $session; + } + + return $sessionVertices; + } } diff --git a/src/Chamilo/CourseBundle/Entity/CQuizAnswer.php b/src/Chamilo/CourseBundle/Entity/CQuizAnswer.php index 3f6537cabf1..9281ea521df 100644 --- a/src/Chamilo/CourseBundle/Entity/CQuizAnswer.php +++ b/src/Chamilo/CourseBundle/Entity/CQuizAnswer.php @@ -390,10 +390,10 @@ public function getDestination() public function setPtestCategory($ptestCategory) { $this->ptestCategory = empty($ptestCategory) ? null : $ptestCategory; - + return $this; } - + /** * Get ptestCategory. * From 2b96ad8c724078dc30f7c56146c42ba5012d3532 Mon Sep 17 00:00:00 2001 From: Nosolored Date: Tue, 18 Feb 2020 17:51:46 +0100 Subject: [PATCH 7/8] Minor - Coding conventions --- main/exercise/admin.php | 4 +- main/exercise/exercise.php | 6 +- main/exercise/exercise_submit.php | 3 +- main/exercise/overview.php | 6 +- main/exercise/ptest_agree_disagree.class.php | 33 +- main/exercise/ptest_agree_reorder.class.php | 35 +- main/exercise/ptest_agree_scale.class.php | 35 +- main/exercise/ptest_category.class.php | 760 +----------------- .../exercise/ptest_category_ranking.class.php | 35 +- main/exercise/ptest_exercise_report.php | 48 +- main/exercise/ptest_stats.php | 2 +- main/exercise/ptest_stats_graph.php | 2 +- main/exercise/ptests_category.php | 42 +- main/exercise/question.class.php | 120 +-- .../question_list_ptest_admin.inc.php | 4 +- main/exercise/question_ptest_admin.inc.php | 36 +- main/inc/ajax/exercise.ajax.php | 14 +- main/inc/lib/exercise_show_functions.lib.php | 10 +- main/install/configuration.dist.php | 9 +- 19 files changed, 300 insertions(+), 904 deletions(-) diff --git a/main/exercise/admin.php b/main/exercise/admin.php index 6b60108839b..8b05b0fe6b0 100755 --- a/main/exercise/admin.php +++ b/main/exercise/admin.php @@ -161,7 +161,9 @@ } if ($objExercise->selectPtType() == EXERCISE_PT_TYPE_PTEST) { - header('Location: '.api_get_path(WEB_CODE_PATH).'exercise/ptest_admin.php?exerciseId='.$exerciseId.'&'.api_get_cidreq()); + header('Location: '.api_get_path(WEB_CODE_PATH).'exercise/ptest_admin.php?'. + 'exerciseId='.$exerciseId.'&'.api_get_cidreq() + ); exit(); } diff --git a/main/exercise/exercise.php b/main/exercise/exercise.php index cae7bacdf88..5b9fd5d2da9 100644 --- a/main/exercise/exercise.php +++ b/main/exercise/exercise.php @@ -526,8 +526,10 @@ $actionsLeft .= ''. Display::return_icon('new_question.png', get_lang('AddQ'), '', ICON_SIZE_MEDIUM).''; if (api_get_configuration_value('show_ptest_quiz')) { - $actionsLeft .= ''. - Display::return_icon('new_personality_test.png', get_lang('AddPT'), '', ICON_SIZE_MEDIUM).''; + $actionsLeft .= ''. + Display::return_icon('new_personality_test.png', get_lang('AddPT'), '', ICON_SIZE_MEDIUM). + ''; } if (api_get_configuration_value('allow_exercise_categories')) { $actionsLeft .= ''; diff --git a/main/exercise/exercise_submit.php b/main/exercise/exercise_submit.php index 9d6e41bd1fa..8a7f4b6cbcd 100755 --- a/main/exercise/exercise_submit.php +++ b/main/exercise/exercise_submit.php @@ -1275,7 +1275,8 @@ function save_now(question_id, url_extra, validate) { // Only for the first time var dataparam = "'.$params.'&type=simple&question_id="+question_id; - dataparam += "&"+my_choice+"&"+hotspot+"&"+remind_list+"&"+my_choiceDc+"&"+my_choice_agree+"&"+my_choice_disagree+"&"+my_choice_agree_scale+"&"+my_choice_agree_reorder; + dataparam += "&"+my_choice+"&"+hotspot+"&"+remind_list+"&"+my_choiceDc+"&"+my_choice_agree+"&" + +my_choice_disagree+"&"+my_choice_agree_scale+"&"+my_choice_agree_reorder; $("#save_for_now_"+question_id).html(\''. Display::returnFontAwesomeIcon('spinner', null, true, 'fa-spin').'\'); diff --git a/main/exercise/overview.php b/main/exercise/overview.php index 0954b7b23dc..3157ddf5699 100755 --- a/main/exercise/overview.php +++ b/main/exercise/overview.php @@ -99,13 +99,15 @@ if ($objExercise->selectPtType() == EXERCISE_PT_TYPE_PTEST) { $editLink .= Display::url( Display::return_icon('test_results.png', get_lang('Results'), [], ICON_SIZE_SMALL), - api_get_path(WEB_CODE_PATH).'exercise/ptest_exercise_report.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id, + api_get_path(WEB_CODE_PATH).'exercise/ptest_exercise_report.php?'. + api_get_cidreq().'&exerciseId='.$objExercise->id, ['title' => get_lang('Results')] ); } else { $editLink .= Display::url( Display::return_icon('test_results.png', get_lang('Results'), [], ICON_SIZE_SMALL), - api_get_path(WEB_CODE_PATH).'exercise/exercise_report.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id, + api_get_path(WEB_CODE_PATH).'exercise/exercise_report.php?'. + api_get_cidreq().'&exerciseId='.$objExercise->id, ['title' => get_lang('Results')] ); } diff --git a/main/exercise/ptest_agree_disagree.class.php b/main/exercise/ptest_agree_disagree.class.php index d3eb1ecfb4e..bb997a3b8ba 100644 --- a/main/exercise/ptest_agree_disagree.class.php +++ b/main/exercise/ptest_agree_disagree.class.php @@ -55,8 +55,8 @@ public function createAnswersForm($form) //this line defines how many questions by default appear when creating a choice question // The previous default value was 2. See task #1759. - $nb_answers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : count($categoriesList); - $nb_answers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0)); + $nbAnswers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : count($categoriesList); + $nbAnswers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0)); $html = '
'.get_lang('Choice').''.get_lang('Answer').'
@@ -76,24 +76,27 @@ public function createAnswersForm($form) $answer = new Answer($this->id); $answer->read(); if ($answer->nbrAnswers > 0 && !$form->isSubmitted()) { - $nb_answers = $answer->nbrAnswers; + $nbAnswers = $answer->nbrAnswers; } } $form->addElement('hidden', 'nb_answers'); //$temp_scenario = []; - if ($nb_answers < 1) { - $nb_answers = 1; + if ($nbAnswers < 1) { + $nbAnswers = 1; echo Display::return_message( get_lang('YouHaveToCreateAtLeastOneAnswer') ); } - for ($i = 1; $i <= $nb_answers; $i++) { + for ($i = 1; $i <= $nbAnswers; $i++) { $form->addHtml(''); if (isset($answer) && is_object($answer)) { $defaults['answer['.$i.']'] = isset($answer->answer[$i]) ? $answer->answer[$i] : ''; - $defaults['ptest_category['.$i.']'] = isset($answer->ptest_category[$i]) ? $answer->ptest_category[$i] : 0; + $defaults['ptest_category['.$i.']'] = 0; + if (isset($answer->ptest_category[$i])) { + $defaults['ptest_category['.$i.']'] = $answer->ptest_category[$i]; + } } $renderer = $form->defaultRenderer(); @@ -167,7 +170,7 @@ public function createAnswersForm($form) $form->setDefaults($defaults); } } - $form->setConstants(['nb_answers' => $nb_answers]); + $form->setConstants(['nb_answers' => $nbAnswers]); } /** @@ -176,9 +179,9 @@ public function createAnswersForm($form) public function processAnswersCreation($form, $exercise) { $objAnswer = new Answer($this->id); - $nb_answers = $form->getSubmitValue('nb_answers'); + $nbAnswers = $form->getSubmitValue('nb_answers'); - for ($i = 1; $i <= $nb_answers; $i++) { + for ($i = 1; $i <= $nbAnswers; $i++) { $answer = trim($form->getSubmitValue('answer['.$i.']')); $goodAnswer = false; $comment = ''; @@ -224,7 +227,7 @@ public function return_header(Exercise $exercise, $counter = null, $score = []) * Saves one answer to the database. * * @param int $id The ID of the answer (has to be calculated for this course) - * @param int $question_id The question ID (to which the answer is attached) + * @param int $questionId The question ID (to which the answer is attached) * @param string $title The text of the answer * @param string $comment The feedback for the answer * @param float $score The score you get when picking this answer @@ -242,7 +245,7 @@ public function addAnswer( $tbl_quiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER); $tbl_quiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION); $course_id = api_get_course_int_id(); - $question_id = intval($question_id); + $questionId = intval($questionId); $score = floatval($score); $correct = intval($correct); $title = Database::escape_string($title); @@ -252,7 +255,7 @@ public function addAnswer( FROM $tbl_quiz_answer WHERE c_id = $course_id AND - question_id = $question_id"; + question_id = $questionId"; $rs_max = Database::query($sql); $row_max = Database::fetch_object($rs_max); $position = $row_max->max_position + 1; @@ -262,7 +265,7 @@ public function addAnswer( $quizAnswer ->setCId($course_id) ->setId($id) - ->setQuestionId($question_id) + ->setQuestionId($questionId) ->setAnswer($title) ->setCorrect($correct) ->setComment($comment) @@ -286,7 +289,7 @@ public function addAnswer( if ($correct) { $sql = "UPDATE $tbl_quiz_question SET ponderation = (ponderation + $score) - WHERE c_id = $course_id AND id = ".$question_id; + WHERE c_id = $course_id AND id = ".$questionId; Database::query($sql); } } diff --git a/main/exercise/ptest_agree_reorder.class.php b/main/exercise/ptest_agree_reorder.class.php index b3da2e24468..ad40aa4758f 100644 --- a/main/exercise/ptest_agree_reorder.class.php +++ b/main/exercise/ptest_agree_reorder.class.php @@ -55,8 +55,8 @@ public function createAnswersForm($form) //this line defines how many questions by default appear when creating a choice question // The previous default value was 2. See task #1759. - $nb_answers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : count($categoriesList); - $nb_answers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0)); + $nbAnswers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : count($categoriesList); + $nbAnswers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0)); $html = '
@@ -76,24 +76,27 @@ public function createAnswersForm($form) $answer = new Answer($this->id); $answer->read(); if ($answer->nbrAnswers > 0 && !$form->isSubmitted()) { - $nb_answers = $answer->nbrAnswers; + $nbAnswers = $answer->nbrAnswers; } } $form->addElement('hidden', 'nb_answers'); //$temp_scenario = []; - if ($nb_answers < 1) { - $nb_answers = 1; + if ($nbAnswers < 1) { + $nbAnswers = 1; echo Display::return_message( get_lang('YouHaveToCreateAtLeastOneAnswer') ); } - for ($i = 1; $i <= $nb_answers; $i++) { + for ($i = 1; $i <= $nbAnswers; $i++) { $form->addHtml(''); if (isset($answer) && is_object($answer)) { $defaults['answer['.$i.']'] = isset($answer->answer[$i]) ? $answer->answer[$i] : ''; - $defaults['ptest_category['.$i.']'] = isset($answer->ptest_category[$i]) ? $answer->ptest_category[$i] : 0; + $defaults['ptest_category['.$i.']'] = 0; + if (isset($answer->ptest_category[$i])) { + $defaults['ptest_category['.$i.']'] = $answer->ptest_category[$i]; + } } $renderer = $form->defaultRenderer(); @@ -167,7 +170,7 @@ public function createAnswersForm($form) $form->setDefaults($defaults); } } - $form->setConstants(['nb_answers' => $nb_answers]); + $form->setConstants(['nb_answers' => $nbAnswers]); } /** @@ -176,9 +179,9 @@ public function createAnswersForm($form) public function processAnswersCreation($form, $exercise) { $objAnswer = new Answer($this->id); - $nb_answers = $form->getSubmitValue('nb_answers'); + $nbAnswers = $form->getSubmitValue('nb_answers'); - for ($i = 1; $i <= $nb_answers; $i++) { + for ($i = 1; $i <= $nbAnswers; $i++) { $answer = trim($form->getSubmitValue('answer['.$i.']')); $goodAnswer = false; $comment = ''; @@ -224,7 +227,7 @@ public function return_header(Exercise $exercise, $counter = null, $score = []) * Saves one answer to the database. * * @param int $id The ID of the answer (has to be calculated for this course) - * @param int $question_id The question ID (to which the answer is attached) + * @param int $questionId The question ID (to which the answer is attached) * @param string $title The text of the answer * @param string $comment The feedback for the answer * @param float $score The score you get when picking this answer @@ -232,7 +235,7 @@ public function return_header(Exercise $exercise, $counter = null, $score = []) */ public function addAnswer( $id, - $question_id, + $questionId, $title, $comment, $score = 0.0, @@ -242,7 +245,7 @@ public function addAnswer( $tbl_quiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER); $tbl_quiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION); $course_id = api_get_course_int_id(); - $question_id = intval($question_id); + $questionId = intval($questionId); $score = floatval($score); $correct = intval($correct); $title = Database::escape_string($title); @@ -252,7 +255,7 @@ public function addAnswer( FROM $tbl_quiz_answer WHERE c_id = $course_id AND - question_id = $question_id"; + question_id = $questionId"; $rs_max = Database::query($sql); $row_max = Database::fetch_object($rs_max); $position = $row_max->max_position + 1; @@ -262,7 +265,7 @@ public function addAnswer( $quizAnswer ->setCId($course_id) ->setId($id) - ->setQuestionId($question_id) + ->setQuestionId($questionId) ->setAnswer($title) ->setCorrect($correct) ->setComment($comment) @@ -286,7 +289,7 @@ public function addAnswer( if ($correct) { $sql = "UPDATE $tbl_quiz_question SET ponderation = (ponderation + $score) - WHERE c_id = $course_id AND id = ".$question_id; + WHERE c_id = $course_id AND id = ".$questionId; Database::query($sql); } } diff --git a/main/exercise/ptest_agree_scale.class.php b/main/exercise/ptest_agree_scale.class.php index 7bffdb38bd5..2f9881a6fa6 100644 --- a/main/exercise/ptest_agree_scale.class.php +++ b/main/exercise/ptest_agree_scale.class.php @@ -55,8 +55,8 @@ public function createAnswersForm($form) //this line defines how many questions by default appear when creating a choice question // The previous default value was 2. See task #1759. - $nb_answers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : count($categoriesList); - $nb_answers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0)); + $nbAnswers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : count($categoriesList); + $nbAnswers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0)); $html = '
@@ -76,24 +76,27 @@ public function createAnswersForm($form) $answer = new Answer($this->id); $answer->read(); if ($answer->nbrAnswers > 0 && !$form->isSubmitted()) { - $nb_answers = $answer->nbrAnswers; + $nbAnswers = $answer->nbrAnswers; } } $form->addElement('hidden', 'nb_answers'); //$temp_scenario = []; - if ($nb_answers < 1) { - $nb_answers = 1; + if ($nbAnswers < 1) { + $nbAnswers = 1; echo Display::return_message( get_lang('YouHaveToCreateAtLeastOneAnswer') ); } - for ($i = 1; $i <= $nb_answers; $i++) { + for ($i = 1; $i <= $nbAnswers; $i++) { $form->addHtml(''); if (isset($answer) && is_object($answer)) { $defaults['answer['.$i.']'] = isset($answer->answer[$i]) ? $answer->answer[$i] : ''; - $defaults['ptest_category['.$i.']'] = isset($answer->ptest_category[$i]) ? $answer->ptest_category[$i] : 0; + $defaults['ptest_category['.$i.']'] = 0; + if (isset($answer->ptest_category[$i])) { + $defaults['ptest_category['.$i.']'] = $answer->ptest_category[$i]; + } } $renderer = $form->defaultRenderer(); @@ -167,7 +170,7 @@ public function createAnswersForm($form) $form->setDefaults($defaults); } } - $form->setConstants(['nb_answers' => $nb_answers]); + $form->setConstants(['nb_answers' => $nbAnswers]); } public function setDirectOptions($i, FormValidator $form, $renderer, $select_lp_id, $select_question) @@ -229,9 +232,9 @@ public function setDirectOptions($i, FormValidator $form, $renderer, $select_lp_ public function processAnswersCreation($form, $exercise) { $objAnswer = new Answer($this->id); - $nb_answers = $form->getSubmitValue('nb_answers'); + $nbAnswers = $form->getSubmitValue('nb_answers'); - for ($i = 1; $i <= $nb_answers; $i++) { + for ($i = 1; $i <= $nbAnswers; $i++) { $answer = trim($form->getSubmitValue('answer['.$i.']')); $goodAnswer = false; $comment = ''; @@ -277,7 +280,7 @@ public function return_header(Exercise $exercise, $counter = null, $score = []) * Saves one answer to the database. * * @param int $id The ID of the answer (has to be calculated for this course) - * @param int $question_id The question ID (to which the answer is attached) + * @param int $questionId The question ID (to which the answer is attached) * @param string $title The text of the answer * @param string $comment The feedback for the answer * @param float $score The score you get when picking this answer @@ -285,7 +288,7 @@ public function return_header(Exercise $exercise, $counter = null, $score = []) */ public function addAnswer( $id, - $question_id, + $questionId, $title, $comment, $score = 0.0, @@ -295,7 +298,7 @@ public function addAnswer( $tbl_quiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER); $tbl_quiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION); $course_id = api_get_course_int_id(); - $question_id = intval($question_id); + $questionId = intval($questionId); $score = floatval($score); $correct = intval($correct); $title = Database::escape_string($title); @@ -305,7 +308,7 @@ public function addAnswer( FROM $tbl_quiz_answer WHERE c_id = $course_id AND - question_id = $question_id"; + question_id = $questionId"; $rs_max = Database::query($sql); $row_max = Database::fetch_object($rs_max); $position = $row_max->max_position + 1; @@ -315,7 +318,7 @@ public function addAnswer( $quizAnswer ->setCId($course_id) ->setId($id) - ->setQuestionId($question_id) + ->setQuestionId($questionId) ->setAnswer($title) ->setCorrect($correct) ->setComment($comment) @@ -339,7 +342,7 @@ public function addAnswer( if ($correct) { $sql = "UPDATE $tbl_quiz_question SET ponderation = (ponderation + $score) - WHERE c_id = $course_id AND id = ".$question_id; + WHERE c_id = $course_id AND id = ".$questionId; Database::query($sql); } } diff --git a/main/exercise/ptest_category.class.php b/main/exercise/ptest_category.class.php index 1a545696685..d816a5b0ce8 100644 --- a/main/exercise/ptest_category.class.php +++ b/main/exercise/ptest_category.class.php @@ -132,12 +132,12 @@ public function removeCategory($id) { $table = Database::get_course_table(TABLE_QUIZ_CATEGORY_PTEST); $id = (int) $id; - $course_id = api_get_course_int_id(); + $courseId = api_get_course_int_id(); $category = $this->getCategory($id); if ($category) { $sql = "DELETE FROM $table - WHERE id= $id AND c_id=".$course_id; + WHERE id= $id AND c_id=".$courseId; Database::query($sql); return true; @@ -254,638 +254,6 @@ public static function getCategoryListInfo($exerciseId, $field = '', $courseId = return $categories; } - /** - * --------Return the PTestCategory id for question with question_id = $questionId - * In this version, a question has only 1 PTestCategory. - * Return the PTestCategory id, 0 if none. - * - * @param int $questionId - * @param int $courseId - * - * @return int - */ - public static function getCategoryForQuestion($questionId, $courseId = 0) - { - $courseId = (int) $courseId; - $questionId = (int) $questionId; - - if (empty($courseId)) { - $courseId = api_get_course_int_id(); - } - - if (empty($courseId) || empty($questionId)) { - return 0; - } - - $table = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY); - $sql = "SELECT category_id - FROM $table - WHERE question_id = $questionId AND c_id = $courseId"; - $res = Database::query($sql); - $result = 0; - if (Database::num_rows($res) > 0) { - $data = Database::fetch_array($res); - $result = (int) $data['category_id']; - } - - return $result; - } - - /** - * ---------- Return the category name for question with question_id = $questionId - * In this version, a question has only 1 category. - * - * @param $questionId - * @param int $courseId - * - * @return string - */ - public static function getCategoryNameForQuestion($questionId, $courseId = 0) - { - if (empty($courseId)) { - $courseId = api_get_course_int_id(); - } - $courseId = (int) $courseId; - $categoryId = self::getCategoryForQuestion($questionId, $courseId); - $table = Database::get_course_table(TABLE_QUIZ_CATEGORY_PTEST); - $sql = "SELECT title - FROM $table - WHERE id = $categoryId AND c_id = $courseId"; - $res = Database::query($sql); - $data = Database::fetch_array($res); - $result = ''; - if (Database::num_rows($res) > 0) { - $result = $data['title']; - } - - return $result; - } - - /** - * Return the list of differents categories ID for a test in the current course - * input : test_id - * return : array of category id (integer) - * hubert.borderiou 07-04-2011. - * - * @param int $exerciseId - * @param int $courseId - * - * @return array - */ - public static function getListOfCategoriesIDForTest($exerciseId, $courseId = 0) - { - // parcourir les questions d'un test, recup les categories uniques dans un tableau - $exercise = new Exercise($courseId); - $exercise->read($exerciseId, false); - $categoriesInExercise = $exercise->getQuestionWithCategories(); - // the array given by selectQuestionList start at indice 1 and not at indice 0 !!! ??? - $categories = []; - if (!empty($categoriesInExercise)) { - foreach ($categoriesInExercise as $category) { - $categories[$category['id']] = $category; - } - } - - return $categories; - } - - /** - * @param Exercise $exercise - * - * @return array - */ - public static function getListOfCategoriesIDForTestObject(Exercise $exercise) - { - // parcourir les questions d'un test, recup les categories uniques dans un tableau - $categories_in_exercise = []; - $question_list = $exercise->getQuestionOrderedListByName(); - - // the array given by selectQuestionList start at indice 1 and not at indice 0 !!! ??? - foreach ($question_list as $questionInfo) { - $question_id = $questionInfo['question_id']; - $category_list = self::getCategoryForQuestion($question_id); - if (is_numeric($category_list)) { - $category_list = [$category_list]; - } - - if (!empty($category_list)) { - $categories_in_exercise = array_merge($categories_in_exercise, $category_list); - } - } - if (!empty($categories_in_exercise)) { - $categories_in_exercise = array_unique(array_filter($categories_in_exercise)); - } - - return $categories_in_exercise; - } - - /** - * Return the list of different categories NAME for a test. - * - * @param int $exerciseId - * @param bool - * - * @return array - * - * @author function rewrote by jmontoya - */ - public static function getListOfCategoriesNameForTest($exerciseId, $grouped_by_category = true) - { - $result = []; - $categories = self::getListOfCategoriesIDForTest($exerciseId); - - foreach ($categories as $catInfo) { - $categoryId = $catInfo['id']; - if (!empty($categoryId)) { - $result[$categoryId] = [ - 'title' => $catInfo['title'], - //'parent_id' => $catInfo['parent_id'], - 'parent_id' => '', - 'c_id' => $catInfo['c_id'], - ]; - } - } - - return $result; - } - - /** - * @param Exercise $exercise - * - * @return array - */ - public static function getListOfCategoriesForTest(Exercise $exercise) - { - $result = []; - $categories = self::getListOfCategoriesIDForTestObject($exercise); - foreach ($categories as $cat_id) { - $cat = new PTestCategory(); - $cat = (array) $cat->getCategory($cat_id); - $cat['iid'] = $cat['id']; - $cat['title'] = $cat['name']; - $result[$cat['id']] = $cat; - } - - return $result; - } - - /** - * return the number of question of a category id in a test. - * - * @param int $exerciseId - * @param int $categoryId - * - * @return int - * - * @author hubert.borderiou 07-04-2011 - */ - public static function getNumberOfQuestionsInCategoryForTest($exerciseId, $categoryId) - { - $nbCatResult = 0; - $quiz = new Exercise(); - $quiz->read($exerciseId); - $questionList = $quiz->selectQuestionList(); - // the array given by selectQuestionList start at indice 1 and not at indice 0 !!! ? ? ? - for ($i = 1; $i <= count($questionList); $i++) { - if (self::getCategoryForQuestion($questionList[$i]) == $categoryId) { - $nbCatResult++; - } - } - - return $nbCatResult; - } - - /** - * return the number of question for a test using random by category - * input : test_id, number of random question (min 1). - * - * @param int $exerciseId - * @param int $random - * - * @return int - * hubert.borderiou 07-04-2011 - * question without categories are not counted - */ - public static function getNumberOfQuestionRandomByCategory($exerciseId, $random) - { - $count = 0; - $categories = self::getListOfCategoriesIDForTest($exerciseId); - foreach ($categories as $category) { - if (empty($category['id'])) { - continue; - } - - $nbQuestionInThisCat = self::getNumberOfQuestionsInCategoryForTest( - $exerciseId, - $category['id'] - ); - - if ($nbQuestionInThisCat > $random) { - $count += $random; - } else { - $count += $nbQuestionInThisCat; - } - } - - return $count; - } - - /** - * Return an array (id=>name) - * array[0] = get_lang('NoCategory');. - * - * @param int $courseId - * - * @return array - */ - public static function getCategoriesIdAndName($courseId = 0) - { - if (empty($courseId)) { - $courseId = api_get_course_int_id(); - } - $categories = self::getCategoryListInfo('', $courseId); - $result = ['0' => get_lang('NoCategorySelected')]; - for ($i = 0; $i < count($categories); $i++) { - $result[$categories[$i]->id] = $categories[$i]->name; - } - - return $result; - } - - /** - * Returns an array of question ids for each category - * $categories[1][30] = 10, array with category id = 1 and question_id = 10 - * A question has "n" categories. - * - * @param int $exerciseId - * @param array $check_in_question_list - * @param array $categoriesAddedInExercise - * - * @return array - */ - public static function getQuestionsByCat( - $exerciseId, - $check_in_question_list = [], - $categoriesAddedInExercise = [] - ) { - $tableQuestion = Database::get_course_table(TABLE_QUIZ_QUESTION); - $TBL_EXERCICE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); - $TBL_QUESTION_REL_CATEGORY = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY); - $categoryTable = Database::get_course_table(TABLE_QUIZ_CATEGORY_PTEST); - $exerciseId = (int) $exerciseId; - $courseId = api_get_course_int_id(); - - $sql = "SELECT DISTINCT qrc.question_id, qrc.category_id - FROM $TBL_QUESTION_REL_CATEGORY qrc - INNER JOIN $TBL_EXERCICE_QUESTION eq - ON (eq.question_id = qrc.question_id AND qrc.c_id = eq.c_id) - INNER JOIN $categoryTable c - ON (c.id = qrc.category_id AND c.c_id = eq.c_id) - INNER JOIN $tableQuestion q - ON (q.id = qrc.question_id AND q.c_id = eq.c_id) - WHERE - exercice_id = $exerciseId AND - qrc.c_id = $courseId - "; - - $res = Database::query($sql); - $categories = []; - while ($data = Database::fetch_array($res)) { - if (!empty($check_in_question_list)) { - if (!in_array($data['question_id'], $check_in_question_list)) { - continue; - } - } - - if (!isset($categories[$data['category_id']]) || - !is_array($categories[$data['category_id']]) - ) { - $categories[$data['category_id']] = []; - } - - $categories[$data['category_id']][] = $data['question_id']; - } - - if (!empty($categoriesAddedInExercise)) { - $newCategoryList = []; - foreach ($categoriesAddedInExercise as $category) { - $categoryId = $category['category_id']; - if (isset($categories[$categoryId])) { - $newCategoryList[$categoryId] = $categories[$categoryId]; - } - } - - $checkQuestionsWithNoCategory = false; - foreach ($categoriesAddedInExercise as $category) { - if (empty($category['category_id'])) { - // Check - $checkQuestionsWithNoCategory = true; - break; - } - } - - // Select questions that don't have any category related - if ($checkQuestionsWithNoCategory) { - $originalQuestionList = $check_in_question_list; - foreach ($originalQuestionList as $questionId) { - $categoriesFlatten = array_flatten($categories); - if (!in_array($questionId, $categoriesFlatten)) { - $newCategoryList[0][] = $questionId; - } - } - } - $categories = $newCategoryList; - } - - return $categories; - } - - /** - * Returns an array of $numberElements from $array. - * - * @param array - * @param int - * - * @return array - */ - public static function getNElementsFromArray($array, $numberElements) - { - $list = $array; - shuffle($list); - if ($numberElements < count($list)) { - $list = array_slice($list, 0, $numberElements); - } - - return $list; - } - - /** - * @param int $questionId - * @param int $displayCategoryName - */ - public static function displayCategoryAndTitle($questionId, $displayCategoryName = 1) - { - echo self::returnCategoryAndTitle($questionId, $displayCategoryName); - } - - /** - * @param int $questionId - * @param int $in_display_category_name - * - * @return string|null - */ - public static function returnCategoryAndTitle($questionId, $in_display_category_name = 1) - { - $is_student = !(api_is_allowed_to_edit(null, true) || api_is_session_admin()); - $objExercise = Session::read('objExercise'); - if (!empty($objExercise)) { - $in_display_category_name = $objExercise->display_category_name; - } - $content = null; - if (self::getCategoryNameForQuestion($questionId) != '' && - ($in_display_category_name == 1 || !$is_student) - ) { - $content .= '"; - } - - return $content; - } - - /** - * sortTabByBracketLabel ($tabCategoryQuestions) - * key of $tabCategoryQuestions are the category id (0 for not in a category) - * value is the array of question id of this category - * Sort question by Category. - */ - public static function sortTabByBracketLabel($in_tab) - { - $tabResult = []; - $tabCatName = []; // tab of category name - foreach ($in_tab as $cat_id => $tabquestion) { - $category = new PTestCategory(); - $category = $category->getCategory($cat_id); - $tabCatName[$cat_id] = $category->name; - } - reset($in_tab); - // sort table by value, keeping keys as they are - asort($tabCatName); - // keys of $tabCatName are keys order for $in_tab - foreach ($tabCatName as $key => $val) { - $tabResult[$key] = $in_tab[$key]; - } - - return $tabResult; - } - - /** - * Return the number max of question in a category - * count the number of questions in all categories, and return the max. - * - * @param int $exerciseId - * - * @author - hubert borderiou - * - * @return int - */ - public static function getNumberMaxQuestionByCat($exerciseId) - { - $res_num_max = 0; - // foreach question - $categories = self::getListOfCategoriesIDForTest($exerciseId); - foreach ($categories as $category) { - if (empty($category['id'])) { - continue; - } - - $nbQuestionInThisCat = self::getNumberOfQuestionsInCategoryForTest( - $exerciseId, - $category['id'] - ); - - if ($nbQuestionInThisCat > $res_num_max) { - $res_num_max = $nbQuestionInThisCat; - } - } - - return $res_num_max; - } - - /** - * Returns a category summary report. - * - * @param int $exerciseId - * @param array $category_list - * pre filled array with the category_id, score, and weight - * example: array(1 => array('score' => '10', 'total' => 20)); - * - * @return string - */ - public static function get_stats_table_by_attempt( - $exerciseId, - $category_list = [] - ) { - if (empty($category_list)) { - return null; - } - $category_name_list = self::getListOfCategoriesNameForTest($exerciseId); - - $table = new HTML_Table(['class' => 'table table-bordered', 'id' => 'category_results']); - $table->setHeaderContents(0, 0, get_lang('Categories')); - $table->setHeaderContents(0, 1, get_lang('AbsoluteScore')); - $table->setHeaderContents(0, 2, get_lang('RelativeScore')); - $row = 1; - - $none_category = []; - if (isset($category_list['none'])) { - $none_category = $category_list['none']; - unset($category_list['none']); - } - - $total = []; - if (isset($category_list['total'])) { - $total = $category_list['total']; - unset($category_list['total']); - } - if (count($category_list) > 1) { - foreach ($category_list as $category_id => $category_item) { - $table->setCellContents($row, 0, $category_name_list[$category_id]); - $table->setCellContents( - $row, - 1, - ExerciseLib::show_score( - $category_item['score'], - $category_item['total'], - false - ) - ); - $table->setCellContents( - $row, - 2, - ExerciseLib::show_score( - $category_item['score'], - $category_item['total'], - true, - false, - true - ) - ); - $row++; - } - - if (!empty($none_category)) { - $table->setCellContents($row, 0, get_lang('None')); - $table->setCellContents( - $row, - 1, - ExerciseLib::show_score( - $none_category['score'], - $none_category['total'], - false - ) - ); - $table->setCellContents( - $row, - 2, - ExerciseLib::show_score( - $none_category['score'], - $none_category['total'], - true, - false, - true - ) - ); - $row++; - } - if (!empty($total)) { - $table->setCellContents($row, 0, get_lang('Total')); - $table->setCellContents( - $row, - 1, - ExerciseLib::show_score( - $total['score'], - $total['total'], - false - ) - ); - $table->setCellContents( - $row, - 2, - ExerciseLib::show_score( - $total['score'], - $total['total'], - true, - false, - true - ) - ); - } - - return $table->toHtml(); - } - - return ''; - } - - /** - * @param Exercise $exercise - * @param int $courseId - * @param string $order - * @param bool $shuffle - * @param bool $excludeCategoryWithNoQuestions - * - * @return array - */ - public function getCategoryExerciseTree( - $exercise, - $courseId, - $order = null, - $shuffle = false, - $excludeCategoryWithNoQuestions = true - ) { - if (empty($exercise)) { - return []; - } - - $courseId = (int) $courseId; - $table = Database::get_course_table(TABLE_QUIZ_REL_CATEGORY); - $categoryTable = Database::get_course_table(TABLE_QUIZ_CATEGORY_PTEST); - $exercise->id = (int) $exercise->id; - - $sql = "SELECT * FROM $table qc - LEFT JOIN $categoryTable c - ON (qc.c_id = c.c_id AND c.id = qc.category_id) - WHERE qc.c_id = $courseId AND exercise_id = {$exercise->id} "; - - if (!empty($order)) { - $order = Database::escape_string($order); - $sql .= "ORDER BY $order"; - } - - $categories = []; - $result = Database::query($sql); - if (Database::num_rows($result)) { - while ($row = Database::fetch_array($result, 'ASSOC')) { - if ($excludeCategoryWithNoQuestions) { - if ($row['count_questions'] == 0) { - continue; - } - } - if (empty($row['title']) && empty($row['category_id'])) { - $row['title'] = get_lang('NoCategory'); - } - $categories[$row['category_id']] = $row; - } - } - - if ($shuffle) { - shuffle_assoc($categories); - } - - return $categories; - } - /** * @param FormValidator $form * @param string $action @@ -923,7 +291,7 @@ public function getForm(&$form, $action = 'new') 'Height' => '200', ] ); - $category_parent_list = []; + $categoryParentList = []; $options = [ '1' => get_lang('Visible'), @@ -939,12 +307,19 @@ public function getForm(&$form, $action = 'new') if (!empty($this->parent_id)) { $parent_cat = new PTestCategory(); $parent_cat = $parent_cat->getCategory($this->parent_id); - $category_parent_list = [$parent_cat->id => $parent_cat->name]; - $script .= ''; + $categoryParentList = [$parent_cat->id => $parent_cat->name]; + $script .= ''; } $form->addElement('html', $script); - $form->addElement('select', 'parent_id', get_lang('Parent'), $category_parent_list, ['id' => 'parent_id']); + $form->addElement('select', 'parent_id', get_lang('Parent'), $categoryParentList, ['id' => 'parent_id']); $form->addElement('style_submit_button', 'SubmitNote', $submit, 'class="add"'); // setting the defaults @@ -960,68 +335,6 @@ public function getForm(&$form, $action = 'new') $form->addRule('category_name', get_lang('ThisFieldIsRequired'), 'required'); } - /** - * Returns the category form. - * - * @param Exercise $exercise - * - * @return string - */ - public function returnCategoryForm(Exercise $exercise) - { - $categories = $this->getListOfCategoriesForTest($exercise); - $saved_categories = $exercise->getCategoriesInExercise(); - $return = null; - - if (!empty($categories)) { - $nbQuestionsTotal = $exercise->getNumberQuestionExerciseCategory(); - $exercise->setCategoriesGrouping(true); - $real_question_count = count($exercise->getQuestionList()); - - $warning = null; - if ($nbQuestionsTotal != $real_question_count) { - $warning = Display::return_message( - get_lang('CheckThatYouHaveEnoughQuestionsInYourCategories'), - 'warning' - ); - } - - $return .= $warning; - $return .= '
'; - $return .= ''; - $return .= ''; - $return .= ''; - - $emptyCategory = [ - 'id' => '0', - 'name' => get_lang('NoCategory'), - 'description' => '', - 'iid' => '0', - 'title' => get_lang('NoCategory'), - ]; - - $categories[] = $emptyCategory; - - foreach ($categories as $category) { - $cat_id = $category['iid']; - $return .= ''; - $return .= ''; - $return .= ''; - $return .= ''; - } - - $return .= '
'.get_lang('Categories').''.get_lang('Number').'
'; - $return .= Display::div($category['name']); - $return .= ''; - $value = isset($saved_categories) && isset($saved_categories[$cat_id]) ? $saved_categories[$cat_id]['count_questions'] : -1; - $return .= ''; - $return .= '
'; - $return .= get_lang('ZeroMeansNoQuestionWillBeSelectedMinusOneMeansThatAllQuestionsWillBeSelected'); - } - - return $return; - } - /** * Return true if a category already exists with the same name. * @@ -1042,33 +355,6 @@ public static function categoryTitleExists($name, $courseId = 0) return false; } - /** - * Return the id of the test category with title = $in_title. - * - * @param string $title - * @param int $courseId - * - * @return int is id of test category - */ - public static function get_category_id_for_title($title, $courseId = 0) - { - $out_res = 0; - if (empty($courseId)) { - $courseId = api_get_course_int_id(); - } - $courseId = (int) $courseId; - $tbl_cat = Database::get_course_table(TABLE_QUIZ_CATEGORY_PTEST); - $sql = "SELECT id FROM $tbl_cat - WHERE c_id = $courseId AND title = '".Database::escape_string($title)."'"; - $res = Database::query($sql); - if (Database::num_rows($res) > 0) { - $data = Database::fetch_array($res); - $out_res = $data['id']; - } - - return $out_res; - } - /** * @param int $exerciseId * @param int $courseId @@ -1127,14 +413,20 @@ public function displayCategories($exerciseId, $courseId, $sessionId = 0) $content .= ''.$category['position'].''; $content .= ''; $content .= ''; - $content .= ''.get_lang('PtestCategoryColor').''; + $content .= ''; + $content .= get_lang('PtestCategoryColor'); + $content .= ''; $content .= ''; $content .= Display::tag( 'span', null, [ 'class' => 'form-control', - 'style' => 'background:'.$category['color'].'; width:100px; vertical-align:middle; display:inline-block; margin-right:20px;', + 'style' => 'background:'.$category['color'].'; + width:100px; + vertical-align:middle; + display:inline-block; + margin-right:20px;', ] ); $content .= $category['color']; @@ -1148,10 +440,14 @@ public function displayCategories($exerciseId, $courseId, $sessionId = 0) $content .= '
'; $links = ''; - $links .= ''. + $links .= ''. Display::return_icon('edit.png', get_lang('Edit'), [], ICON_SIZE_SMALL).''; - $links .= ' '; + $links .= ' '; $links .= Display::return_icon('delete.png', get_lang('Delete'), [], ICON_SIZE_SMALL).''; $html .= Display::panel($content, $category['title'].$links); diff --git a/main/exercise/ptest_category_ranking.class.php b/main/exercise/ptest_category_ranking.class.php index 57569c7d126..c741fa5e021 100644 --- a/main/exercise/ptest_category_ranking.class.php +++ b/main/exercise/ptest_category_ranking.class.php @@ -55,8 +55,8 @@ public function createAnswersForm($form) //this line defines how many questions by default appear when creating a choice question // The previous default value was 2. See task #1759. - $nb_answers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : count($categoriesList); - $nb_answers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0)); + $nbAnswers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : count($categoriesList); + $nbAnswers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0)); $html = ' @@ -76,24 +76,27 @@ public function createAnswersForm($form) $answer = new Answer($this->id); $answer->read(); if ($answer->nbrAnswers > 0 && !$form->isSubmitted()) { - $nb_answers = $answer->nbrAnswers; + $nbAnswers = $answer->nbrAnswers; } } $form->addElement('hidden', 'nb_answers'); //$temp_scenario = []; - if ($nb_answers < 1) { - $nb_answers = 1; + if ($nbAnswers < 1) { + $nbAnswers = 1; echo Display::return_message( get_lang('YouHaveToCreateAtLeastOneAnswer') ); } - for ($i = 1; $i <= $nb_answers; $i++) { + for ($i = 1; $i <= $nbAnswers; $i++) { $form->addHtml(''); if (isset($answer) && is_object($answer)) { $defaults['answer['.$i.']'] = isset($answer->answer[$i]) ? $answer->answer[$i] : ''; - $defaults['ptest_category['.$i.']'] = isset($answer->ptest_category[$i]) ? $answer->ptest_category[$i] : 0; + $defaults['ptest_category['.$i.']'] = 0; + if (isset($answer->ptest_category[$i])) { + $defaults['ptest_category['.$i.']'] = $answer->ptest_category[$i]; + } } $renderer = $form->defaultRenderer(); @@ -169,7 +172,7 @@ public function createAnswersForm($form) $form->setDefaults($defaults); } } - $form->setConstants(['nb_answers' => $nb_answers]); + $form->setConstants(['nb_answers' => $nbAnswers]); } /** @@ -178,9 +181,9 @@ public function createAnswersForm($form) public function processAnswersCreation($form, $exercise) { $objAnswer = new Answer($this->id); - $nb_answers = $form->getSubmitValue('nb_answers'); + $nbAnswers = $form->getSubmitValue('nb_answers'); - for ($i = 1; $i <= $nb_answers; $i++) { + for ($i = 1; $i <= $nbAnswers; $i++) { $answer = trim($form->getSubmitValue('answer['.$i.']')); $goodAnswer = false; $comment = ''; @@ -226,7 +229,7 @@ public function return_header(Exercise $exercise, $counter = null, $score = []) * Saves one answer to the database. * * @param int $id The ID of the answer (has to be calculated for this course) - * @param int $question_id The question ID (to which the answer is attached) + * @param int $questionId The question ID (to which the answer is attached) * @param string $title The text of the answer * @param string $comment The feedback for the answer * @param float $score The score you get when picking this answer @@ -234,7 +237,7 @@ public function return_header(Exercise $exercise, $counter = null, $score = []) */ public function addAnswer( $id, - $question_id, + $questionId, $title, $comment, $score = 0.0, @@ -244,7 +247,7 @@ public function addAnswer( $tbl_quiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER); $tbl_quiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION); $course_id = api_get_course_int_id(); - $question_id = intval($question_id); + $questionId = intval($questionId); $score = floatval($score); $correct = intval($correct); $title = Database::escape_string($title); @@ -254,7 +257,7 @@ public function addAnswer( FROM $tbl_quiz_answer WHERE c_id = $course_id AND - question_id = $question_id"; + question_id = $questionId"; $rs_max = Database::query($sql); $row_max = Database::fetch_object($rs_max); $position = $row_max->max_position + 1; @@ -264,7 +267,7 @@ public function addAnswer( $quizAnswer ->setCId($course_id) ->setId($id) - ->setQuestionId($question_id) + ->setQuestionId($questionId) ->setAnswer($title) ->setCorrect($correct) ->setComment($comment) @@ -288,7 +291,7 @@ public function addAnswer( if ($correct) { $sql = "UPDATE $tbl_quiz_question SET ponderation = (ponderation + $score) - WHERE c_id = $course_id AND id = ".$question_id; + WHERE c_id = $course_id AND id = ".$questionId; Database::query($sql); } } diff --git a/main/exercise/ptest_exercise_report.php b/main/exercise/ptest_exercise_report.php index 51347d1cd10..814b954f5f0 100644 --- a/main/exercise/ptest_exercise_report.php +++ b/main/exercise/ptest_exercise_report.php @@ -101,7 +101,8 @@ $actions .= ''. Display::return_icon('statistics.png', get_lang('ReportByQuestion'), '', ICON_SIZE_MEDIUM).''; $actions .= ''. - Display::return_icon('survey_reporting_question.png', get_lang('ExerciseGraph'), '', ICON_SIZE_MEDIUM).''; + Display::return_icon('survey_reporting_question.png', get_lang('ExerciseGraph'), '', ICON_SIZE_MEDIUM); + $actions .= ''; // clean result before a selected date icon $actions .= Display::url( Display::return_icon( @@ -230,7 +231,8 @@ $actions = Display::div($actions, ['class' => 'actions']); echo $actions; -$url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_ptest_exercise_results&exerciseId='.$exercise_id.'&filter_by_user='.$filter_user.'&'.api_get_cidreq(); +$url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?'; +$url .= 'a=get_ptest_exercise_results&exerciseId='.$exercise_id.'&filter_by_user='.$filter_user.'&'.api_get_cidreq(); $action_links = ''; // Generating group list $group_list = GroupManager::get_group_list(); @@ -270,7 +272,14 @@ // Column config $column_model = [ ['name' => 'firstname', 'index' => 'firstname', 'width' => '50', 'align' => 'left', 'search' => 'true'], - ['name' => 'lastname', 'index' => 'lastname', 'width' => '50', 'align' => 'left', 'formatter' => 'action_formatter', 'search' => 'true'], + [ + 'name' => 'lastname', + 'index' => 'lastname', + 'width' => '50', + 'align' => 'left', + 'formatter' => 'action_formatter', + 'search' => 'true' + ], [ 'name' => 'login', 'index' => 'username', @@ -299,11 +308,24 @@ ['name' => 'exe_date', 'index' => 'exe_date', 'width' => '60', 'align' => 'left', 'search' => 'true'], ['name' => 'ip', 'index' => 'user_ip', 'width' => '40', 'align' => 'center', 'search' => 'true'], ['name' => 'lp', 'index' => 'orig_lp_id', 'width' => '60', 'align' => 'left', 'search' => 'false'], - ['name' => 'actions', 'index' => 'actions', 'width' => '60', 'align' => 'left', 'search' => 'false', 'sortable' => 'false'], + [ + 'name' => 'actions', + 'index' => 'actions', + 'width' => '60', + 'align' => 'left', + 'search' => 'false', + 'sortable' => 'false' + ], ]; if ($officialCodeInList === 'true') { - $officialCodeRow = ['name' => 'official_code', 'index' => 'official_code', 'width' => '50', 'align' => 'left', 'search' => 'true']; + $officialCodeRow = [ + 'name' => 'official_code', + 'index' => 'official_code', + 'width' => '50', + 'align' => 'left', + 'search' => 'true' + ]; $column_model = array_merge([$officialCodeRow], $column_model); } @@ -438,15 +460,14 @@ function exportExcel() if (!$(this).data('user') || !$(this).data('exercise') || !$(this).data('id')) { return; } - var url = 'exercise/recalculate.php?'; + var url = 'exercise/recalculate.php?'+ + ''; var recalculateXhr = $.post(url, $(this).data()); $.when(recalculateXhr).done(function (response) { $('#results').trigger('reloadGrid'); }); }); - + }); // datepicker functions var datapickerInputModified = false; @@ -487,8 +508,13 @@ function submit_datepicker() { // Format the date for confirm box var dateFormat = $( "#datepicker_start" ).datepicker( "option", "dateFormat" ); var selectedDate = $.datepicker.formatDate(dateFormat, dateTypeVar); - if (confirm("" + selectedDate)) { - self.location.href = "ptest_exercise_report.php?&exerciseId=&delete_before_date="+dateForBDD+"&sec_token="; + if (confirm(" + " + + selectedDate) + ) { + self.location.href = "ptest_exercise_report.php?" + + "&exerciseId=" + + "&delete_before_date="+dateForBDD+"&sec_token="; } } } diff --git a/main/exercise/ptest_stats.php b/main/exercise/ptest_stats.php index 645c34fb59e..872896a5318 100644 --- a/main/exercise/ptest_stats.php +++ b/main/exercise/ptest_stats.php @@ -1,5 +1,5 @@ getCategory($category_id); + $objcat = $objcat->getCategory($categoryId); + + $params = [ + 'exerciseId' => $exerciseId, + 'action' => $action, + 'category_id' => $categoryId, + ]; $form = new FormValidator( 'note', 'post', - api_get_self().'?exerciseId='.$exerciseId.'&action='.$action.'&category_id='.$category_id.'&'.api_get_cidreq() + api_get_self().'?'.http_build_query($params).'&'.api_get_cidreq() ); // Setting the form elements @@ -115,12 +121,12 @@ function edit_category_form($action) $form->addElement('color', 'category_color', get_lang('PtestCategoryColor'), ['size' => '95']); $form->addElement('number', 'category_position', get_lang('PtestCategoryPosition'), ['size' => '95']); $form->addHtmlEditor( - 'category_description', - get_lang('PtestCategoryDescription'), - false, - false, - ['ToolbarSet' => 'TestQuestionDescription', 'Height' => '200'] - ); + 'category_description', + get_lang('PtestCategoryDescription'), + false, + false, + ['ToolbarSet' => 'TestQuestionDescription', 'Height' => '200'] + ); $form->addButtonCreate(get_lang('ModifyPTestFeature'), 'SubmitNote'); // setting the rules @@ -196,7 +202,14 @@ function add_category_form($action) $exerciseId = (int) $_GET['exerciseId']; $action = Security::remove_XSS($action); // initiate the object - $form = new FormValidator('note', 'post', api_get_self().'?exerciseId='.$exerciseId.'&action='.$action.'&'.api_get_cidreq()); + $form = new FormValidator( + 'note', + 'post', + api_get_self().'?'.http_build_query([ + 'exerciseId' => $exerciseId, + 'action' => $action, + ]).'&'.api_get_cidreq() + ); // Setting the form elements $form->addElement('header', get_lang('AddACategory')); $form->addElement('text', 'category_name', get_lang('PtestCategoryName'), ['size' => '95']); @@ -243,11 +256,14 @@ function displayActionBar() { $exerciseId = (int) $_GET['exerciseId']; echo '
'; - echo ''. - Display::return_icon('back.png', get_lang('GoBackToQuestionList'), '', ICON_SIZE_MEDIUM).''; + $urlParams = 'exerciseId='.$exerciseId.'&'.api_get_cidreq(); + echo ''. + Display::return_icon('back.png', get_lang('GoBackToQuestionList'), '', ICON_SIZE_MEDIUM). + ''; echo ''. - Display::return_icon('new_folder.png', get_lang('AddACategory'), null, ICON_SIZE_MEDIUM).''; + Display::return_icon('new_folder.png', get_lang('AddACategory'), null, ICON_SIZE_MEDIUM). + ''; echo '
'; echo "
"; diff --git a/main/exercise/question.class.php b/main/exercise/question.class.php index 14b4001225b..6d382e1b5ab 100755 --- a/main/exercise/question.class.php +++ b/main/exercise/question.class.php @@ -142,9 +142,9 @@ public static function read($id, $course_info = [], $getExerciseList = true, $pt if (empty($course_info)) { $course_info = api_get_course_info(); } - $course_id = $course_info['real_id']; + $courseId = $course_info['real_id']; - if (empty($course_id) || $course_id == -1) { + if (empty($courseId) || $courseId == -1) { return false; } @@ -153,7 +153,7 @@ public static function read($id, $course_info = [], $getExerciseList = true, $pt $sql = "SELECT * FROM $TBL_QUESTIONS - WHERE c_id = $course_id AND id = $id "; + WHERE c_id = $courseId AND id = $id "; $result = Database::query($sql); // if the question has been found @@ -172,7 +172,7 @@ public static function read($id, $course_info = [], $getExerciseList = true, $pt $objQuestion->extra = $object->extra; $objQuestion->course = $course_info; $objQuestion->feedback = isset($object->feedback) ? $object->feedback : ''; - $objQuestion->category = TestCategory::getCategoryForQuestion($id, $course_id); + $objQuestion->category = TestCategory::getCategoryForQuestion($id, $courseId); $objQuestion->code = isset($object->code) ? $object->code : ''; if ($getExerciseList) { @@ -182,7 +182,7 @@ public static function read($id, $course_info = [], $getExerciseList = true, $pt INNER JOIN $tblQuiz e ON e.c_id = q.c_id AND e.id = q.exercice_id WHERE - q.c_id = $course_id AND + q.c_id = $courseId AND q.question_id = $id AND e.active >= 0"; @@ -644,10 +644,10 @@ public function updateLevel($level) public function updateType($type) { $table = Database::get_course_table(TABLE_QUIZ_ANSWER); - $course_id = $this->course['real_id']; + $courseId = $this->course['real_id']; - if (empty($course_id)) { - $course_id = api_get_course_int_id(); + if (empty($courseId)) { + $courseId = api_get_course_int_id(); } // if we really change the type if ($type != $this->type) { @@ -657,7 +657,7 @@ public function updateType($type) ) { // removes old answers $sql = "DELETE FROM $table - WHERE c_id = $course_id AND question_id = ".intval($this->id); + WHERE c_id = $courseId AND question_id = ".intval($this->id); Database::query($sql); } @@ -808,7 +808,7 @@ public function exportPicture($questionId, $courseInfo) return false; } - $course_id = $courseInfo['real_id']; + $courseId = $courseInfo['real_id']; $destination_path = $this->getHotSpotFolderInCourse($courseInfo); if (empty($destination_path)) { @@ -822,7 +822,7 @@ public function exportPicture($questionId, $courseInfo) return false; } - $sourcePictureName = $this->getPictureFilename($course_id); + $sourcePictureName = $this->getPictureFilename($courseId); $picture = $this->generatePictureName(); $result = false; if (file_exists($source_path.'/'.$sourcePictureName)) { @@ -852,7 +852,7 @@ public function exportPicture($questionId, $courseInfo) $table = Database::get_course_table(TABLE_QUIZ_QUESTION); $sql = "UPDATE $table SET picture = '".Database::escape_string($picture)."' - WHERE c_id = $course_id AND id='".intval($questionId)."'"; + WHERE c_id = $courseId AND id='".intval($questionId)."'"; Database::query($sql); $documentId = add_document( @@ -1114,19 +1114,19 @@ public function search_engine_edit( if (!empty($exerciseId) && api_get_setting('search_enabled') == 'true' && extension_loaded('xapian') ) { - $course_id = api_get_course_id(); + $courseId = api_get_course_id(); // get search_did $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF); if ($addQs || $rmQs) { //there's only one row per question on normal db and one document per question on search engine db $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_second_level=%s LIMIT 1'; - $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id); + $sql = sprintf($sql, $tbl_se_ref, $courseId, TOOL_QUIZ, $this->id); } else { $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%s LIMIT 1'; - $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $exerciseId, $this->id); + $sql = sprintf($sql, $tbl_se_ref, $courseId, TOOL_QUIZ, $exerciseId, $this->id); } $res = Database::query($sql); @@ -1173,10 +1173,10 @@ public function search_engine_edit( // build the chunk to index $ic_slide = new IndexableChunk(); $ic_slide->addValue("title", $this->question); - $ic_slide->addCourseId($course_id); + $ic_slide->addCourseId($courseId); $ic_slide->addToolId(TOOL_QUIZ); $xapian_data = [ - SE_COURSE_ID => $course_id, + SE_COURSE_ID => $courseId, SE_TOOL_ID => TOOL_QUIZ, SE_DATA => [ 'type' => SE_DOCTYPE_EXERCISE_QUESTION, @@ -1203,7 +1203,7 @@ public function search_engine_edit( if ($addQs || $rmQs) { $sql = "DELETE FROM %s WHERE course_code = '%s' AND tool_id = '%s' AND ref_id_second_level = '%s'"; - $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id); + $sql = sprintf($sql, $tbl_se_ref, $courseId, TOOL_QUIZ, $this->id); } else { $sql = "DELETE FROM %S WHERE @@ -1212,7 +1212,7 @@ public function search_engine_edit( AND tool_id = '%s' AND ref_id_high_level = '%s' AND ref_id_second_level = '%s'"; - $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $exerciseId, $this->id); + $sql = sprintf($sql, $tbl_se_ref, $courseId, TOOL_QUIZ, $exerciseId, $this->id); } Database::query($sql); if ($rmQs) { @@ -1226,7 +1226,7 @@ public function search_engine_edit( $sql = sprintf( $sql, $tbl_se_ref, - $course_id, + $courseId, TOOL_QUIZ, array_shift($question_exercises), $this->id, @@ -1241,7 +1241,7 @@ public function search_engine_edit( VALUES ( NULL , '%s', '%s', %s, %s, %s )"; - $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $exerciseId, $this->id, $did); + $sql = sprintf($sql, $tbl_se_ref, $courseId, TOOL_QUIZ, $exerciseId, $this->id, $did); Database::query($sql); } } @@ -1490,14 +1490,14 @@ public function duplicate($courseInfo = []) ); } - $course_id = $courseInfo['real_id']; + $courseId = $courseInfo['real_id']; // Read the source options $options = self::readQuestionOption($this->id, $this->course['real_id']); // Inserting in the new course db / or the same course db $params = [ - 'c_id' => $course_id, + 'c_id' => $courseId, 'question' => $question, 'description' => $description, 'ponderation' => $weighting, @@ -1518,7 +1518,7 @@ public function duplicate($courseInfo = []) // Saving the quiz_options foreach ($options as $item) { $item['question_id'] = $newQuestionId; - $item['c_id'] = $course_id; + $item['c_id'] = $courseId; unset($item['id']); unset($item['iid']); $id = Database::insert($TBL_QUESTION_OPTIONS, $item); @@ -1694,12 +1694,12 @@ public function createForm(&$form, $exercise) if ($this->type != MEDIA_QUESTION) { // Advanced parameters - $select_level = self::get_default_levels(); + $selectLevel = self::get_default_levels(); $form->addElement( 'select', 'questionLevel', get_lang('Difficulty'), - $select_level + $selectLevel ); // Categories @@ -1823,9 +1823,8 @@ public function createForm(&$form, $exercise) * A subclass can redefine this function to add fields... * * @param FormValidator $form - * @param Exercise $exercise */ - public function createPtForm(&$form, $exercise) + public function createPtForm(&$form) { echo '