diff --git a/lib/questionlib.php b/lib/questionlib.php index a735f350ae1..21411cc1cf5 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -1428,8 +1428,32 @@ function question_hash($question) { /// FUNCTIONS THAT SIMPLY WRAP QUESTIONTYPE METHODS ////////////////////////////////// +/** + * Get the HTML that needs to be included in the head tag when the + * questions in $questionlist are printed in the gives states. + * + * @param array $questionlist a list of questionids of the questions what will appear on this page. + * @param array $questions an array of question objects, whose keys are question ids. + * Must contain all the questions in $questionlist + * @param array $states an array of question state objects, whose keys are question ids. + * Must contain the state of all the questions in $questionlist + * + * @return string some HTML code that can go inside the head tag. + */ +function get_html_head_contributions(&$questionlist, &$questions, &$states) { + global $QTYPES; - /** + $contributions = array(); + foreach ($questionlist as $questionid) { + $question = $questions[$questionid]; + $contributions = array_merge($contributions, + $QTYPES[$question->qtype]->get_html_head_contributions( + $question, $states[$questionid])); + } + return implode("\n", array_unique($contributions)); +} + +/** * Prints a question * * Simply calls the question type specific print_question() method. @@ -1446,10 +1470,10 @@ function print_question(&$question, &$state, $number, $cmoptions, $options=null) $cmoptions, $options); } /** -* Saves question options -* -* Simply calls the question type specific save_question_options() method. -*/ + * Saves question options + * + * Simply calls the question type specific save_question_options() method. + */ function save_question_options($question) { global $QTYPES; diff --git a/mod/quiz/attempt.php b/mod/quiz/attempt.php index 583f0762fc9..9b2030138d3 100644 --- a/mod/quiz/attempt.php +++ b/mod/quiz/attempt.php @@ -392,9 +392,11 @@ /// Print the quiz page //////////////////////////////////////////////////////// // Print the page header + $pagequestions = explode(',', $pagelist); + $headtags = get_html_head_contributions($pagequestions, $questions, $states); if (!empty($popup)) { define('MESSAGE_WINDOW', true); // This prevents the message window coming up - print_header($course->shortname.': '.format_string($quiz->name), '', '', '', '', false, '', '', false, ''); + print_header($course->shortname.': '.format_string($quiz->name), '', '', '', $headtags, false, '', '', false, ''); include('protect_js.php'); } else { $strupdatemodule = has_capability('moodle/course:manageactivities', $coursecontext) @@ -403,7 +405,7 @@ print_header_simple(format_string($quiz->name), "", "id\">$strquizzes -> id\">".format_string($quiz->name)." -> $strattemptnum", - "", "", true, $strupdatemodule); + "", $headtags, true, $strupdatemodule); } echo '
'; // for overlib @@ -475,7 +477,6 @@ // Add a hidden field with questionids echo '\n"; - $pagequestions = explode(',', $pagelist); $number = quiz_first_questionnumber($attempt->layout, $pagelist); foreach ($pagequestions as $i) { $options = quiz_get_renderoptions($quiz->review, $states[$i]); diff --git a/question/type/questiontype.php b/question/type/questiontype.php index 1bd6b57578e..61dbcc13e9e 100644 --- a/question/type/questiontype.php +++ b/question/type/questiontype.php @@ -98,6 +98,22 @@ class default_questiontype { return new $classname($submiturl, $question); } + /** + * @return string the full path of the folder this plugin's files live in. + */ + function plugin_dir() { + global $CFG; + return $CFG->dirroot . '/question/type/' . $this->name(); + } + + /** + * @return string the URL of the folder this plugin's files live in. + */ + function plugin_baseurl() { + global $CFG; + return $CFG->wwwroot . '/question/type/' . $this->name(); + } + /** * This method should be overriden if you want to include a special heading or some other * html on a question editing page besides the question editing form. @@ -542,32 +558,84 @@ class default_questiontype { } /** - * Prints the question including the number, grading details, content, - * feedback and interactions - * - * This function prints the question including the question number, - * grading details, content for the question, any feedback for the previously - * submitted responses and the interactions. The default implementation calls - * various other methods to print each of these parts and most question types - * will just override those methods. - * @param object $question The question to be rendered. Question type - * specific information is included. The - * maximum possible grade is in ->maxgrade. The name - * prefix for any named elements is in ->name_prefix. - * @param object $state The state to render the question in. The grading - * information is in ->grade, ->raw_grade and - * ->penalty. The current responses are in - * ->responses. This is an associative array (or the - * empty string or null in the case of no responses - * submitted). The last graded state is in - * ->last_graded (hence the most recently graded - * responses are in ->last_graded->responses). The - * question type specific information is also - * included. - * @param integer $number The number for this question. - * @param object $cmoptions - * @param object $options An object describing the rendering options. - */ + * If this question type requires extra CSS or JavaScript to function, + * then this method will return an array of tags that reference + * those stylesheets. This function will also call require_js() + * from ajaxlib.php, to get any necessary JavaScript linked in too. + * + * The two parameters match the first two parameters of print_question. + * + * @param object $question The question object. + * @param object $state The state object. + * + * @return an array of bits of HTML to add to the head of pages where + * this question is print_question-ed in the body. The array should use + * integer array keys, which have no significance. + */ + function get_html_head_contributions(&$question, &$state) { + // By default, we link to any of the files styles.css, styles.php, + // script.js or script.php that exist in the plugin folder. + // Core question types should not use this mechanism. Their styles + // should be included in the standard theme. + + // We only do this once + // for this question type, no matter how often this method is called. + static $already_done = false; + if ($already_done) { + return array(); + } + $already_done = true; + + $plugindir = $this->plugin_dir(); + $baseurl = $this->plugin_baseurl(); + $stylesheets = array(); + if (file_exists($plugindir . '/styles.css')) { + $stylesheets[] = 'styles.css'; + } + if (file_exists($plugindir . '/styles.php')) { + $stylesheets[] = 'styles.php'; + } + if (file_exists($plugindir . '/script.js')) { + require_js($baseurl . '/script.js'); + } + if (file_exists($plugindir . '/script.php')) { + require_js($baseurl . '/script.php'); + } + $contributions = array(); + foreach ($stylesheets as $stylesheet) { + $contributions[] = '"'; + } + return $contributions; + } + + /** + * Prints the question including the number, grading details, content, + * feedback and interactions + * + * This function prints the question including the question number, + * grading details, content for the question, any feedback for the previously + * submitted responses and the interactions. The default implementation calls + * various other methods to print each of these parts and most question types + * will just override those methods. + * @param object $question The question to be rendered. Question type + * specific information is included. The + * maximum possible grade is in ->maxgrade. The name + * prefix for any named elements is in ->name_prefix. + * @param object $state The state to render the question in. The grading + * information is in ->grade, ->raw_grade and + * ->penalty. The current responses are in + * ->responses. This is an associative array (or the + * empty string or null in the case of no responses + * submitted). The last graded state is in + * ->last_graded (hence the most recently graded + * responses are in ->last_graded->responses). The + * question type specific information is also + * included. + * @param integer $number The number for this question. + * @param object $cmoptions + * @param object $options An object describing the rendering options. + */ function print_question(&$question, &$state, $number, $cmoptions, $options) { /* The default implementation should work for most question types provided the member functions it calls are overridden where required. diff --git a/question/type/random/questiontype.php b/question/type/random/questiontype.php index fb96a22a44b..7f1765507cd 100644 --- a/question/type/random/questiontype.php +++ b/question/type/random/questiontype.php @@ -218,6 +218,12 @@ class random_qtype extends default_questiontype { ->get_actual_response($wrappedquestion, $state); } + function get_html_head_contributions(&$question, &$state) { + global $QTYPES; + $wrappedquestion = &$state->options->question; + return $QTYPES[$wrappedquestion->qtype] + ->get_html_head_contributions($wrappedquestion, $state); + } function print_question(&$question, &$state, &$number, $cmoptions, $options) { global $QTYPES;