diff --git a/main/exercise/question_pool.php b/main/exercise/question_pool.php
index 06bfbec0570..209541f35f8 100755
--- a/main/exercise/question_pool.php
+++ b/main/exercise/question_pool.php
@@ -23,6 +23,19 @@
$is_allowedToEdit = api_is_allowed_to_edit(null, true);
+$extended = api_get_plugin_setting('extendedquestionpool', 'enable_plugin');
+
+if ($extended == 'true') {
+ $query = $_SERVER['QUERY_STRING'] ?? '';
+ $urlDest = 'question_pool_extended.php';
+
+ if (!empty($query)) {
+ $urlDest .= '?' . $query;
+ }
+ header('Location: ' . $urlDest);
+ exit;
+}
+
$delete = isset($_GET['delete']) ? (int) $_GET['delete'] : null;
$recup = isset($_GET['recup']) ? (int) $_GET['recup'] : null;
$fromExercise = isset($_REQUEST['fromExercise']) ? (int) $_REQUEST['fromExercise'] : null;
@@ -510,7 +523,15 @@ function confirm_your_choice() {
$form->addHidden('exercise_id_changed', '0');
$extraField = new ExtraField('question');
-$jsForExtraFields = $extraField->addElements($form, 0, [], true);
+$extendedExtraFields = array(
+ "additional_question_category",
+ "question_data1",
+ "question_data2",
+ "question_data3",
+ "question_extra_info"
+ );
+$jsForExtraFields = $extraField->addElements($form, 0, $extendedExtraFields, true);
+
$form->addButtonFilter(get_lang('Filter'), 'name');
diff --git a/main/exercise/question_pool_extended.php b/main/exercise/question_pool_extended.php
new file mode 100644
index 00000000000..c940dca3f04
--- /dev/null
+++ b/main/exercise/question_pool_extended.php
@@ -0,0 +1,1390 @@
+read($fromExercise, false);
+}
+
+$nameTools = get_lang('QuestionPool');
+$interbreadcrumb[] = ['url' => 'exercise.php?'.api_get_cidreq(), 'name' => get_lang('Exercises')];
+
+if (!empty($objExercise->iid)) {
+ $interbreadcrumb[] = [
+ 'url' => 'admin.php?exerciseId='.$objExercise->iid.'&'.api_get_cidreq(),
+ 'name' => $objExercise->selectTitle(true),
+ ];
+}
+
+// message to be displayed if actions successful
+$displayMessage = '';
+if ($is_allowedToEdit) {
+ // Duplicating a Question
+ if (!isset($_POST['recup']) && $question_copy != 0 && isset($fromExercise)) {
+ $origin_course_id = (int) $_GET['course_id'];
+ $origin_course_info = api_get_course_info_by_id($origin_course_id);
+ $current_course = api_get_course_info();
+ $old_question_id = $question_copy;
+ // Reading the source question
+ $old_question_obj = Question::read($old_question_id, $origin_course_info);
+ $courseId = $current_course['real_id'];
+ if ($old_question_obj) {
+ $old_question_obj->updateTitle($old_question_obj->selectTitle().' - '.get_lang('Copy'));
+ //Duplicating the source question, in the current course
+ $new_id = $old_question_obj->duplicate($current_course);
+ //Reading new question
+ $new_question_obj = Question::read($new_id);
+ $new_question_obj->addToList($fromExercise);
+ //Reading Answers obj of the current course
+ $new_answer_obj = new Answer($old_question_id, $origin_course_id);
+ $new_answer_obj->read();
+ //Duplicating the Answers in the current course
+ $new_answer_obj->duplicate($new_question_obj, $current_course);
+ // destruction of the Question object
+ unset($new_question_obj);
+ unset($old_question_obj);
+
+ $objExercise = new Exercise($courseId);
+ $objExercise->read($fromExercise);
+ Session::write('objExercise', $objExercise);
+ }
+
+ $nQuestions = $objExercise->getQuestionCount();
+ $displayMessage = get_lang('ItemAdded').' ('.$nQuestions.' '.get_lang('Questions').')';
+
+ }
+
+ // Deletes a question from the database and all exercises
+ if ($delete) {
+ $limitTeacherAccess = api_get_configuration_value('limit_exercise_teacher_access');
+ if ($limitTeacherAccess && !api_is_platform_admin()) {
+ api_not_allowed(true);
+ }
+ // Construction of the Question object
+ $objQuestionTmp = isQuestionInActiveQuiz($delete) ? false : Question::read($delete);
+ // if the question exists
+ if ($objQuestionTmp) {
+ // deletes the question from all exercises
+ $objQuestionTmp->delete();
+
+ // solving the error that when deleting a question from the question pool it is not displaying all questions
+ $exerciseId = null;
+ }
+ // destruction of the Question object
+ unset($objQuestionTmp);
+ } elseif ($recup && $fromExercise) {
+ // gets an existing question and copies it into a new exercise
+ $objQuestionTmp = Question::read($recup);
+ // if the question exists
+ if ($objQuestionTmp) {
+ /* Adds the exercise ID represented by $fromExercise into the list
+ of exercises for the current question */
+ $objQuestionTmp->addToList($fromExercise);
+ }
+ // destruction of the Question object
+ unset($objQuestionTmp);
+
+ if (!$objExercise instanceof Exercise) {
+ $objExercise = new Exercise();
+ $objExercise->read($fromExercise);
+ }
+ // Adds the question ID represented by $recup into the list of questions for the current exercise
+ $objExercise->addToList($recup);
+ Session::write('objExercise', $objExercise);
+ $nQuestions = $objExercise->getQuestionCount();
+ //Display::addFlash(Display::return_message(get_lang('ItemAdded'), 'success'));
+ Display::addFlash(Display::return_message(get_lang('ItemAdded').' ('.$nQuestions.' '.get_lang('Questions').')', 'success'));
+ } elseif (isset($_POST['recup']) && is_array($_POST['recup']) && $fromExercise) {
+ $list_recup = $_POST['recup'];
+ foreach ($list_recup as $course_id => $question_data) {
+ $origin_course_id = (int) $course_id;
+ $origin_course_info = api_get_course_info_by_id($origin_course_id);
+ $current_course = api_get_course_info();
+ foreach ($question_data as $old_question_id) {
+ // Reading the source question
+ $old_question_obj = Question::read($old_question_id, $origin_course_info);
+ if ($old_question_obj) {
+ $old_question_obj->updateTitle(
+ $old_question_obj->selectTitle().' - '.get_lang('Copy')
+ );
+
+ // Duplicating the source question, in the current course
+ $new_id = $old_question_obj->duplicate($current_course);
+
+ // Reading new question
+ $new_question_obj = Question::read($new_id);
+ $new_question_obj->addToList($fromExercise);
+
+ //Reading Answers obj of the current course
+ $new_answer_obj = new Answer($old_question_id, $origin_course_id);
+ $new_answer_obj->read();
+
+ //Duplicating the Answers in the current course
+ $new_answer_obj->duplicate($new_question_obj, $current_course);
+
+ // destruction of the Question object
+ unset($new_question_obj);
+ unset($old_question_obj);
+
+ if (!$objExercise instanceof Exercise) {
+ $objExercise = new Exercise();
+ $objExercise->read($fromExercise);
+ }
+ }
+ }
+ }
+ Session::write('objExercise', $objExercise);
+ $nQuestions = $objExercise->getQuestionCount();
+ Display::addFlash(Display::return_message(get_lang('Done').' ('.$nQuestions.' '.get_lang('Questions').')', 'success'));
+ }
+}
+
+if (api_is_in_gradebook()) {
+ $interbreadcrumb[] = [
+ 'url' => Category::getUrl(),
+ 'name' => get_lang('ToolGradebook'),
+ ];
+}
+
+// if admin of course
+if (!$is_allowedToEdit) {
+ api_not_allowed(true);
+}
+
+$confirmYourChoice = addslashes(api_htmlentities(get_lang('ConfirmYourChoice'), ENT_QUOTES, $charset));
+$htmlHeadXtra[] = "
+";
+
+$url = api_get_self().'?'.api_get_cidreq().'&'.http_build_query(
+ [
+ 'fromExercise' => $fromExercise,
+ 'session_id' => $session_id,
+ 'selected_course' => $selected_course,
+ 'courseCategoryId' => $courseCategoryId,
+ 'exerciseId' => $exerciseId,
+ 'exerciseLevel' => $exerciseLevel,
+ 'answerType' => $answerType,
+ 'question_id' => $questionId,
+ 'description' => Security::remove_XSS($description),
+ 'course_id_changed' => $course_id_changed,
+ 'exercise_id_changed' => $exercise_id_changed,
+ ]
+);
+
+if (isset($_REQUEST['action'])) {
+ switch ($_REQUEST['action']) {
+ case 'score':
+ if (!empty($_REQUEST['questions'])) {
+ $txtError = '';
+ $questions = $_REQUEST['questions'];
+ $correctScore = api_get_plugin_setting('extendedquestionpool', 'correct_score');
+ $errorScore = api_get_plugin_setting('extendedquestionpool', 'error_score');
+ if (!is_numeric($correctScore) || $correctScore<=0) {
+ Display::addFlash(Display::return_message('Debe definir correctamente las puntuaciones en la configuración del plugin', 'error'));
+ } elseif (!empty($errorScore) && !is_numeric($errorScore)) {
+ Display::addFlash(Display::return_message('Debe definir correctamente las puntuaciones en la configuración del plugin', 'error'));
+ } elseif (count($questions) > 0) {
+ foreach ($questions as $questionId) {
+ $objQuestionTmp = Question::read($questionId);
+ if ($objQuestionTmp) {
+ if ($objQuestionTmp->selectType()==UNIQUE_ANSWER) {
+ $table = Database::get_course_table(TABLE_QUIZ_ANSWER);
+ $query1 = "UPDATE $table SET ponderation = $correctScore
+ WHERE question_id=$questionId AND correct = 1";
+ $query2 = "UPDATE $table SET ponderation = $errorScore
+ WHERE question_id=$questionId AND correct = 0";
+ $tableQ = Database::get_course_table(TABLE_QUIZ_QUESTION);
+ $query3 = "UPDATE $tableQ SET ponderation = $correctScore
+ WHERE iid=$questionId";
+ Database::query($query1);
+ Database::query($query2);
+ Database::query($query3);
+ }
+ }
+ }
+ }
+ header('Location: '.$url);
+ exit;
+ }
+ break;
+ case 'reuse':
+ if (!empty($_REQUEST['questions']) && !empty($fromExercise)) {
+ $questions = $_REQUEST['questions'];
+ $objExercise = new Exercise();
+ $objExercise->read($fromExercise, false);
+
+ if (count($questions) > 0) {
+ foreach ($questions as $questionId) {
+ // gets an existing question and copies it into a new exercise
+ $objQuestionTmp = Question::read($questionId);
+ // if the question exists
+ if ($objQuestionTmp) {
+ if (false === $objExercise->hasQuestion($questionId)) {
+ $objExercise->addToList($questionId);
+ $objQuestionTmp->addToList($fromExercise);
+ }
+ }
+ }
+ }
+
+ $nQuestions = $objExercise->getQuestionCount();
+ Display::addFlash(Display::return_message(get_lang('Added').' ('.$nQuestions.' '.get_lang('Questions').')', 'success'));
+
+ header('Location: '.$url);
+ exit;
+ }
+ break;
+ case 'clone':
+ if (!empty($_REQUEST['questions']) && !empty($fromExercise)) {
+ $questions = $_REQUEST['questions'];
+ $origin_course_id = (int) $_GET['course_id'];
+
+ $origin_course_info = api_get_course_info_by_id($origin_course_id);
+ $current_course = api_get_course_info();
+
+ if (count($questions) > 0) {
+ foreach ($questions as $questionId) {
+ // gets an existing question and copies it into a new exercise
+ // Reading the source question
+ $old_question_obj = Question::read($questionId, $origin_course_info);
+ $courseId = $current_course['real_id'];
+ if ($old_question_obj) {
+ $old_question_obj->updateTitle($old_question_obj->selectTitle().' - '.get_lang('Copy'));
+ // Duplicating the source question, in the current course
+ $new_id = $old_question_obj->duplicate($current_course);
+ // Reading new question
+ $new_question_obj = Question::read($new_id);
+ $new_question_obj->addToList($fromExercise);
+ //Reading Answers obj of the current course
+ $new_answer_obj = new Answer($questionId, $origin_course_id);
+ $new_answer_obj->read();
+ //Duplicating the Answers in the current course
+ $new_answer_obj->duplicate($new_question_obj, $current_course);
+ // destruction of the Question object
+ unset($new_question_obj);
+ unset($old_question_obj);
+ }
+ }
+ }
+
+ Display::addFlash(Display::return_message(get_lang('Added')));
+ header('Location: '.$url);
+ exit;
+ }
+ break;
+ }
+}
+
+Display::display_header($nameTools, 'Exercise');
+
+// Menu
+echo '
';
+
+if ('' != $displayMessage) {
+ echo Display::return_message($displayMessage, 'confirm');
+}
+
+// Session list, if sessions are used.
+$sessionList = SessionManager::get_sessions_by_user(api_get_user_id(), api_is_platform_admin());
+$session_select_list = ['-1' => get_lang('Select')];
+foreach ($sessionList as $item) {
+ $session_select_list[$item['session_id']] = $item['session_name'];
+}
+
+// Course list, get course list of session, or for course where user is admin
+$course_list = [];
+
+// Course list, get course list of session, or for course where user is admin
+if (!empty($session_id) && $session_id != '-1' && !empty($sessionList)) {
+ $sessionInfo = [];
+ foreach ($sessionList as $session) {
+ if ($session['session_id'] == $session_id) {
+ $sessionInfo = $session;
+ }
+ }
+ $course_list = $sessionInfo['courses'];
+} else {
+ if (api_is_platform_admin()) {
+ $course_list = CourseManager::get_courses_list(0, 0, 'title');
+ } else {
+ $course_list = CourseManager::get_course_list_of_user_as_course_admin(api_get_user_id());
+ }
+
+ // Admin fix, add the current course in the question pool.
+ if (api_is_platform_admin()) {
+ $courseInfo = api_get_course_info();
+ if (!empty($course_list)) {
+ if (!in_array($courseInfo['real_id'], $course_list)) {
+ $course_list = array_merge($course_list, [$courseInfo]);
+ }
+ } else {
+ $course_list = [$courseInfo];
+ }
+ }
+}
+
+$course_select_list = ['-1' => get_lang('Select')];
+foreach ($course_list as $item) {
+ $courseItemId = $item['real_id'];
+ $courseInfo = api_get_course_info_by_id($courseItemId);
+ $course_select_list[$courseItemId] = '';
+ if ($courseItemId == api_get_course_int_id()) {
+ $course_select_list[$courseItemId] = '> ';
+ }
+ $course_select_list[$courseItemId] .= $courseInfo['title'];
+}
+
+if (empty($selected_course) || $selected_course == '-1') {
+ $course_info = api_get_course_info();
+ // no course selected, reset menu test / difficult� / type de reponse
+ reset_menu_exo_lvl_type();
+} else {
+ $course_info = api_get_course_info_by_id($selected_course);
+}
+// If course has changed, reset the menu default
+if ($course_id_changed) {
+ reset_menu_exo_lvl_type();
+}
+
+// Get category list for the course $selected_course
+$categoryList = TestCategory::getCategoriesIdAndName($selected_course);
+
+// Get exercise list for this course
+$exercise_list = ExerciseLib::get_all_exercises_for_course_id(
+ $course_info,
+ $session_id,
+ $selected_course,
+ false
+);
+
+if (1 == $exercise_id_changed) {
+ reset_menu_lvl_type();
+}
+
+// Exercise List
+$my_exercise_list = [];
+$my_exercise_list['0'] = get_lang('AllExercises');
+$my_exercise_list['-1'] = get_lang('OrphanQuestions');
+$titleSavedAsHtml = api_get_configuration_value('save_titles_as_html');
+if (is_array($exercise_list)) {
+ foreach ($exercise_list as $row) {
+ $my_exercise_list[$row['iid']] = '';
+ if ($row['iid'] == $fromExercise && $selected_course == api_get_course_int_id()) {
+ $my_exercise_list[$row['iid']] = "> ";
+ }
+
+ $exerciseTitle = $row['title'];
+ if ($titleSavedAsHtml) {
+ $exerciseTitle = strip_tags(api_html_entity_decode(trim($exerciseTitle)));
+ }
+ $my_exercise_list[$row['iid']] .= $exerciseTitle;
+ }
+}
+
+// Difficulty list (only from 0 to 5)
+$levels = [
+ -1 => get_lang('All'),
+ 0 => 0,
+ 1 => 1,
+ 2 => 2,
+ 3 => 3,
+ 4 => 4,
+ 5 => 5,
+];
+
+// Answer type
+$question_list = Question::getQuestionTypeList();
+
+$new_question_list = [];
+$new_question_list['-1'] = get_lang('All');
+if (!empty($_course)) {
+ foreach ($question_list as $key => $item) {
+ if (in_array(
+ $objExercise->getFeedbackType(),
+ [EXERCISE_FEEDBACK_TYPE_DIRECT, EXERCISE_FEEDBACK_TYPE_POPUP]
+ )) {
+ if (!in_array($key, [HOT_SPOT_DELINEATION, UNIQUE_ANSWER])) {
+ continue;
+ }
+ $new_question_list[$key] = get_lang($item[1]);
+ } else {
+ if (HOT_SPOT_DELINEATION == $key) {
+ continue;
+ }
+ $new_question_list[$key] = get_lang($item[1]);
+ }
+ }
+}
+
+// Form
+$form = new FormValidator('question_pool', 'GET', $url);
+$form->addHeader($nameTools.' - '.$titleAdd);
+$form->addHidden('fromExercise', $fromExercise);
+$form
+ ->addSelect(
+ 'session_id',
+ get_lang('Session'),
+ $session_select_list,
+ ['onchange' => 'submit_form(this)', 'id' => 'session_id']
+ )
+ ->setSelected($session_id);
+$form
+ ->addSelect(
+ 'selected_course',
+ get_lang('Course'),
+ $course_select_list,
+ ['onchange' => 'mark_course_id_changed(); submit_form(this);', 'id' => 'selected_course']
+ )
+ ->setSelected($selected_course);
+$form
+ ->addSelect(
+ 'courseCategoryId',
+ get_lang('QuestionCategory'),
+ $categoryList,
+ ['onchange' => 'submit_form(this);', 'id' => 'courseCategoryId']
+ )
+ ->setSelected($courseCategoryId);
+$form
+ ->addSelect(
+ 'exerciseId',
+ get_lang('Exercise'),
+ $my_exercise_list,
+ ['onchange' => 'mark_exercise_id_changed(); submit_form(this);', 'id' => 'exerciseId']
+ )
+ ->setSelected($exerciseId);
+$form
+ ->addSelect(
+ 'exerciseLevel',
+ get_lang('Difficulty'),
+ $levels,
+ ['onchange' => 'submit_form(this);', 'id' => 'exerciseLevel']
+ )
+ ->setSelected($exerciseLevel);
+$form
+ ->addSelect(
+ 'answerType',
+ get_lang('AnswerType'),
+ $new_question_list,
+ ['onchange' => 'submit_form(this);', 'id' => 'answerType']
+ )
+ ->setSelected($answerType);
+$form
+ ->addText('question_id', get_lang('Id'), false)
+ ->setValue($questionId);
+$form
+ ->addText('description', get_lang('Description'), false)
+ ->setValue(Security::remove_XSS($description));
+
+$form->addHidden('course_id_changed', '0');
+$form->addHidden('exercise_id_changed', '0');
+
+$extraField = new ExtraField('question');
+
+$jsForExtraFields = $extraField->addElements($form, 0, [], true);
+
+$form->addButtonFilter(get_lang('Filter'), 'name');
+
+echo $form->display();
+
+echo '';
+?>
+
+validate() ? $form->exportValues() : [];
+/**
+ * @return array
+ */
+function getExtraFieldConditions(array $formValues, $queryType = 'from')
+{
+ $extraField = new ExtraField('question');
+ $fields = $extraField->get_all(
+ ['visible_to_self = ? AND filter = ?' => [1, 1]],
+ 'display_text'
+ );
+
+ $from = '';
+ $where = '';
+
+ foreach ($fields as $field) {
+ $variable = $field['variable'];
+
+ if (empty($formValues["extra_$variable"])) {
+ continue;
+ }
+
+ $value = $formValues["extra_$variable"];
+
+ switch ($field['field_type']) {
+ case ExtraField::FIELD_TYPE_CHECKBOX:
+ $value = $value["extra_$variable"];
+ break;
+ case ExtraField::FIELD_TYPE_DOUBLE_SELECT:
+ if (!isset($value["extra_{$variable}_second"])) {
+ $value = null;
+ break;
+ }
+
+ $value = $value["extra_$variable"].'::'.$value["extra_{$variable}_second"];
+ break;
+ }
+
+ if (empty($value)) {
+ continue;
+ }
+
+ if ($queryType === 'from') {
+ $from .= ", extra_field_values efv_$variable, extra_field ef_$variable";
+ $where .= "AND (
+ qu.iid = efv_$variable.item_id
+ AND efv_$variable.field_id = ef_$variable.id
+ AND ef_$variable.extra_field_type = ".ExtraFieldEntity::QUESTION_FIELD_TYPE."
+ AND ef_$variable.variable = '$variable'
+ AND efv_$variable.value = '$value'
+ )";
+ } elseif ($queryType === 'join') {
+ $from .= " INNER JOIN extra_field_values efv_$variable ON qu.iid = efv_$variable.item_id
+ INNER JOIN extra_field ef_$variable ON efv_$variable.field_id = ef_$variable.id";
+ $where .= "AND (
+ ef_$variable.extra_field_type = ".ExtraFieldEntity::QUESTION_FIELD_TYPE."
+ AND ef_$variable.variable = '$variable'
+ AND efv_$variable.value = '$value'
+ )";
+ }
+ }
+
+ return [
+ 'from' => $from,
+ 'where' => $where,
+ ];
+}
+/**
+ * get question list for sortable table
+ */
+function getQuestions($from, $number_of_items, $column, $direction, $getCount = false) {
+ $start = (int) $from;
+ $length = (int) $number_of_items;
+ if (empty($length)) {
+ $length = 20;
+ }
+ global $exerciseId,
+ $courseCategoryId,
+ $selected_course,
+ $session_id,
+ $exerciseLevel,
+ $answerType,
+ $questionId,
+ $description,
+ $fromExercise,
+ $formValues,
+ $objExercise,
+ $actionIcon1,
+ $actionIcon2,
+ $questionTagA;
+
+ $TBL_EXERCISE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
+ $TBL_EXERCISES = Database::get_course_table(TABLE_QUIZ_TEST);
+ $TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION);
+ $TBL_COURSE_REL_CATEGORY = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY);
+
+ $currentExerciseCondition = '';
+ if (!empty($fromExercise)) {
+ $currentCourseId = api_get_course_int_id();
+ $currentExerciseCondition = "
+ AND qu.iid NOT IN (
+ SELECT question_id FROM $TBL_EXERCISE_QUESTION
+ WHERE exercice_id = $fromExercise AND c_id = $currentCourseId
+ )";
+ }
+
+ // if we have selected an exercise in the list-box 'Filter'
+ if ($exerciseId > 0) {
+ $efConditions = getExtraFieldConditions($formValues, 'from');
+
+ $where = '';
+ $from = '';
+ if (isset($courseCategoryId) && $courseCategoryId > 0) {
+ $from = ", $TBL_COURSE_REL_CATEGORY crc ";
+ $where .= " AND
+ crc.c_id = $selected_course AND
+ crc.question_id = qu.iid AND
+ crc.category_id = $courseCategoryId";
+ }
+ if (isset($exerciseLevel) && -1 != $exerciseLevel) {
+ $where .= ' AND level = '.$exerciseLevel;
+ }
+ if (isset($answerType) && $answerType > 0) {
+ $where .= ' AND type = '.$answerType;
+ }
+
+ if (!empty($questionId)) {
+ $where .= ' AND qu.iid = '.$questionId;
+ }
+
+ if (!empty($description)) {
+ $where .= " AND qu.description LIKE '%$description%'";
+ }
+
+ $select = 'DISTINCT
+ qu.iid,
+ qu.question,
+ qu.type,
+ qu.level,
+ qt.exercice_id exerciseId';
+ if ($getCount) {
+ //$select = 'count(qu.iid) as count';
+ }
+ $sql = "SELECT $select
+ FROM
+ $TBL_EXERCISE_QUESTION qt
+ INNER JOIN $TBL_QUESTIONS qu
+ ON qt.question_id = qu.iid
+ $from
+ {$efConditions['from']}
+ WHERE
+ qt.exercice_id = $exerciseId AND
+ qt.c_id = $selected_course
+ $where
+ $currentExerciseCondition
+ {$efConditions['where']}
+ ORDER BY BINARY qu.question ASC
+ ";
+ } elseif ($exerciseId == -1) {
+ $efConditions = getExtraFieldConditions($formValues, 'join');
+ // If we have selected the option 'Orphan questions' in the list-box 'Filter'
+ $level_where = '';
+ $from = '';
+ if (isset($courseCategoryId) && $courseCategoryId > 0) {
+ $from = " INNER JOIN $TBL_COURSE_REL_CATEGORY crc
+ ON crc.question_id = ex.iid ";
+ $level_where .= " AND
+ crc.c_id = $selected_course AND
+ crc.category_id = $courseCategoryId";
+ }
+ if (isset($exerciseLevel) && -1 != $exerciseLevel) {
+ $level_where = ' AND level='.$exerciseLevel;
+ }
+ $answer_where = '';
+ if (isset($answerType) && $answerType > 0 - 1) {
+ $answer_where = ' AND qu.type='.$answerType;
+ }
+
+ if (!empty($questionId)) {
+ $answer_where .= ' AND ex.iid = '.$questionId;
+ }
+
+ if (!empty($description)) {
+ $answer_where .= " AND ex.description LIKE '%$description%'";
+ }
+
+ $select = ' qu.*, r.exercice_id exerciseId ';
+
+ $sql = " (
+ SELECT $select
+ FROM $TBL_QUESTIONS qu
+ INNER JOIN $TBL_EXERCISE_QUESTION r
+ ON qu.iid = r.question_id
+ INNER JOIN $TBL_EXERCISES ex
+ ON ex.iid = r.exercice_id
+ $from
+ {$efConditions['from']}
+ WHERE
+ ex.c_id = $selected_course AND
+ ex.active = '-1'
+ $level_where
+ $answer_where
+ {$efConditions['where']}
+ )
+ UNION
+ (
+ SELECT $select
+ FROM $TBL_QUESTIONS qu
+ LEFT OUTER JOIN $TBL_EXERCISE_QUESTION r
+ ON qu.iid = r.question_id
+ $from
+ {$efConditions['from']}
+ WHERE
+ qu.c_id = $selected_course AND
+ r.question_id is null
+ $level_where
+ $answer_where
+ {$efConditions['where']}
+ )
+ UNION
+ (
+ SELECT $select
+ FROM $TBL_QUESTIONS qu
+ INNER JOIN $TBL_EXERCISE_QUESTION r
+ ON qu.iid = r.question_id
+ $from
+ {$efConditions['from']}
+ WHERE
+ r.c_id = $selected_course AND
+ (r.exercice_id = '-1' OR r.exercice_id = '0')
+ $level_where
+ $answer_where
+ {$efConditions['where']}
+ )
+ ";
+ } else {
+ $efConditions = getExtraFieldConditions($formValues, 'from');
+ // All tests for selected course
+ // If we have not selected any option in the list-box 'Filter'
+ $filter = '';
+ $from = '';
+ if (isset($courseCategoryId) && $courseCategoryId > 0) {
+ $from = ", $TBL_COURSE_REL_CATEGORY crc ";
+ $filter .= " AND
+ crc.c_id = $selected_course AND
+ crc.question_id = qu.iid AND
+ crc.category_id = $courseCategoryId";
+ }
+ if (isset($exerciseLevel) && -1 != $exerciseLevel) {
+ $filter .= ' AND level = '.$exerciseLevel.' ';
+ }
+ if (isset($answerType) && $answerType > 0) {
+ $filter .= ' AND qu.type = '.$answerType.' ';
+ }
+
+ if (!empty($questionId)) {
+ $filter .= ' AND qu.iid = '.$questionId;
+ }
+
+ if (!empty($description)) {
+ $filter .= " AND qu.description LIKE '%$description%'";
+ }
+
+ if (-1 == $session_id || empty($session_id)) {
+ $session_id = 0;
+ }
+ $sessionCondition = api_get_session_condition($session_id, true, 'ex.session_id');
+
+ $select = 'qu.iid, question, qu.type, level, ex.session_id, qt.exercice_id exerciseId ';
+
+ // All tests for the course selected, not in session
+ $sql = "SELECT DISTINCT
+ $select
+ FROM
+ $TBL_QUESTIONS as qu
+ INNER JOIN $TBL_EXERCISE_QUESTION as qt
+ ON qu.iid = qt.question_id
+ INNER JOIN $TBL_EXERCISES as ex
+ ON ex.iid = qt.exercice_id
+ {$efConditions['from']}
+ $from
+ WHERE
+ qt.c_id = $selected_course AND
+ ex.c_id = $selected_course
+ $sessionCondition
+ $filter
+ $currentExerciseCondition
+ {$efConditions['where']}
+ GROUP BY qu.iid
+ ORDER BY BINARY qu.question ASC
+ ";
+ }
+
+ if ($getCount) {
+ $result = Database::query($sql);
+ return Database::num_rows($result);
+ }
+ //$sql .= " LIMIT $start, $length";
+ $result = Database::query($sql);
+
+ $mainQuestionList = [];
+ while ($row = Database::fetch_array($result, 'ASSOC')) {
+ if ($exerciseId == -1 && isQuestionInActiveQuiz($row['iid'])) {
+ continue;
+ }
+
+ $mainQuestionList[] = $row;
+ }
+
+ $QList = [];
+ foreach ($mainQuestionList as $question) {
+ $row = [];
+ // This function checks if the question can be read
+ $question_type = get_question_type_for_question($selected_course, $question['iid']);
+
+ if (empty($question_type)) {
+ continue;
+ }
+ $sessionId = isset($question['session_id']) ? $question['session_id'] : null;
+ if (!$objExercise->hasQuestion($question['iid'])) {
+ $row[0] = Display::input(
+ 'checkbox',
+ 'questions[]',
+ $question['iid'],
+ ['class' => 'question_checkbox']
+ );
+ } else {
+ $row[1] = '';
+ }
+ $row[1] = $question['iid'];
+
+ $row[2] = getLinkForQuestion(
+ $questionTagA,
+ $fromExercise,
+ $question['iid'],
+ $question['type'],
+ $question['question'],
+ $sessionId,
+ $question['exerciseId']
+ );
+
+ $row[3] = $question_type;
+ $row[4] = TestCategory::getCategoryNameForQuestion($question['iid'], $selected_course);
+ $row[5] = $question['level'];
+
+ $row[6] = getQuestionOcurrences($question['iid']);
+ $row[7] = getQuestionFailures($question['iid']);
+ $row[8] = getQuestionSuccesses($question['iid']);
+
+ $row[9] = get_action_icon_for_question(
+ $actionIcon1,
+ $fromExercise,
+ $question['iid'],
+ $question['type'],
+ $question['question'],
+ $selected_course,
+ $courseCategoryId,
+ $exerciseLevel,
+ $answerType,
+ $session_id,
+ $question['exerciseId'],
+ $objExercise
+ ).' '.
+ get_action_icon_for_question(
+ $actionIcon2,
+ $fromExercise,
+ $question['iid'],
+ $question['type'],
+ $question['question'],
+ $selected_course,
+ $courseCategoryId,
+ $exerciseLevel,
+ $answerType,
+ $session_id,
+ $question['exerciseId'],
+ $objExercise
+ );
+ $QList[] = $row;
+ }
+ if(!empty($column)) {
+ $asc = $direction =='ASC' ? 1 : 0;
+ $QList = sortByCol($QList, $column, $asc);
+ }
+ $x = 0;
+ if (!empty($length)) {
+ $questionList = [];
+ $end = $start+$length;
+ foreach($QList as $question) {
+ if ($x>=$start && $x<$end) {
+ $questionList[] = $question;
+ }
+ $x++;
+ }
+ return $questionList;
+ }
+ return $QList;
+
+}
+/**
+ * get number total of questions
+ */
+function getNbrQuestions() {
+ $res = getQuestions(
+ 0,
+ 0,
+ 0,
+ null,
+ true
+ );
+ return $res;
+}
+
+
+$nbrQuestions = getQuestions(
+ 0,
+ 0,
+ 0,
+ null,
+ true
+ );
+
+
+$length = api_get_configuration_value('question_pagination_length');
+if (empty($length)) {
+ $length = 20;
+}
+
+$start = ($page - 1) * $length;
+
+$paginator = new Paginator();
+$pagination = $paginator->paginate([]);
+$pagination->setTotalItemCount($nbrQuestions);
+$pagination->setItemNumberPerPage($length);
+$pagination->setCurrentPageNumber($page);
+
+$pagination->renderer = function ($data) use ($url) {
+ $render = '';
+ if ($data['pageCount'] > 1) {
+ $render = '';
+ }
+
+ return $render;
+};
+
+// build the line of the array to display questions
+// Actions are different if you launch the question_pool page
+// They are different too if you have displayed questions from your course
+// Or from another course you are the admin(or session admin)
+// from a test or not
+/*
++--------------------------------------------+--------------------------------------------+
+| NOT IN A TEST | IN A TEST |
++----------------------+---------------------+---------------------+----------------------+
+|IN THE COURSE (*) "x | NOT IN THE COURSE o | IN THE COURSE + | NOT IN THE COURSE o |
++----------------------+---------------------+---------------------+----------------------+
+|Edit the question | Do nothing | Add question to test|Clone question in test|
+|Delete the question | | | |
+|(true delete) | | | |
++----------------------+---------------------+---------------------+----------------------+
+(*) this is the only way to delete or modify orphan questions
+*/
+
+if ($fromExercise <= 0) {
+ // NOT IN A TEST - NOT IN THE COURSE
+ $actionLabel = get_lang('Reuse');
+ $actionIcon1 = get_lang('MustBeInATest');
+ $actionIcon2 = '';
+ // We are not in this course, to messy if we link to the question in another course
+ $questionTagA = 0;
+ if ($selected_course == api_get_course_int_id()) {
+ // NOT IN A TEST - IN THE COURSE
+ $actionLabel = get_lang('Modify');
+ $actionIcon1 = 'edit';
+ $actionIcon2 = 'delete';
+ // We are in the course, question title can be a link to the question edit page
+ $questionTagA = 1;
+ }
+} else {
+ // IN A TEST - NOT IN THE COURSE
+ $actionLabel = get_lang('ReUseACopyInCurrentTest');
+ $actionIcon1 = 'clone';
+ $actionIcon2 = '';
+ $questionTagA = 0;
+
+ if ($selected_course == api_get_course_int_id()) {
+ // IN A TEST - IN THE COURSE
+ $actionLabel = get_lang('Reuse');
+ $actionIcon1 = 'add';
+ $actionIcon2 = '';
+ $questionTagA = 1;
+ } elseif (true === api_get_configuration_value('quiz_question_allow_inter_course_linking')) {
+ $actionIcon2 = 'add';
+ }
+}
+
+
+$headers = [];
+
+//echo $pagination;
+
+$tableId = 'question_pool_id';
+echo '';
+
+$html = ''; //toolbar
+
+echo $html;
+
+Display::display_footer();
+
+/**
+ * Put the menu entry for level and type to default "Choice"
+ * It is useful if you change the exercise, you need to reset the other menus.
+ *
+ * @author hubert.borderiou 13-10-2011
+ */
+function reset_menu_lvl_type()
+{
+ global $exerciseLevel, $answerType;
+ $answerType = -1;
+ $exerciseLevel = -1;
+}
+
+/**
+ * Put the menu entry for exercise and level and type to default "Choice"
+ * It is useful if you change the course, you need to reset the other menus.
+ *
+ * @author hubert.borderiou 13-10-2011
+ */
+function reset_menu_exo_lvl_type()
+{
+ global $exerciseId, $courseCategoryId;
+ reset_menu_lvl_type();
+ $exerciseId = 0;
+ $courseCategoryId = 0;
+}
+
+/**
+ * return the link to admin question, if needed.
+ *
+ * @param int $in_addA
+ * @param int $fromExercise
+ * @param int $questionId
+ * @param int $questionType
+ * @param string $questionName
+ * @param int $sessionId
+ * @param int $exerciseId
+ *
+ * @return string
+ *
+ * @author hubert.borderiou
+ */
+function getLinkForQuestion(
+ $in_addA,
+ $fromExercise,
+ $questionId,
+ $questionType,
+ $questionName,
+ $sessionId,
+ $exerciseId
+) {
+ $result = $questionName;
+ if ($in_addA) {
+ $sessionIcon = '';
+ if (!empty($sessionId) && -1 != $sessionId) {
+ $sessionIcon = ' '.Display::return_icon('star.png', get_lang('Session'));
+ }
+ $exerciseId = (int) $exerciseId;
+ $questionId = (int) $questionId;
+ $questionType = (int) $questionType;
+ $fromExercise = (int) $fromExercise;
+ $qOptions = getQuestionAnswers($questionId);
+ $result = Display::url(
+ $questionName.$sessionIcon,
+ 'admin.php?'.api_get_cidreq().
+ "&exerciseId=$exerciseId&editQuestion=$questionId&type=$questionType&fromExercise=$fromExercise",
+ ['title' => $qOptions]
+ );
+ }
+
+ return $result;
+}
+
+/**
+ Return the html code for delete, add, clone, edit a question
+ in_action = the code of the action triggered by the button
+ from_exercise = the id of the current exercise from which we click on question pool
+ in_questionid = the id of the current question
+ in_questiontype = the code of the type of the current question
+ in_questionname = the name of the question
+ in_selected_course = the if of the course chosen in the FILTERING MENU
+ in_courseCategoryId = the id of the category chosen in the FILTERING MENU
+ in_exerciseLevel = the level of the exercise chosen in the FILTERING MENU
+ in_answerType = the code of the type of the question chosen in the FILTERING MENU
+ in_session_id = the id of the session_id chosen in the FILTERING MENU
+ in_exercise_id = the id of the exercise chosen in the FILTERING MENU
+*/
+function get_action_icon_for_question(
+ $in_action,
+ $from_exercise,
+ $in_questionid,
+ $in_questiontype,
+ $in_questionname,
+ $in_selected_course,
+ $in_courseCategoryId,
+ $in_exerciseLevel,
+ $in_answerType,
+ $in_session_id,
+ $in_exercise_id,
+ Exercise $myObjEx
+) {
+ $limitTeacherAccess = api_get_configuration_value('limit_exercise_teacher_access');
+ $getParams = "&selected_course=$in_selected_course&courseCategoryId=$in_courseCategoryId&exerciseId=$in_exercise_id&exerciseLevel=$in_exerciseLevel&answerType=$in_answerType&session_id=$in_session_id";
+ $res = '';
+ switch ($in_action) {
+ case 'delete':
+ if ($limitTeacherAccess && !api_is_platform_admin()) {
+ break;
+ }
+
+ if (isQuestionInActiveQuiz($in_questionid)) {
+ $res = Display::return_icon('delete_na.png', get_lang('ThisQuestionExistsInAnotherExercisesWarning'));
+ } else {
+ $res = "";
+ $res .= Display::return_icon('delete.png', get_lang('Delete'));
+ $res .= "";
+ }
+
+ break;
+ case 'edit':
+ $res = getLinkForQuestion(
+ 1,
+ $from_exercise,
+ $in_questionid,
+ $in_questiontype,
+ Display::return_icon('edit.png', get_lang('Modify')),
+ $in_session_id,
+ $in_exercise_id
+ );
+ break;
+ case 'add':
+ $res = '-';
+ if (!$myObjEx->hasQuestion($in_questionid)) {
+ $res = "";
+ $res .= Display::return_icon('view_more_stats.gif', get_lang('InsertALinkToThisQuestionInTheExercise'));
+ $res .= '';
+ }
+ break;
+ case 'clone':
+ $url = api_get_self().'?'.api_get_cidreq().$getParams.
+ "&question_copy=$in_questionid&course_id=$in_selected_course&fromExercise=$from_exercise";
+ $res = Display::url(
+ Display::return_icon('cd.png', get_lang('ReUseACopyInCurrentTest')),
+ $url
+ );
+ break;
+ default:
+ $res = $in_action;
+ break;
+ }
+
+ return $res;
+}
+
+/**
+ * @param int $questionId
+ *
+ * @return bool
+ */
+function isQuestionInActiveQuiz($questionId)
+{
+ $tblQuizRelQuestion = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
+ $tblQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
+
+ $questionId = (int) $questionId;
+
+ if (empty($questionId)) {
+ return false;
+ }
+
+ $result = Database::fetch_assoc(
+ Database::query(
+ "SELECT COUNT(qq.question_id) count
+ FROM $tblQuizRelQuestion qq
+ INNER JOIN $tblQuiz ex
+ ON qq.exercice_id = ex.iid
+ WHERE
+ ex.active = 1 AND
+ qq.question_id = $questionId"
+ )
+ );
+
+ return $result['count'] > 0;
+}
+
+/**
+ * Return the icon for the question type.
+ *
+ * @author hubert.borderiou 13-10-2011
+ */
+function get_question_type_for_question($in_selectedcourse, $in_questionid)
+{
+ $courseInfo = api_get_course_info_by_id($in_selectedcourse);
+ $question = Question::read($in_questionid, $courseInfo);
+ $questionType = null;
+ if (!empty($question)) {
+ $typeImg = $question->getTypePicture();
+ $typeExpl = $question->getExplanation();
+
+ $questionType = Display::tag('div', Display::return_icon($typeImg, $typeExpl, [], 32), []);
+ }
+
+ return $questionType;
+}
diff --git a/plugin/extendedquestionpool/README.md b/plugin/extendedquestionpool/README.md
new file mode 100644
index 00000000000..fc4e05bd90d
--- /dev/null
+++ b/plugin/extendedquestionpool/README.md
@@ -0,0 +1,26 @@
+Extended Question Pool plugin
+==============================
+Este plugin amplía las funcionalidades del banco de preguntas.
+
+**Instrucciones de puesta en funcionamiento**
+
+- Habilitar el plugin en la administración de Chamilo.
+- Configurar las opciones deseadas
+
+**Accesos a la herramienta**
+
+- Desde plugins, para configurar las opciones
+
+**Funcionalidades que incluye**
+
+- Mostrar el nº de preguntas total del ejercicio
+- Campos extra en las preguntas para ampliar la clasificación
+- Detectar preguntas ya utilizadas
+- Asignar puntuaciones de preguntas de manera masiva
+- Contador aciertos y fallos por preguntas
+- Ordenar las preguntas por nombre, categoría, dificultad, nº de apariciones, aciertos, fallos
+
+
+Credits
+-------
+Contributed by [Nosolored](https://www.nosolored.com/).
\ No newline at end of file
diff --git a/plugin/extendedquestionpool/config.php b/plugin/extendedquestionpool/config.php
new file mode 100644
index 00000000000..94ca5e720db
--- /dev/null
+++ b/plugin/extendedquestionpool/config.php
@@ -0,0 +1,12 @@
+
+ */
+require_once __DIR__.'/../../main/inc/global.inc.php';
+require_once api_get_path(SYS_PLUGIN_PATH).'extendedquestionpool/src/extendedquestionpool_plugin.class.php';
\ No newline at end of file
diff --git a/plugin/extendedquestionpool/database.php b/plugin/extendedquestionpool/database.php
new file mode 100644
index 00000000000..e2c02180075
--- /dev/null
+++ b/plugin/extendedquestionpool/database.php
@@ -0,0 +1,200 @@
+getConnection();
+$platform = $connection->getDatabasePlatform();
+$sm = $connection->getSchemaManager();
+
+$extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD);
+
+$categoryExtraField = Database::select(
+ "*",
+ $extraFieldTable,
+ [
+ 'where' => ['variable = ?' => 'additional_question_category'],
+ ],
+ 'first'
+);
+
+if (!$categoryExtraField) {
+ Database::insert(
+ $extraFieldTable,
+ [
+ 'extra_field_type' => 4,
+ 'field_type' => 1,
+ 'variable' => 'additional_question_category',
+ 'display_text' => 'Categoría adicional',
+ 'default_value' => '',
+ 'field_order' => 0,
+ 'visible_to_self' => 1,
+ 'changeable' => 1,
+ 'filter' => 1,
+ 'created_at' => api_get_utc_datetime(),
+ ]
+ );
+} else {
+ $query = "UPDATE $extraFieldTable
+ SET visible_to_self = 1,
+ visible_to_others = 1,
+ changeable = 1,
+ filter = 1
+ WHERE variable = 'additional_question_category'";
+ Database::query($query);
+}
+
+$categoryExtraField = Database::select(
+ "*",
+ $extraFieldTable,
+ [
+ 'where' => ['variable = ?' => 'question_data1'],
+ ],
+ 'first'
+);
+
+if (!$categoryExtraField) {
+ Database::insert(
+ $extraFieldTable,
+ [
+ 'extra_field_type' => 4,
+ 'field_type' => 1,
+ 'variable' => 'question_data1',
+ 'display_text' => 'Campo 1',
+ 'default_value' => '',
+ 'field_order' => 0,
+ 'visible_to_self' => 1,
+ 'changeable' => 1,
+ 'filter' => 1,
+ 'created_at' => api_get_utc_datetime(),
+ ]
+ );
+} else {
+ $query = "UPDATE $extraFieldTable
+ SET visible_to_self = 1,
+ visible_to_others = 1,
+ changeable = 1,
+ filter = 1
+ WHERE variable = 'question_data1'";
+ Database::query($query);
+}
+
+$categoryExtraField = Database::select(
+ "*",
+ $extraFieldTable,
+ [
+ 'where' => ['variable = ?' => 'question_data2'],
+ ],
+ 'first'
+);
+
+if (!$categoryExtraField) {
+ Database::insert(
+ $extraFieldTable,
+ [
+ 'extra_field_type' => 4,
+ 'field_type' => 1,
+ 'variable' => 'question_data2',
+ 'display_text' => 'Campo 2',
+ 'default_value' => '',
+ 'field_order' => 0,
+ 'visible_to_self' => 1,
+ 'changeable' => 1,
+ 'filter' => 1,
+ 'created_at' => api_get_utc_datetime(),
+ ]
+ );
+} else {
+ $query = "UPDATE $extraFieldTable
+ SET visible_to_self = 1,
+ visible_to_others = 1,
+ changeable = 1,
+ filter = 1
+ WHERE variable = 'question_data2'";
+ Database::query($query);
+}
+
+$categoryExtraField = Database::select(
+ "*",
+ $extraFieldTable,
+ [
+ 'where' => ['variable = ?' => 'question_data3'],
+ ],
+ 'first'
+);
+
+if (!$categoryExtraField) {
+ Database::insert(
+ $extraFieldTable,
+ [
+ 'extra_field_type' => 4,
+ 'field_type' => 1,
+ 'variable' => 'question_data3',
+ 'display_text' => 'Campo 3',
+ 'default_value' => '',
+ 'field_order' => 0,
+ 'visible_to_self' => 1,
+ 'changeable' => 1,
+ 'filter' => 1,
+ 'created_at' => api_get_utc_datetime(),
+ ]
+ );
+} else {
+ $query = "UPDATE $extraFieldTable
+ SET visible_to_self = 1,
+ visible_to_others = 1,
+ changeable = 1,
+ filter = 1
+ WHERE variable = 'question_data3'";
+ Database::query($query);
+}
+
+$categoryExtraField = Database::select(
+ "*",
+ $extraFieldTable,
+ [
+ 'where' => ['variable = ?' => 'question_extra_info'],
+ ],
+ 'first'
+);
+
+if (!$categoryExtraField) {
+ Database::insert(
+ $extraFieldTable,
+ [
+ 'extra_field_type' => 4,
+ 'field_type' => 2,
+ 'variable' => 'question_extra_info',
+ 'display_text' => 'Información adicional',
+ 'default_value' => '',
+ 'field_order' => 0,
+ 'visible_to_self' => 1,
+ 'changeable' => 1,
+ 'filter' => 1,
+ 'created_at' => api_get_utc_datetime(),
+ ]
+ );
+} else {
+ $query = "UPDATE $extraFieldTable
+ SET visible_to_self = 1,
+ visible_to_others = 1,
+ changeable = 1,
+ filter = 1
+ WHERE variable = 'question_extra_info'";
+ Database::query($query);
+}
\ No newline at end of file
diff --git a/plugin/extendedquestionpool/index.php b/plugin/extendedquestionpool/index.php
new file mode 100644
index 00000000000..e70cd86d150
--- /dev/null
+++ b/plugin/extendedquestionpool/index.php
@@ -0,0 +1,3 @@
+install();
diff --git a/plugin/extendedquestionpool/lang/english.php b/plugin/extendedquestionpool/lang/english.php
new file mode 100644
index 00000000000..c15c5e79c41
--- /dev/null
+++ b/plugin/extendedquestionpool/lang/english.php
@@ -0,0 +1,12 @@
+Plugins).
+ *
+ * @package chamilo.plugin.extendedquestionpool
+ */
+
+/**
+ * Plugin details (must be present).
+ */
+require_once __DIR__.'/config.php';
+$plugin_info = ExtendedQuestionPoolPlugin::create()->get_info();
diff --git a/plugin/extendedquestionpool/src/extendedquestionpool.lib.php b/plugin/extendedquestionpool/src/extendedquestionpool.lib.php
new file mode 100644
index 00000000000..93c9090dab0
--- /dev/null
+++ b/plugin/extendedquestionpool/src/extendedquestionpool.lib.php
@@ -0,0 +1,126 @@
+
+ */
+
+/**
+ * Get the number of occurrences of a question in quizzes
+ * @param int $questionId
+ * @return int
+ */
+function getQuestionOcurrences($questionId) {
+ $questionQuizTable = 'c_quiz_rel_question';
+ if (empty($questionId)) {
+ return false;
+ }
+ $query = "SELECT count(iid) as nbr FROM $questionQuizTable
+ WHERE question_id = '$questionId'";
+ $q = Database::query($query);
+ $row = Database::fetch_assoc($q);
+ $res = $row['nbr'];
+
+ return $res;
+}
+/**
+ * Get the number of failures answers of a question in quizzes
+ * @param int $questionId
+ * @param int $c_id course id
+ * @return int
+ */
+function getQuestionFailures($questionId, $c_id = 0)
+{
+ $attemptTable = 'track_e_attempt';
+ if (empty($questionId)) {
+ return false;
+ }
+ $query = "SELECT count(id) as nbr FROM $attemptTable
+ WHERE question_id = '$questionId'
+ AND marks <= 0";
+ if (!empty($c_id)) {
+ $query .= " AND c_id='$c_id'";
+ }
+ $q = Database::query($query);
+ $row = Database::fetch_assoc($q);
+ $res = $row['nbr'];
+
+ return $res;
+}
+/**
+ * Get the number of successes answers of a question in quizzes
+ * @param int $questionId
+ * @param int $c_id course id
+ * @return int
+ */
+function getQuestionSuccesses($questionId, $c_id = 0)
+{
+ $attemptTable = 'track_e_attempt';
+ if (empty($questionId)) {
+ return false;
+ }
+ $query = "SELECT count(id) as nbr FROM $attemptTable
+ WHERE question_id = '$questionId'
+ AND marks > 0";
+ if (!empty($c_id)) {
+ $query .= " AND c_id='$c_id'";
+ }
+ $q = Database::query($query);
+ $row = Database::fetch_assoc($q);
+ $res = $row['nbr'];
+
+ return $res;
+}
+/**
+ * Sort array by column
+ * @param array $data
+ * @param string $col
+ * @param bool $asc
+ * @return array
+ */
+function sortByCol(array $data, string $col, bool $asc = true): array {
+ usort($data, function ($a, $b) use ($col, $asc) {
+ if (!isset($a[$col]) || !isset($b[$col])) {
+ return 0; // column doesn't exist, nothing to sort
+ }
+
+ if ($a[$col] == $b[$col]) {
+ return 0;
+ }
+
+ if ($asc) {
+ return (strip_tags($a[$col]) < strip_tags($b[$col])) ? -1 : 1;
+ } else {
+ return (strip_tags($a[$col]) > strip_tags($b[$col])) ? -1 : 1;
+ }
+ });
+
+ return $data;
+}
+/**
+ * Get answer options for a question
+ * @param int $questionId
+ * @return string
+ */
+function getQuestionAnswers(int $questionId) {
+ $table = Database::get_course_table(TABLE_QUIZ_ANSWER);
+ if (empty($questionId)) {
+ return false;
+ }
+ $res = '';
+ $query = "SELECT * FROM $table WHERE question_id=$questionId ORDER BY position";
+ $result = Database::query($query);
+ while ($row = Database::fetch_assoc($result)) {
+ $c = '- ';
+ if ($row['correct'] == 1) {
+ $c = '# ';
+ }
+ $answer = html_entity_decode(strip_tags($row['answer']));
+ $res .= $c.$answer.chr(13).chr(10);
+ }
+ return $res;
+}
\ No newline at end of file
diff --git a/plugin/extendedquestionpool/src/extendedquestionpool_plugin.class.php b/plugin/extendedquestionpool/src/extendedquestionpool_plugin.class.php
new file mode 100644
index 00000000000..08817f99ac0
--- /dev/null
+++ b/plugin/extendedquestionpool/src/extendedquestionpool_plugin.class.php
@@ -0,0 +1,108 @@
+
+ */
+class ExtendedQuestionPoolPlugin extends Plugin
+{
+ //public $isCoursePlugin = true;
+
+ /**
+ * Constructor.
+ */
+ protected function __construct()
+ {
+ parent::__construct(
+ '1.0',
+ 'NoSoloRed',
+ [
+ 'enable_plugin' => 'boolean',
+ 'correct_score' => 'text',
+ 'error_score' => 'text',
+ ]
+ );
+
+ $this->isAdminPlugin = true;
+ }
+
+ /**
+ * Instance the plugin.
+ *
+ * @staticvar null $result
+ *
+ * @return ExtendedQuestionPool
+ */
+ public static function create()
+ {
+ static $result = null;
+
+ return $result ? $result : $result = new self();
+ }
+
+ /**
+ * This method creates the tables required to this plugin.
+ */
+ public function install()
+ {
+ require_once api_get_path(SYS_PLUGIN_PATH).'extendedquestionpool/database.php';
+ }
+
+ /**
+ * This method drops the plugin tables.
+ */
+ public function uninstall()
+ {
+ // Deleting course settings.
+ $this->uninstall_course_fields_in_all_courses();
+ $extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD);
+ $query = "UPDATE $extraFieldTable
+ SET visible_to_self = 0,
+ visible_to_others = 0,
+ changeable = 0,
+ filter = 0
+ WHERE variable = 'additional_question_category'";
+ Database::query($query);
+ $query = "UPDATE $extraFieldTable
+ SET visible_to_self = 0,
+ visible_to_others = 0,
+ changeable = 0,
+ filter = 0
+ WHERE variable = 'question_data1'";
+ Database::query($query);
+ $query = "UPDATE $extraFieldTable
+ SET visible_to_self = 0,
+ visible_to_others = 0,
+ changeable = 0,
+ filter = 0
+ WHERE variable = 'question_data2'";
+ Database::query($query);
+ $query = "UPDATE $extraFieldTable
+ SET visible_to_self = 0,
+ visible_to_others = 0,
+ changeable = 0,
+ filter = 0
+ WHERE variable = 'question_data3'";
+ Database::query($query);
+ $query = "UPDATE $extraFieldTable
+ SET visible_to_self = 0,
+ visible_to_others = 0,
+ changeable = 0,
+ filter = 0
+ WHERE variable = 'question_extra_info'";
+ Database::query($query);
+ $this->manageTab(false);
+ }
+
+ /**
+ * This method updates plugin.
+ */
+ public function update()
+ {
+ //update actions
+ }
+}
\ No newline at end of file
diff --git a/plugin/extendedquestionpool/src/index.php b/plugin/extendedquestionpool/src/index.php
new file mode 100644
index 00000000000..ddfd7d17566
--- /dev/null
+++ b/plugin/extendedquestionpool/src/index.php
@@ -0,0 +1,3 @@
+get('enable_plugin_extendedquestionpool') == 'true';
+
+if ($enable) {
+ if (api_is_platform_admin() || api_is_teacher()) {
+ $url = 'src/index.php?';
+ $url .= (isset($_GET['cidReq']) ? api_get_cidreq() : 'default=1');
+ header('Location: '.$url);
+ exit;
+ } else {
+ $session = api_get_session_entity(api_get_session_id());
+ $_course = api_get_course_info();
+ $webCoursePath = api_get_path(WEB_COURSE_PATH);
+ $url = $webCoursePath.$_course['path'].'/index.php'.($session ? '?id_session='.$session->getId() : '');
+
+ Display::addFlash(
+ Display::return_message($plugin->get_lang('OnlyAdminPlatform'))
+ );
+
+ header('Location: '.$url);
+ exit;
+ }
+} else {
+ api_not_allowed(true, $plugin->get_lang('ToolDisabled'));
+}
diff --git a/plugin/extendedquestionpool/uninstall.php b/plugin/extendedquestionpool/uninstall.php
new file mode 100644
index 00000000000..4cd189a3910
--- /dev/null
+++ b/plugin/extendedquestionpool/uninstall.php
@@ -0,0 +1,12 @@
+uninstall();
diff --git a/plugin/extendedquestionpool/update.php b/plugin/extendedquestionpool/update.php
new file mode 100644
index 00000000000..26c8d8b9935
--- /dev/null
+++ b/plugin/extendedquestionpool/update.php
@@ -0,0 +1,14 @@
+update();