How to extend abstract "qtype_multichoice_renderer_base" class in theme folder

How to extend abstract "qtype_multichoice_renderer_base" class in theme folder

av Tejas Vanmali -
Antall svar: 3

Hi All,

I want to extend the abstract class "qtype_multichoice_renderer_base" located at "

/question/type/multichoice/renderer.php". I am using enlightlite theme and moodle version 3.6.  I added a new file in the theme/classes folder and tried extending the class but I am getting 'HTTP ERROR 500' error. 

Please suggest how can I resolve this error and can extend the class successfully.

Please find below the snippet of the code for your reference.

<?php

defined('MOODLE_INTERNAL') || die();

require_once($CFG->dirroot . '/question/type/multichoice/renderer.php');

abstract class theme_enlightlite_qtype_multichoice_renderer extends qtype_multichoice_renderer_base {

protected abstract function get_input_type();
protected abstract function get_input_name(question_attempt $qa, $value);
protected abstract function get_input_value($value);
protected abstract function get_input_id(question_attempt $qa, $value);
protected abstract function is_right(question_answer $ans);
protected abstract function prompt();


public function formulation_and_controls(question_attempt $qa, question_display_options $options) {

$question = $qa->get_question();
$response = $question->get_response($qa);

$inputname = $qa->get_qt_field_name('answer');
$inputattributes = array(
'type' => $this->get_input_type(),
'name' => $inputname,
);

if ($options->readonly) {
$inputattributes['disabled'] = 'disabled';
}

$radiobuttons = array();
$feedbackimg = array();
$feedback = array();
$classes = array();
foreach ($question->get_order($qa) as $value => $ansid) {
$ans = $question->answers[$ansid];
$inputattributes['name'] = $this->get_input_name($qa, $value);
$inputattributes['value'] = $this->get_input_value($value);
$inputattributes['id'] = $this->get_input_id($qa, $value);
$isselected = $question->is_choice_selected($response, $value);
if ($isselected) {
$inputattributes['checked'] = 'checked';
} else {
unset($inputattributes['checked']);
}
$hidden = '';
if (!$options->readonly && $this->get_input_type() == 'checkbox') {
$hidden = html_writer::empty_tag('input', array(
'type' => 'hidden',
'name' => $inputattributes['name'],
'value' => 0,
));
}
$radiobuttons[] = $hidden . html_writer::empty_tag('input', $inputattributes) .
html_writer::tag('label',
html_writer::span($this->number_in_style($value, $question->answernumbering), 'answernumber') .
$question->make_html_inline($question->format_text(
$ans->answer, $ans->answerformat,
$qa, 'question', 'answer', $ansid)),
array('for' => $inputattributes['id'], 'class' => 'm-l-1'));

// Param $options->suppresschoicefeedback is a hack specific to the
// oumultiresponse question type. It would be good to refactor to
// avoid refering to it here.
if ($options->feedback && empty($options->suppresschoicefeedback) &&
$isselected && trim($ans->feedback)) {
$feedback[] = html_writer::tag('div',
$question->make_html_inline($question->format_text(
$ans->feedback, $ans->feedbackformat,
$qa, 'question', 'answerfeedback', $ansid)),
array('class' => 'specificfeedback'));
} else {
$feedback[] = '';
}
$class = 'r' . ($value % 2);
if ($options->correctness && $isselected) {
$feedbackimg[] = $this->feedback_image($this->is_right($ans));
$class .= ' ' . $this->feedback_class($this->is_right($ans));
} else {
$feedbackimg[] = '';
}
$classes[] = $class;
}

$result = '';
$result .= html_writer::tag('div', $question->format_questiontext($qa),
array('class' => 'qtext hello'));

$result .= html_writer::start_tag('div', array('class' => 'ablock'));
$result .= html_writer::tag('div', $this->prompt(), array('class' => 'prompt'));

$result .= html_writer::start_tag('div', array('class' => 'answer'));
foreach ($radiobuttons as $key => $radio) {
$result .= html_writer::tag('div', $radio . ' ' . $feedbackimg[$key] . $feedback[$key],
array('class' => $classes[$key])) . "\n";
}
$result .= html_writer::end_tag('div'); // Answer.

$result .= html_writer::end_tag('div'); // Ablock.

if ($qa->get_state() == question_state::$invalid) {
$result .= html_writer::nonempty_tag('div',
$question->get_validation_error($qa->get_last_qt_data()),
array('class' => 'validationerror'));
}

return $result;
}



}
 
Gjennomsnittlig vurdering: -
Som svar til Tejas Vanmali

Re: How to extend abstract "qtype_multichoice_renderer_base" class in theme folder

av Gareth J Barnard -
Bilde av Core developers Bilde av Particularly helpful Moodlers Bilde av Plugin developers

Why is your implementation class 'abstract'?

Som svar til Gareth J Barnard

Re: How to extend abstract "qtype_multichoice_renderer_base" class in theme folder

av Tejas Vanmali -
The core class is defined as abstract, that is why I have defined my implementation class as 'abstract'. I want to override the 'formulation_and_controls' function. Please let me know what I am doing wrong. Your help is highly appreciated. I am a newbie. Thanks.

Please find below base core class code/syntax.

abstract class qtype_multichoice_renderer_base extends qtype_with_combined_feedback_renderer {
protected abstract function get_input_type();

protected abstract function get_input_name(question_attempt $qa, $value);

protected abstract function get_input_value($value);

protected abstract function get_input_id(question_attempt $qa, $value);

/**
* Whether a choice should be considered right, wrong or partially right.
* @param question_answer $ans representing one of the choices.
* @return fload 1.0, 0.0 or something in between, respectively.
*/
protected abstract function is_right(question_answer $ans);

protected abstract function prompt();

public function formulation_and_controls(question_attempt $qa,
question_display_options $options) {

$question = $qa->get_question();
$response = $question->get_response($qa);

$inputname = $qa->get_qt_field_name('answer');
$inputattributes = array(
'type' => $this->get_input_type(),
'name' => $inputname,
);

if ($options->readonly) {
$inputattributes['disabled'] = 'disabled';
}

$radiobuttons = array();
$feedbackimg = array();
$feedback = array();
$classes = array();
foreach ($question->get_order($qa) as $value => $ansid) {
$ans = $question->answers[$ansid];
$inputattributes['name'] = $this->get_input_name($qa, $value);
$inputattributes['value'] = $this->get_input_value($value);
$inputattributes['id'] = $this->get_input_id($qa, $value);
$isselected = $question->is_choice_selected($response, $value);
if ($isselected) {
$inputattributes['checked'] = 'checked';
} else {
unset($inputattributes['checked']);
}
$hidden = '';
if (!$options->readonly && $this->get_input_type() == 'checkbox') {
$hidden = html_writer::empty_tag('input', array(
'type' => 'hidden',
'name' => $inputattributes['name'],
'value' => 0,
));
}
$radiobuttons[] = $hidden . html_writer::empty_tag('input', $inputattributes) .
html_writer::tag('label',
html_writer::span($this->number_in_style($value, $question->answernumbering), 'answernumber') .
$question->make_html_inline($question->format_text(
$ans->answer, $ans->answerformat,
$qa, 'question', 'answer', $ansid)),
array('for' => $inputattributes['id'], 'class' => 'm-l-1'));

// Param $options->suppresschoicefeedback is a hack specific to the
// oumultiresponse question type. It would be good to refactor to
// avoid refering to it here.
if ($options->feedback && empty($options->suppresschoicefeedback) &&
$isselected && trim($ans->feedback)) {
$feedback[] = html_writer::tag('div',
$question->make_html_inline($question->format_text(
$ans->feedback, $ans->feedbackformat,
$qa, 'question', 'answerfeedback', $ansid)),
array('class' => 'specificfeedback'));
} else {
$feedback[] = '';
}
$class = 'r' . ($value % 2);
if ($options->correctness && $isselected) {
$feedbackimg[] = $this->feedback_image($this->is_right($ans));
$class .= ' ' . $this->feedback_class($this->is_right($ans));
} else {
$feedbackimg[] = '';
}
$classes[] = $class;
}

$result = '';
$result .= html_writer::tag('div', $question->format_questiontext($qa),
array('class' => 'qtext'));

$result .= html_writer::start_tag('div', array('class' => 'ablock'));
$result .= html_writer::tag('div', $this->prompt(), array('class' => 'prompt'));

$result .= html_writer::start_tag('div', array('class' => 'answer'));
foreach ($radiobuttons as $key => $radio) {
$result .= html_writer::tag('div', $radio . ' ' . $feedbackimg[$key] . $feedback[$key],
array('class' => $classes[$key])) . "\n";
}
$result .= html_writer::end_tag('div'); // Answer.

$result .= html_writer::end_tag('div'); // Ablock.

if ($qa->get_state() == question_state::$invalid) {
$result .= html_writer::nonempty_tag('div',
$question->get_validation_error($qa->get_last_qt_data()),
array('class' => 'validationerror'));
}

return $result;
}

protected function number_html($qnum) {
return $qnum . '. ';
}

/**
* @param int $num The number, starting at 0.
* @param string $style The style to render the number in. One of the
* options returned by {@link qtype_multichoice:;get_numbering_styles()}.
* @return string the number $num in the requested style.
*/
protected function number_in_style($num, $style) {
switch($style) {
case 'abc':
$number = chr(ord('a') + $num);
break;
case 'ABCD':
$number = chr(ord('A') + $num);
break;
case '123':
$number = $num + 1;
break;
case 'iii':
$number = question_utils::int_to_roman($num + 1);
break;
case 'IIII':
$number = strtoupper(question_utils::int_to_roman($num + 1));
break;
case 'none':
return '';
default:
return 'ERR';
}
return $this->number_html($number);
}

public function specific_feedback(question_attempt $qa) {
return $this->combined_feedback($qa);
}

/**
* Function returns string based on number of correct answers
* @param array $right An Array of correct responses to the current question
* @return string based on number of correct responses
*/
protected function correct_choices(array $right) {
// Return appropriate string for single/multiple correct answer(s).
if (count($right) == 1) {
return get_string('correctansweris', 'qtype_multichoice',
implode(', ', $right));
} else if (count($right) > 1) {
return get_string('correctanswersare', 'qtype_multichoice',
implode(', ', $right));
} else {
return "";
}
}
}


/**
* Subclass for generating the bits of output specific to multiple choice
* single questions.
*
* @copyright 2009 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qtype_multichoice_single_renderer extends qtype_multichoice_renderer_base {
protected function get_input_type() {
return 'radio';
}

protected function get_input_name(question_attempt $qa, $value) {
return $qa->get_qt_field_name('answer');
}

protected function get_input_value($value) {
return $value;
}

protected function get_input_id(question_attempt $qa, $value) {
return $qa->get_qt_field_name('answer' . $value);
}

protected function is_right(question_answer $ans) {
return $ans->fraction;
}

protected function prompt() {
return get_string('selectone', 'qtype_multichoice');
}

public function correct_response(question_attempt $qa) {
$question = $qa->get_question();

// Put all correct answers (100% grade) into $right.
$right = array();
foreach ($question->answers as $ansid => $ans) {
if (question_state::graded_state_for_fraction($ans->fraction) ==
question_state::$gradedright) {
$right[] = $question->make_html_inline($question->format_text($ans->answer, $ans->answerformat,
$qa, 'question', 'answer', $ansid));
}
}
return $this->correct_choices($right);
}
}

/**
* Subclass for generating the bits of output specific to multiple choice
* multi=select questions.
*
* @copyright 2009 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qtype_multichoice_multi_renderer extends qtype_multichoice_renderer_base {
protected function get_input_type() {
return 'checkbox';
}

protected function get_input_name(question_attempt $qa, $value) {
return $qa->get_qt_field_name('choice' . $value);
}

protected function get_input_value($value) {
return 1;
}

protected function get_input_id(question_attempt $qa, $value) {
return $this->get_input_name($qa, $value);
}

protected function is_right(question_answer $ans) {
if ($ans->fraction > 0) {
return 1;
} else {
return 0;
}
}

protected function prompt() {
return get_string('selectmulti', 'qtype_multichoice');
}

public function correct_response(question_attempt $qa) {
$question = $qa->get_question();

$right = array();
foreach ($question->answers as $ansid => $ans) {
if ($ans->fraction > 0) {
$right[] = $question->make_html_inline($question->format_text($ans->answer, $ans->answerformat,
$qa, 'question', 'answer', $ansid));
}
}
return $this->correct_choices($right);
}

protected function num_parts_correct(question_attempt $qa) {
if ($qa->get_question()->get_num_selected_choices($qa->get_last_qt_data()) > $qa->get_question()->get_num_correct_choices()) {
return get_string('toomanyselected', 'qtype_multichoice');
}

return parent::num_parts_correct($qa);
}
}
Som svar til Tejas Vanmali

Re: How to extend abstract "qtype_multichoice_renderer_base" class in theme folder

av Tejas Vanmali -
Thanks Gareth for all the help.

I got it working by adding the code given below. Now I am able to extend "qtype_multichoice_renderer_base" class located at " /question/type/multichoice/renderer.php".

Code:

require_once($CFG->dirroot.'/question/type/rendererbase.php');
require_once($CFG->dirroot . '/question/type/multichoice/renderer.php');

class theme_enlightlite_qtype_multichoice_single_renderer extends qtype_multichoice_single_renderer {
//Added code there and it's working
}

class theme_enlightlite_qtype_multichoice_multi_renderer extends qtype_multichoice_multi_renderer {

}