diff --git a/main/css/base.css b/main/css/base.css index b3acc56433..6f956edae8 100755 --- a/main/css/base.css +++ b/main/css/base.css @@ -5820,3 +5820,41 @@ ul.holder li.bit-box{ border-radius: 50%; border: 5px solid rgba(0,0,30,0.8); } + +.question_options .exercise-unique-answer-image { + +} + +@media (min-width: 768px) { + .question_options .exercise-unique-answer-image:nth-child(2n-1) { + clear: both; + } +} + +@media (min-width: 992px) { + .question_options .exercise-unique-answer-image:nth-child(2n-1) { + clear: none; + } + + .question_options .exercise-unique-answer-image:nth-child(3n-2) { + clear: both; + } +} + +.question_options label > input + div.thumbnail { + border-color: transparent; + border-width: 5px; +} + +.question_options label > input:checked + div.thumbnail { + border: 5px solid red; +} + +.question_options div.thumbnail p { + margin: 0; +} + +.question_options div.thumbnail img { + height: auto !important; + max-width: 100%; +} diff --git a/main/exercice/UniqueAnswerImage.php b/main/exercice/UniqueAnswerImage.php new file mode 100644 index 0000000000..cbcc1d46c7 --- /dev/null +++ b/main/exercice/UniqueAnswerImage.php @@ -0,0 +1,360 @@ + + */ +class UniqueAnswerImage extends UniqueAnswer +{ + + static $typePicture = 'mcua.png'; + static $explanationLangVar = 'UniqueSelect'; + + public function __construct() + { + //this is highly important + parent::__construct(); + $this->type = UNIQUE_ANSWER_IMAGE; + $this->isContent = $this->getIsContent(); + } + + public function createAnswersForm($form) + { + $objExercise = $_SESSION['objExercise']; + + $editorConfig = array( + 'ToolbarSet' => 'UniqueAnswerImage', + 'Width' => '100%', + 'Height' => '125' + ); + + //this line defines how many questions by default appear when creating a choice question + // The previous default value was 2. See task #1759. + $numberAnswers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : 4; + $numberAnswers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0)); + + $feedbackTitle = ''; + + if ($objExercise->selectFeedbackType() == EXERCISE_FEEDBACK_TYPE_DIRECT) { + //Scenario + $commentTitle = '' . get_lang('Comment') . ''; + $feedbackTitle = '' . get_lang('Scenario') . ''; + } else { + $commentTitle = '' . get_lang('Comment') . ''; + } + + $html = ' + + + + + + ' . $commentTitle . ' + ' . $feedbackTitle . ' + + + + '; + + $form->addHeader(get_lang('Answers')); + $form->addHtml($html); + + $defaults = array(); + $correct = 0; + + if (!empty($this->id)) { + $answer = new Answer($this->id); + $answer->read(); + + if (count($answer->nbrAnswers) > 0 && !$form->isSubmitted()) { + $numberAnswers = $answer->nbrAnswers; + } + } + + $form->addElement('hidden', 'nb_answers'); + + //Feedback SELECT + $questionList = $objExercise->selectQuestionList(); + $selectQuestion = array(); + $selectQuestion[0] = get_lang('SelectTargetQuestion'); + + if (is_array($questionList)) { + foreach ($questionList as $key => $questionid) { + //To avoid warning messages + if (!is_numeric($questionid)) { + continue; + } + + $question = Question::read($questionid); + $selectQuestion[$questionid] = 'Q' . $key . ' :' . cut( + $question->selectTitle(), 20 + ); + } + } + + $selectQuestion[-1] = get_lang('ExitTest'); + + $list = new LearnpathList(api_get_user_id()); + $flatList = $list->get_flat_list(); + $selectLpId = array(); + $selectLpId[0] = get_lang('SelectTargetLP'); + + foreach ($flatList as $id => $details) { + $selectLpId[$id] = cut($details['lp_name'], 20); + } + + $tempScenario = array(); + + if ($numberAnswers < 1) { + $numberAnswers = 1; + Display::display_normal_message( + get_lang('YouHaveToCreateAtLeastOneAnswer') + ); + } + + for ($i = 1; $i <= $numberAnswers; ++$i) { + $form->addHtml(''); + if (isset($answer) && is_object($answer)) { + if ($answer->correct[$i]) { + $correct = $i; + } + + $defaults['answer[' . $i . ']'] = $answer->answer[$i]; + $defaults['comment[' . $i . ']'] = $answer->comment[$i]; + $defaults['weighting[' . $i . ']'] = float_format( + $answer->weighting[$i], 1 + ); + + $itemList = explode('@@', $answer->destination[$i]); + + $try = $itemList[0]; + $lp = $itemList[1]; + $listDestination = $itemList[2]; + $url = $itemList[3]; + + $try = 0; + + if ($try != 0) { + $tryResult = 1; + } + + $urlResult = ''; + + if ($url != 0) { + $urlResult = $url; + } + + $tempScenario['url' . $i] = $urlResult; + $tempScenario['try' . $i] = $tryResult; + $tempScenario['lp' . $i] = $lp; + $tempScenario['destination' . $i] = $listDestination; + } else { + $defaults['answer[1]'] = get_lang('DefaultUniqueAnswer1'); + $defaults['weighting[1]'] = 10; + $defaults['answer[2]'] = get_lang('DefaultUniqueAnswer2'); + $defaults['weighting[2]'] = 0; + + $tempScenario['destination' . $i] = array('0'); + $tempScenario['lp' . $i] = array('0'); + } + + $defaults['scenario'] = $tempScenario; + + $renderer = $form->defaultRenderer(); + + $renderer->setElementTemplate( + '', + 'correct' + ); + $renderer->setElementTemplate( + '', + 'counter[' . $i . ']' + ); + $renderer->setElementTemplate( + '', + 'answer[' . $i . ']' + ); + $renderer->setElementTemplate( + '', + 'comment[' . $i . ']' + ); + $renderer->setElementTemplate( + '', + 'weighting[' . $i . ']' + ); + + $answerNumber = $form->addElement('text', 'counter[' . $i . ']', null, ' value = "' . $i . '"'); + $answerNumber->freeze(); + + $form->addElement('radio', 'correct', null, null, $i, 'class="checkbox"'); + $form->addHtmlEditor('answer[' . $i . ']', null, null, true, $editorConfig); + + $form->addRule('answer[' . $i . ']', get_lang('ThisFieldIsRequired'), 'required'); + + if ($objExercise->selectFeedbackType() == EXERCISE_FEEDBACK_TYPE_DIRECT) { + $form->addHtmlEditor('comment[' . $i . ']', null, null, false, $editorConfig); + // Direct feedback + //Adding extra feedback fields + $group = array(); + $group['try' . $i] = $form->createElement('checkbox', 'try' . $i, null, get_lang('TryAgain')); + $group['lp' . $i] = $form->createElement( + 'select', + 'lp' . $i, + get_lang('SeeTheory') . ': ', + $selectLpId + ); + $group['destination' . $i] = $form->createElement( + 'select', + 'destination' . $i, + get_lang('GoToQuestion') . ': ', + $selectQuestion + ); + $group['url' . $i] = $form->createElement( + 'text', 'url' . $i, + get_lang('Other') . ': ', + array( + 'class' => 'col-md-2', + 'placeholder' => get_lang('Other') + ) + ); + $form->addGroup($group, 'scenario'); + + $renderer->setElementTemplate( + ''); + } + + $form->addHtml(''); + $form->addHtml('
' . get_lang('Number') . '' . get_lang('True') . '' . get_lang('Answer') . '' . get_lang('Weighting') . '
{error}
{element}
{error}
{element}
{error}
{element}
{error}
{element}
{error}
{element}
{error}
{element}', + 'scenario' + ); + } else { + $form->addHtmlEditor('comment[' . $i . ']', null, null, false, $editorConfig); + } + $form->addText('weighting[' . $i . ']', null, null, array('class' => "col-md-1", 'value' => '0')); + $form->addHtml('
'); + + global $text, $class; + + $buttonGroup = []; + + if ($objExercise->edit_exercise_in_lp == true) { + //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->addButtonSave($text, 'submitQuestion', true); + + $form->addGroup($buttonGroup); + } + + // We check the first radio button to be sure a radio button will be check + if ($correct == 0) { + $correct = 1; + } + + $defaults['correct'] = $correct; + + if (!empty($this->id)) { + $form->setDefaults($defaults); + } else { + if ($this->isContent == 1) { + // Default sample content. + $form->setDefaults($defaults); + } else { + $form->setDefaults(array('correct' => 1)); + } + } + + $form->setConstants(array('nb_answers' => $numberAnswers)); + } + + public function processAnswersCreation($form) + { + $questionWeighting = $nbrGoodAnswers = 0; + $correct = $form->getSubmitValue('correct'); + $objAnswer = new Answer($this->id); + $numberAnswers = $form->getSubmitValue('nb_answers'); + + for ($i = 1; $i <= $numberAnswers; $i++) { + $answer = trim($form->getSubmitValue('answer[' . $i . ']')); + $comment = trim($form->getSubmitValue('comment[' . $i . ']')); + $weighting = trim($form->getSubmitValue('weighting[' . $i . ']')); + + $scenario = $form->getSubmitValue('scenario'); + + //$listDestination = $form -> getSubmitValue('destination'.$i); + //$destinationStr = $form -> getSubmitValue('destination'.$i); + + $try = $scenario['try' . $i]; + $lp = $scenario['lp' . $i]; + $destination = $scenario['destination' . $i]; + $url = trim($scenario['url' . $i]); + + /* + How we are going to parse the destination value + + here we parse the destination value which is a string + 1@@3@@2;4;4;@@http://www.chamilo.org + + where: try_again@@lp_id@@selected_questions@@url + + try_again = is 1 || 0 + lp_id = id of a learning path (0 if dont select) + selected_questions= ids of questions + url= an url + + $destinationStr=''; + foreach ($listDestination as $destination_id) + { + $destinationStr.=$destination_id.';'; + } */ + + $goodAnswer = ($correct == $i) ? true : false; + + if ($goodAnswer) { + $nbrGoodAnswers++; + $weighting = abs($weighting); + + if ($weighting > 0) { + $questionWeighting += $weighting; + } + } + + if (empty($try)) { + $try = 0; + } + + if (empty($lp)) { + $lp = 0; + } + + if (empty($destination)) { + $destination = 0; + } + + if ($url == '') { + $url = 0; + } + + //1@@1;2;@@2;4;4;@@http://www.chamilo.org + $dest = $try . '@@' . $lp . '@@' . $destination . '@@' . $url; + + $objAnswer->createAnswer($answer, $goodAnswer, $comment, $weighting, $i, null, null, $dest); + } + + // saves the answers into the data base + $objAnswer->save(); + + // sets the total weighting of the question + $this->updateWeighting($questionWeighting); + $this->save(); + } + + public function return_header($feedback_type = null, $counter = null, $score = null) + { + return parent::return_header($feedback_type, $counter, $score); + } + +} diff --git a/main/exercice/exercise.class.php b/main/exercice/exercise.class.php index 5fb5c680e0..3456c4d1e9 100755 --- a/main/exercice/exercise.class.php +++ b/main/exercice/exercise.class.php @@ -2277,6 +2277,7 @@ class Exercise switch ($answerType) { // for unique answer case UNIQUE_ANSWER: + case UNIQUE_ANSWER_IMAGE: case UNIQUE_ANSWER_NO_OPTION: if ($from_database) { $sql = "SELECT answer FROM $TBL_TRACK_ATTEMPT @@ -2937,7 +2938,7 @@ class Exercise if ($debug) error_log('Showing questions $from '.$from); //display answers (if not matching type, or if the answer is correct) if ($answerType != MATCHING || $answerCorrect) { - if (in_array($answerType, array(UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION, MULTIPLE_ANSWER, MULTIPLE_ANSWER_COMBINATION, GLOBAL_MULTIPLE_ANSWER))) { + if (in_array($answerType, array(UNIQUE_ANSWER, UNIQUE_ANSWER_IMAGE, UNIQUE_ANSWER_NO_OPTION, MULTIPLE_ANSWER, MULTIPLE_ANSWER_COMBINATION, GLOBAL_MULTIPLE_ANSWER))) { //if ($origin != 'learnpath') { ExerciseShowFunctions::display_unique_or_multiple_answer($feedback_type, $answerType, $studentChoice, $answer, $answerComment, $answerCorrect, 0, 0, 0, $results_disabled); //} @@ -3132,6 +3133,7 @@ class Exercise switch ($answerType) { case UNIQUE_ANSWER : + case UNIQUE_ANSWER_IMAGE: case UNIQUE_ANSWER_NO_OPTION: case MULTIPLE_ANSWER : case GLOBAL_MULTIPLE_ANSWER : @@ -3604,8 +3606,7 @@ class Exercise } elseif ($answerType == ORAL_EXPRESSION) { $answer = $choice; Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0, $this->id, $nano); - - } elseif ($answerType == UNIQUE_ANSWER || $answerType == UNIQUE_ANSWER_NO_OPTION) { + } elseif (in_array($answerType, [UNIQUE_ANSWER, UNIQUE_ANSWER_IMAGE, UNIQUE_ANSWER_NO_OPTION])) { $answer = $choice; Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0, $this->id); // } elseif ($answerType == HOT_SPOT || $answerType == HOT_SPOT_DELINEATION) { diff --git a/main/exercice/exercise_submit.php b/main/exercice/exercise_submit.php index 3b1eabc577..3dd10fc16c 100755 --- a/main/exercice/exercise_submit.php +++ b/main/exercice/exercise_submit.php @@ -1128,6 +1128,7 @@ if (!empty($error)) { echo Display::div($exercise_actions, array('class'=>'exercise_actions')); } echo ''; + echo ''; } if ($origin != 'learnpath') { diff --git a/main/exercice/question.class.php b/main/exercice/question.class.php index 48e0613fe7..2c67c0a809 100755 --- a/main/exercice/question.class.php +++ b/main/exercice/question.class.php @@ -45,7 +45,8 @@ abstract class Question MULTIPLE_ANSWER_TRUE_FALSE => array('multiple_answer_true_false.class.php', 'MultipleAnswerTrueFalse'), MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE => array('multiple_answer_combination_true_false.class.php', 'MultipleAnswerCombinationTrueFalse'), GLOBAL_MULTIPLE_ANSWER => array('global_multiple_answer.class.php' , 'GlobalMultipleAnswer'), - CALCULATED_ANSWER => array('calculated_answer.class.php' , 'CalculatedAnswer') + CALCULATED_ANSWER => array('calculated_answer.class.php' , 'CalculatedAnswer'), + UNIQUE_ANSWER_IMAGE => ['UniqueAnswerImage.php', 'UniqueAnswerImage'] //MEDIA_QUESTION => array('media_question.class.php' , 'MediaQuestion') ); diff --git a/main/inc/lib/api.lib.php b/main/inc/lib/api.lib.php index 6f4d27ca0f..315422e11a 100644 --- a/main/inc/lib/api.lib.php +++ b/main/inc/lib/api.lib.php @@ -472,6 +472,7 @@ define('ORAL_EXPRESSION', 13); define('GLOBAL_MULTIPLE_ANSWER', 14); define('MEDIA_QUESTION', 15); define('CALCULATED_ANSWER', 16); +define('UNIQUE_ANSWER_IMAGE', 17); //Some alias used in the QTI exports define('MCUA', 1); diff --git a/main/inc/lib/exercise.lib.php b/main/inc/lib/exercise.lib.php index 28302cbe50..05ffa23407 100644 --- a/main/inc/lib/exercise.lib.php +++ b/main/inc/lib/exercise.lib.php @@ -82,7 +82,8 @@ class ExerciseLib return ''; } - echo '
'; + echo '
'; + // construction of the Answer object (also gets all answers details) $objAnswerTmp = new Answer($questionId); @@ -263,7 +264,7 @@ class ExerciseLib $attributes = array(); // Unique answer - if ($answerType == UNIQUE_ANSWER || $answerType == UNIQUE_ANSWER_NO_OPTION) { + if (in_array($answerType, [UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION, UNIQUE_ANSWER_IMAGE])) { $input_id = 'choice-' . $questionId . '-' . $answerId; if (isset($user_choice[0]['answer']) && $user_choice[0]['answer'] == $numAnswer) { $attributes = array( @@ -282,6 +283,11 @@ class ExerciseLib } } + if ($answerType == UNIQUE_ANSWER_IMAGE) { + $s .= '
'; + } + $answer = Security::remove_XSS($answer, STUDENT); $s .= Display::input( 'hidden', @@ -289,7 +295,15 @@ class ExerciseLib '0' ); - $answer_input = '
"; + } + if ($show_comment) { $s .= ''; $s .= $answer_input;