diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php index 3044c7ff808..c9705a98b33 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -802,7 +802,14 @@ class restore_groups_structure_step extends restore_structure_step { $data->groupingid = $this->get_new_parentid('grouping'); // Use new parentid $data->groupid = $this->get_mappingid('group', $data->groupid); // Get from mappings - $DB->insert_record('groupings_groups', $data); // No need to set this mapping (no child info nor files) + + $params = array(); + $params['groupingid'] = $data->groupingid; + $params['groupid'] = $data->groupid; + + if (!$DB->record_exists('groupings_groups', $params)) { + $DB->insert_record('groupings_groups', $data); // No need to set this mapping (no child info nor files) + } } protected function after_execute() { diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 83adec74439..75e30882706 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -6916,6 +6916,23 @@ FROM upgrade_main_savepoint(true, 2011110200.02); } + if ($oldversion < 2011111500.01) { + upgrade_set_timeout(60*20); // this may take a while + // Remove duplicate entries from groupings_groups table + $sql = 'SELECT MIN(id) AS firstid, groupingid, groupid FROM {groupings_groups} '. + 'GROUP BY groupingid, groupid HAVING COUNT(id)>1'; + $badrs = $DB->get_recordset_sql($sql); + foreach ($badrs as $badrec) { + $where = 'groupingid = ? and groupid = ? and id > ?'; + $params = array($badrec->groupingid, $badrec->groupid, $badrec->firstid); + $DB->delete_records_select('groupings_groups', $where, $params); + } + $badrs->close(); + + // Main savepoint reached + upgrade_main_savepoint(true, 2011111500.01); + } + return true; } diff --git a/lib/moodlelib.php b/lib/moodlelib.php index fee53244e8f..b5ea4586750 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -8725,12 +8725,17 @@ function generate_password($maxlen=10) { $filler1 = $fillers[rand(0, strlen($fillers) - 1)]; $password = $word1 . $filler1 . $word2; } else { - $maxlen = !empty($CFG->minpasswordlength) ? $CFG->minpasswordlength : 0; + $minlen = !empty($CFG->minpasswordlength) ? $CFG->minpasswordlength : 0; $digits = $CFG->minpassworddigits; $lower = $CFG->minpasswordlower; $upper = $CFG->minpasswordupper; $nonalphanum = $CFG->minpasswordnonalphanum; - $additional = $maxlen - ($lower + $upper + $digits + $nonalphanum); + $total = $lower + $upper + $digits + $nonalphanum; + // minlength should be the greater one of the two ( $minlen and $total ) + $minlen = $minlen < $total ? $total : $minlen; + // maxlen can never be smaller than minlen + $maxlen = $minlen > $maxlen ? $minlen : $maxlen; + $additional = $maxlen - $total; // Make sure we have enough characters to fulfill // complexity requirements diff --git a/lib/questionlib.php b/lib/questionlib.php index 1bfe045e44f..081a0e67cd0 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -825,16 +825,8 @@ function get_question_options(&$questions, $loadtags = false) { * @return string the HTML for the img tag. */ function print_question_icon($question) { - global $OUTPUT; - - $qtype = question_bank::get_qtype($question->qtype, false); - $namestr = $qtype->menu_name(); - - // TODO convert to return a moodle_icon object, or whatever the class is. - $html = '' .
-            $namestr . ''; - - return $html; + global $PAGE; + return $PAGE->get_renderer('question', 'bank')->qtype_icon($question->qtype); } /** diff --git a/lib/simpletest/testcompletionlib.php b/lib/simpletest/testcompletionlib.php index f4288fd9eb0..c78063be010 100644 --- a/lib/simpletest/testcompletionlib.php +++ b/lib/simpletest/testcompletionlib.php @@ -527,7 +527,7 @@ WHERE $c->__construct((object)array('id'=>42)); // 1) Basic usage - $c->expectAt(0,'get_tracked_users',array(false, array(), 0, '', '', '')); + $c->expectAt(0,'get_tracked_users',array(false, array(), 0, '', '', '', null)); $c->setReturnValueAt(0,'get_tracked_users',array( (object)array('id'=>100,'firstname'=>'Woot','lastname'=>'Plugh'), (object)array('id'=>201,'firstname'=>'Vroom','lastname'=>'Xyzzy'), @@ -556,7 +556,7 @@ WHERE ),$c->get_progress_all(false)); // 2) With more than 1,000 results - $c->expectAt(1,'get_tracked_users',array(true, 3, 0, '', '', '')); + $c->expectAt(1,'get_tracked_users',array(true, 3, 0, '', '', '', null)); $tracked=array(); $ids=array(); diff --git a/mod/assignment/lib.php b/mod/assignment/lib.php index 5d4d5fb3ded..0be90a6d383 100644 --- a/mod/assignment/lib.php +++ b/mod/assignment/lib.php @@ -3540,7 +3540,7 @@ function assignment_get_coursemodule_info($coursemodule) { } if ($coursemodule->showdescription) { // Convert intro to html. Do not filter cached version, filters run at display time. - $info->content = format_module_intro('assignment', $assignment, $coursemodule->id, false); + $result->content = format_module_intro('assignment', $assignment, $coursemodule->id, false); } return $result; } else { diff --git a/mod/forum/lib.php b/mod/forum/lib.php index a3f28f1a395..4a0e2f7dacf 100644 --- a/mod/forum/lib.php +++ b/mod/forum/lib.php @@ -5943,8 +5943,9 @@ function forum_tp_mark_posts_read($user, $postids) { if ($new) { list($usql, $new_params) = $DB->get_in_or_equal($new); - $params = array($user->id, $now, $now, $user->id, $cutoffdate); + $params = array($user->id, $now, $now, $user->id); $params = array_merge($params, $new_params); + $params[] = $cutoffdate; $sql = "INSERT INTO {forum_read} (userid, postid, discussionid, forumid, firstread, lastread) diff --git a/mod/quiz/attemptlib.php b/mod/quiz/attemptlib.php index 22dba91fac0..385832d3d3d 100644 --- a/mod/quiz/attemptlib.php +++ b/mod/quiz/attemptlib.php @@ -418,10 +418,17 @@ class quiz_attempt { * @param object $quiz the quiz object for this attempt and user. * @param object $cm the course_module object for this quiz. * @param object $course the row from the course table for the course we belong to. + * @param bool $loadquestions (optional) if true, the default, load all the details + * of the state of each question. Else just set up the basic details of the attempt. */ - public function __construct($attempt, $quiz, $cm, $course) { + public function __construct($attempt, $quiz, $cm, $course, $loadquestions = true) { $this->attempt = $attempt; $this->quizobj = new quiz($quiz, $cm, $course); + + if (!$loadquestions) { + return; + } + $this->quba = question_engine::load_questions_usage_by_activity($this->attempt->uniqueid); $this->determine_layout(); $this->number_questions(); diff --git a/mod/quiz/editlib.php b/mod/quiz/editlib.php index 6b5ddf9f74a..0097787deb5 100644 --- a/mod/quiz/editlib.php +++ b/mod/quiz/editlib.php @@ -781,7 +781,7 @@ function quiz_print_singlequestion($question, $returnurl, $quiz) { echo quiz_question_edit_button($quiz->cmid, $question, $returnurl, quiz_question_tostring($question) . ' '); echo ''; - print_question_icon($question); + echo print_question_icon($question); echo ' ' . question_bank::get_qtype_name($question->qtype) . ''; echo '' . quiz_question_preview_button($quiz, $question, true) . ''; @@ -807,7 +807,7 @@ function quiz_print_randomquestion(&$question, &$pageurl, &$quiz, $quiz_qbanktoo } echo '
'; - print_question_icon($question); + echo print_question_icon($question); print_random_option_icon($question); echo ' ' . get_string('randomfromcategory', 'quiz') . '
'; @@ -886,7 +886,7 @@ function quiz_print_randomquestion(&$question, &$pageurl, &$quiz, $quiz_qbanktoo function quiz_print_singlequestion_reordertool($question, $returnurl, $quiz) { echo '
'; echo ''; echo '' . @@ -920,7 +920,7 @@ function quiz_print_randomquestion_reordertool(&$question, &$pageurl, &$quiz) { echo '
'; echo '
'; echo $reordercheckboxlabel; - print_question_icon($question); + echo print_question_icon($question); print_random_option_icon($question); if ($questioncount == 0) { diff --git a/mod/quiz/grade.php b/mod/quiz/grade.php index dd2b22cbebc..ddc8c07ef91 100644 --- a/mod/quiz/grade.php +++ b/mod/quiz/grade.php @@ -26,27 +26,68 @@ require_once(dirname(__FILE__) . '/../../config.php'); +require_once($CFG->dirroot . '/mod/quiz/locallib.php'); require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php'); $id = required_param('id', PARAM_INT); +$userid = optional_param('userid', 0, PARAM_INT); -if (!$cm = get_coursemodule_from_id('quiz', $id)) { - print_error('invalidcoursemodule'); -} -if (!$quiz = $DB->get_record('quiz', array('id' => $cm->instance))) { - print_error('invalidquizid'); -} -if (!$course = $DB->get_record('course', array('id' => $quiz->course))) { - print_error('coursemisconf'); -} - +$cm = get_coursemodule_from_id('quiz', $id, 0, false, MUST_EXIST); +$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); +$quiz = $DB->get_record('quiz', array('id' => $cm->instance), '*', MUST_EXIST); require_login($course, false, $cm); -$reportlist = quiz_report_list(get_context_instance(CONTEXT_MODULE, $cm->id)); -if (!empty($reportlist)) { - redirect(new moodle_url('/mod/quiz/report.php', array( - 'id' => $cm->id, 'mode' => reset($reportlist)))); -} else { - redirect(new moodle_url('/mod/quiz/view.php', array('id' => $cm->id))); +$reportlist = quiz_report_list(context_module::instance($cm->id)); +if (empty($reportlist) || $userid == $USER->id) { + // If the user cannot see reports, or can see reports but is looking + // at their own grades, redirect them to the view.php page. + // (The looking at their own grades case is unlikely, since users who + // appear in the gradebook are unlikely to be able to see quiz reports, + // but it is possible.) + redirect(new moodle_url('/mod/quiz/view.php', array('id' => $cm->id))); } + +// Now we know the user is interested in reports. If they are interested in a +// specific other user, try to send them to the most appropriate attempt review page. +if ($userid) { + + // Work out which attempt is most significant from a grading point of view. + $attempts = quiz_get_user_attempts($quiz->id, $userid, 'finished'); + $attempt = null; + switch ($quiz->grademethod) { + case QUIZ_ATTEMPTFIRST: + $attempt = reset($attempts); + break; + + case QUIZ_ATTEMPTLAST: + case QUIZ_GRADEAVERAGE: + $attempt = end($attempts); + break; + + case QUIZ_GRADEHIGHEST: + $maxmark = 0; + foreach ($attempts as $at) { + // >=, since we want to most recent relevant attempt. + if ((float) $at->sumgrades >= $maxmark) { + $maxmark = $at->sumgrades; + $attempt = $at; + } + } + break; + } + + // If the user can review the relevant attempt, redirect to it. + if ($attempt) { + $attemptobj = new quiz_attempt($attempt, $quiz, $cm, $course, false); + if ($attemptobj->is_review_allowed()) { + redirect($attemptobj->review_url()); + } + } + + // Otherwise, fall thorugh to the generic case. +} + +// Send the user to the first report they can see. +redirect(new moodle_url('/mod/quiz/report.php', array( + 'id' => $cm->id, 'mode' => reset($reportlist)))); diff --git a/mod/quiz/module.js b/mod/quiz/module.js index ec37234f43f..d9d217f685c 100644 --- a/mod/quiz/module.js +++ b/mod/quiz/module.js @@ -170,7 +170,7 @@ M.mod_quiz.nav.init = function(Y) { if (Y.one('a.endtestlink')) { Y.on('click', function(e) { - e.preventDefault(e); + e.preventDefault(); Y.one('#followingpage').set('value', -1); Y.one('#responseform').submit(); }, 'a.endtestlink'); diff --git a/mod/quiz/report/attemptsreport.php b/mod/quiz/report/attemptsreport.php index 89bfefc1fc5..f26b2ec7071 100644 --- a/mod/quiz/report/attemptsreport.php +++ b/mod/quiz/report/attemptsreport.php @@ -621,13 +621,21 @@ abstract class quiz_attempt_report_table extends table_sql { return; } + // This condition roughly filters the list of attempts to be considered. + // It is only used in a subselect to help crappy databases (see MDL-30122) + // therefore, it is better to use a very simple join, which may include + // too many records, than to do a super-accurate join. + $qubaids = new qubaid_join("{quiz_attempts} {$alias}quiza", "{$alias}quiza.uniqueid", + "{$alias}quiza.quiz = :{$alias}quizid", array("{$alias}quizid" => $this->sql->params['quizid'])); + $dm = new question_engine_data_mapper(); - $inlineview = $dm->question_attempt_latest_state_view($alias); + list($inlineview, $viewparams) = $dm->question_attempt_latest_state_view($alias, $qubaids); $this->sql->fields .= ",\n$fields"; $this->sql->from .= "\nLEFT JOIN $inlineview ON " . "$alias.questionusageid = quiza.uniqueid AND $alias.slot = :{$alias}slot"; $this->sql->params[$alias . 'slot'] = $slot; + $this->sql->params = array_merge($this->sql->params, $viewparams); } /** diff --git a/mod/quiz/startattempt.php b/mod/quiz/startattempt.php index c660df32e93..c3e31d855cd 100644 --- a/mod/quiz/startattempt.php +++ b/mod/quiz/startattempt.php @@ -101,7 +101,7 @@ if ($lastattempt && !$lastattempt->timefinish) { $output = $PAGE->get_renderer('mod_quiz'); if (!$quizobj->is_preview_user() && $messages) { print_error('attempterror', 'quiz', $quizobj->view_url(), - $output->print_messages($messages)); + $output->access_messages($messages)); } if ($accessmanager->is_preflight_check_required($currentattemptid)) { diff --git a/mod/quiz/styles.css b/mod/quiz/styles.css index 60d83d46150..f96340959e2 100644 --- a/mod/quiz/styles.css +++ b/mod/quiz/styles.css @@ -96,6 +96,13 @@ div.editq div.question div.content .singlequestion a .questiontext{text-decorati #page-mod-quiz-mod #reviewoptionshdr fieldset.fgroup span label, #adminquizreviewoptions span label {margin-left: 0.4em;} +#page-mod-quiz-mod.dir-rtl #reviewoptionshdr .fitem, +#adminquizreviewoptions .group {float: right; width: 24%;} +#page-mod-quiz-mod.dir-rtl #reviewoptionshdr fieldset.fgroup span, +#adminquizreviewoptions span {float: right; clear: right;} +#page-mod-quiz-mod.dir-rtl #reviewoptionshdr .fitemtitle, +#adminquizreviewoptions .fitemtitle {text-align: right;} + /** Mod quiz view **/ #page-mod-quiz-view .quizinfo, #page-mod-quiz-view #page .quizgradefeedback, @@ -377,4 +384,4 @@ bank window's title is prominent enough*/ .qnum label {padding-right: 0.25em;} /** settings.php */ -#adminquizreviewoptions {margin-bottom: 0.5em;} +#adminquizreviewoptions {margin-bottom: 0.5em;} \ No newline at end of file diff --git a/mod/quiz/summary.php b/mod/quiz/summary.php index 065bb8cccb3..869b4b1d75e 100644 --- a/mod/quiz/summary.php +++ b/mod/quiz/summary.php @@ -61,7 +61,7 @@ $messages = $accessmanager->prevent_access(); $output = $PAGE->get_renderer('mod_quiz'); if (!$attemptobj->is_preview_user() && $messages) { print_error('attempterror', 'quiz', $attemptobj->view_url(), - $output->print_messages($messages)); + $output->access_messages($messages)); } if ($accessmanager->is_preflight_check_required($attemptobj->get_attemptid())) { redirect($attemptobj->start_attempt_url(null, $page)); diff --git a/mod/scorm/lib.php b/mod/scorm/lib.php index b4c84a94fa4..50906fbd4d9 100644 --- a/mod/scorm/lib.php +++ b/mod/scorm/lib.php @@ -510,7 +510,7 @@ function scorm_cron () { $cfg_scorm = get_config('scorm'); if (!empty($cfg_scorm->allowaicchacp)) { $expiretime = time() - ($cfg_scorm->aicchacpkeepsessiondata*24*60*60); - $DB->delete_records_select('scorm_aicc_session', 'WHERE timemodified < ?', array($expiretime)); + $DB->delete_records_select('scorm_aicc_session', 'timemodified < ?', array($expiretime)); } } diff --git a/mod/wiki/styles.css b/mod/wiki/styles.css index 18e0391422e..dcfd4804bbd 100644 --- a/mod/wiki/styles.css +++ b/mod/wiki/styles.css @@ -171,6 +171,10 @@ float: right; } +.dir-rtl .wiki_diffuserleft { + float: left; +} + .wiki_diffuserright { float: left; } diff --git a/question/behaviour/adaptive/behaviour.php b/question/behaviour/adaptive/behaviour.php index c663b522268..9144296f543 100644 --- a/question/behaviour/adaptive/behaviour.php +++ b/question/behaviour/adaptive/behaviour.php @@ -38,8 +38,8 @@ defined('MOODLE_INTERNAL') || die(); class qbehaviour_adaptive extends question_behaviour_with_save { const IS_ARCHETYPAL = true; - public function required_question_definition_type() { - return 'question_automatically_gradable'; + public function is_compatible_question(question_definition $question) { + return $question instanceof question_automatically_gradable; } public function get_expected_data() { diff --git a/question/behaviour/behaviourbase.php b/question/behaviour/behaviourbase.php index 95c0e2a4dfe..ca09117def6 100644 --- a/question/behaviour/behaviourbase.php +++ b/question/behaviour/behaviourbase.php @@ -70,21 +70,39 @@ abstract class question_behaviour { public function __construct(question_attempt $qa, $preferredbehaviour) { $this->qa = $qa; $this->question = $qa->get_question(); - $requiredclass = $this->required_question_definition_type(); - if (!$this->question instanceof $requiredclass) { + if (!$this->is_compatible_question($this->question)) { throw new coding_exception('This behaviour (' . $this->get_name() . ') cannot work with this question (' . get_class($this->question) . ')'); } } + /** + * Some behaviours can only work with certing types of question. This method + * allows the behaviour to verify that a question is compatible. + * + * This implementation is only provided for backwards-compatibility. You should + * override this method if you are implementing a behaviour. + * + * @param question_definition $question the question. + */ + public function is_compatible_question(question_definition $question) { + $requiredclass = $this->required_question_definition_type(); + return $this->question instanceof $requiredclass; + } + /** * Most behaviours can only work with {@link question_definition}s * of a particular subtype, or that implement a particular interface. * This method lets the behaviour document that. The type of * question passed to the constructor is then checked against this type. + * + * @deprecated since 2.2. Please use/override {@link is_compatible_question()} instead. + * * @return string class/interface name. */ - public abstract function required_question_definition_type(); + protected function required_question_definition_type() { + return 'question_definition'; + } /** * @return string the name of this behaviour. For example the name of diff --git a/question/behaviour/deferredfeedback/behaviour.php b/question/behaviour/deferredfeedback/behaviour.php index 8bc59fc7a70..5c5de7a31ee 100644 --- a/question/behaviour/deferredfeedback/behaviour.php +++ b/question/behaviour/deferredfeedback/behaviour.php @@ -40,8 +40,8 @@ defined('MOODLE_INTERNAL') || die(); class qbehaviour_deferredfeedback extends question_behaviour_with_save { const IS_ARCHETYPAL = true; - public function required_question_definition_type() { - return 'question_automatically_gradable'; + public function is_compatible_question(question_definition $question) { + return $question instanceof question_automatically_gradable; } public static function get_unused_display_options() { diff --git a/question/behaviour/immediatefeedback/behaviour.php b/question/behaviour/immediatefeedback/behaviour.php index 2e7bbe02655..f12db5d90f4 100644 --- a/question/behaviour/immediatefeedback/behaviour.php +++ b/question/behaviour/immediatefeedback/behaviour.php @@ -42,8 +42,8 @@ defined('MOODLE_INTERNAL') || die(); class qbehaviour_immediatefeedback extends question_behaviour_with_save { const IS_ARCHETYPAL = true; - public function required_question_definition_type() { - return 'question_automatically_gradable'; + public function is_compatible_question(question_definition $question) { + return $question instanceof question_automatically_gradable; } public function get_min_fraction() { diff --git a/question/behaviour/informationitem/behaviour.php b/question/behaviour/informationitem/behaviour.php index 8c2cefed705..472a6e258c4 100644 --- a/question/behaviour/informationitem/behaviour.php +++ b/question/behaviour/informationitem/behaviour.php @@ -39,8 +39,8 @@ defined('MOODLE_INTERNAL') || die(); */ class qbehaviour_informationitem extends question_behaviour { - public function required_question_definition_type() { - return 'question_definition'; + public function is_compatible_question(question_definition $question) { + return true; } public function get_expected_data() { diff --git a/question/behaviour/interactive/behaviour.php b/question/behaviour/interactive/behaviour.php index 976bb08e95f..27e0b303ba4 100644 --- a/question/behaviour/interactive/behaviour.php +++ b/question/behaviour/interactive/behaviour.php @@ -52,8 +52,8 @@ class qbehaviour_interactive extends question_behaviour_with_save { */ const READONLY_EXCEPT_TRY_AGAIN = 23485299; - public function required_question_definition_type() { - return 'question_automatically_gradable'; + public function is_compatible_question(question_definition $question) { + return $question instanceof question_automatically_gradable; } public function get_right_answer_summary() { diff --git a/question/behaviour/interactivecountback/behaviour.php b/question/behaviour/interactivecountback/behaviour.php index 5b3e7f44164..8a14cb76818 100644 --- a/question/behaviour/interactivecountback/behaviour.php +++ b/question/behaviour/interactivecountback/behaviour.php @@ -64,8 +64,8 @@ require_once(dirname(__FILE__) . '/../interactive/behaviour.php'); class qbehaviour_interactivecountback extends qbehaviour_interactive { const IS_ARCHETYPAL = false; - public function required_question_definition_type() { - return 'question_automatically_gradable_with_countback'; + public function is_compatible_question(question_definition $question) { + return $question instanceof question_automatically_gradable_with_countback; } protected function adjust_fraction($fraction, question_attempt_pending_step $pendingstep) { diff --git a/question/behaviour/missing/behaviour.php b/question/behaviour/missing/behaviour.php index 1726e011166..3475040a767 100644 --- a/question/behaviour/missing/behaviour.php +++ b/question/behaviour/missing/behaviour.php @@ -45,8 +45,9 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class qbehaviour_missing extends question_behaviour { - public function required_question_definition_type() { - return 'question_definition'; + + public function is_compatible_question(question_definition $question) { + return true; } public function summarise_action(question_attempt_step $step) { diff --git a/question/behaviour/upgrade.txt b/question/behaviour/upgrade.txt index db780e8dbcd..63b941ee3de 100644 --- a/question/behaviour/upgrade.txt +++ b/question/behaviour/upgrade.txt @@ -2,10 +2,15 @@ This files describes API changes for question behaviour plugins. === 2.2 === -* The old +1) The old public static function get_required_behaviours() method is no more. Instead use the ->dependencies facility in version.php. E.g. $plugin->dependencies = array( 'qbehaviour_immediatefeedback' => 2011102700, 'qbehaviour_deferredcbm' => 2011102700 ); + +2) The old required_question_definition_type method has been replaced by a new +is_compatible_question method. You should change your behaviour to override the +new method, not the old one. This change has been implemented in a +backwards-compatible way, so behaviours will not break. diff --git a/question/editlib.php b/question/editlib.php index 8bc1c2c80c1..4079d48488b 100644 --- a/question/editlib.php +++ b/question/editlib.php @@ -1793,7 +1793,7 @@ function print_qtype_to_add_option($qtype) { echo ''; $fakequestion = new stdClass(); $fakequestion->qtype = $qtype->name(); - print_question_icon($fakequestion); + echo print_question_icon($fakequestion); echo $qtype->menu_name() . '' . $summary; echo "\n"; echo "
\n"; diff --git a/question/engine/datalib.php b/question/engine/datalib.php index cdcaf8f07da..2f41b12d7ff 100644 --- a/question/engine/datalib.php +++ b/question/engine/datalib.php @@ -865,33 +865,42 @@ ORDER BY END) = 0"; } - public function question_attempt_latest_state_view($alias) { - return "( - SELECT - {$alias}qa.id AS questionattemptid, - {$alias}qa.questionusageid, - {$alias}qa.slot, - {$alias}qa.behaviour, - {$alias}qa.questionid, - {$alias}qa.variant, - {$alias}qa.maxmark, - {$alias}qa.minfraction, - {$alias}qa.flagged, - {$alias}qa.questionsummary, - {$alias}qa.rightanswer, - {$alias}qa.responsesummary, - {$alias}qa.timemodified, - {$alias}qas.id AS attemptstepid, - {$alias}qas.sequencenumber, - {$alias}qas.state, - {$alias}qas.fraction, - {$alias}qas.timecreated, - {$alias}qas.userid + /** + * Get a subquery that returns the latest step of every qa in some qubas. + * Currently, this is only used by the quiz reports. See + * {@link quiz_attempt_report_table::add_latest_state_join()}. + * @param string $alias alias to use for this inline-view. + * @param qubaid_condition $qubaids restriction on which question_usages we + * are interested in. This is important for performance. + * @return array with two elements, the SQL fragment and any params requried. + */ + public function question_attempt_latest_state_view($alias, qubaid_condition $qubaids) { + return array("( + SELECT {$alias}qa.id AS questionattemptid, + {$alias}qa.questionusageid, + {$alias}qa.slot, + {$alias}qa.behaviour, + {$alias}qa.questionid, + {$alias}qa.variant, + {$alias}qa.maxmark, + {$alias}qa.minfraction, + {$alias}qa.flagged, + {$alias}qa.questionsummary, + {$alias}qa.rightanswer, + {$alias}qa.responsesummary, + {$alias}qa.timemodified, + {$alias}qas.id AS attemptstepid, + {$alias}qas.sequencenumber, + {$alias}qas.state, + {$alias}qas.fraction, + {$alias}qas.timecreated, + {$alias}qas.userid - FROM {question_attempts} {$alias}qa - JOIN {question_attempt_steps} {$alias}qas ON - {$alias}qas.id = {$this->latest_step_for_qa_subquery($alias . 'qa.id')} - ) $alias"; + FROM {$qubaids->from_question_attempts($alias . 'qa')} + JOIN {question_attempt_steps} {$alias}qas ON + {$alias}qas.id = {$this->latest_step_for_qa_subquery($alias . 'qa.id')} + WHERE {$qubaids->where()} + ) $alias", $qubaids->from_where_params()); } protected function latest_step_for_qa_subquery($questionattemptid = 'qa.id') { diff --git a/question/renderer.php b/question/renderer.php new file mode 100644 index 00000000000..f8f667b5981 --- /dev/null +++ b/question/renderer.php @@ -0,0 +1,49 @@ +. + +/** + * Renderers for outputting parts of the question bank. + * + * @package moodlecore + * @subpackage questionbank + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +/** + * This renderer outputs parts of the question bank. + * + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class core_question_bank_renderer extends plugin_renderer_base { + + /** + * Output the icon for a question type + * @param string $qtype the question type. + * @return string HTML fragment. + */ + public function qtype_icon($qtype) { + $qtype = question_bank::get_qtype($qtype, false); + $namestr = $qtype->local_name(); + + return $this->pix_icon('icon', $namestr, $qtype->plugin_name(), array('title' => $namestr)); + } +} diff --git a/question/type/edit_question_form.php b/question/type/edit_question_form.php index 34f52a80df9..e7505f35edc 100644 --- a/question/type/edit_question_form.php +++ b/question/type/edit_question_form.php @@ -344,7 +344,7 @@ abstract class question_edit_form extends moodleform { $mform->setType($feedbackname, PARAM_RAW); if ($withshownumpartscorrect && $feedbackname == 'partiallycorrectfeedback') { - $mform->addElement('checkbox', 'shownumcorrect', + $mform->addElement('advcheckbox', 'shownumcorrect', get_string('options', 'question'), get_string('shownumpartscorrect', 'question')); } @@ -361,11 +361,11 @@ abstract class question_edit_form extends moodleform { $repeatedoptions['hint']['type'] = PARAM_RAW; if ($withclearwrong) { - $repeated[] = $mform->createElement('checkbox', 'hintclearwrong', + $repeated[] = $mform->createElement('advcheckbox', 'hintclearwrong', get_string('options', 'question'), get_string('clearwrongparts', 'question')); } if ($withshownumpartscorrect) { - $repeated[] = $mform->createElement('checkbox', 'hintshownumcorrect', '', + $repeated[] = $mform->createElement('advcheckbox', 'hintshownumcorrect', '', get_string('shownumpartscorrect', 'question')); } diff --git a/question/type/numerical/edit_numerical_form.php b/question/type/numerical/edit_numerical_form.php index 512f6949581..2775ef0b8d5 100644 --- a/question/type/numerical/edit_numerical_form.php +++ b/question/type/numerical/edit_numerical_form.php @@ -264,7 +264,7 @@ class qtype_numerical_edit_form extends question_edit_form { if ($data['fraction'][$key] == 1) { $maxgrade = true; } - if (!is_numeric($data['tolerance'][$key])) { + if ($answer !== '*' && !is_numeric($data['tolerance'][$key])) { $errors['tolerance['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated'); } diff --git a/question/type/numerical/questiontype.php b/question/type/numerical/questiontype.php index 5090b00206a..07433189c02 100644 --- a/question/type/numerical/questiontype.php +++ b/question/type/numerical/questiontype.php @@ -641,16 +641,17 @@ class qtype_numerical_answer_processor { $numberstring = $matches[0]; if ($this->unitsbefore) { - $unit = substr($response, 0, -strlen($numberstring)); + // substr returns false when it means '', so cast back to string. + $unit = (string) substr($response, 0, -strlen($numberstring)); } else { - $unit = substr($response, strlen($numberstring)); + $unit = (string) substr($response, strlen($numberstring)); } if (!is_null($separateunit)) { $unit = $separateunit; } - if ($unit && $this->is_known_unit($unit)) { + if ($this->is_known_unit($unit)) { $multiplier = 1 / $this->units[$unit]; } else { $multiplier = null; diff --git a/theme/base/style/core.css b/theme/base/style/core.css index 4976044d817..3abeb99b9cf 100644 --- a/theme/base/style/core.css +++ b/theme/base/style/core.css @@ -218,6 +218,8 @@ a.skip:active {position: static;display: block;} .mform .ftags label.accesshide {display: block;position: static;} .mform .ftags select {margin-bottom: 0.7em;min-width: 22em;} +input#id_externalurl {direction:ltr;} + /** Browser corrections for mforms **/ .ie .mform .fitem .felement {margin-left:0;text-align:left;float:left;} /** Fix IE double margin + float bugs **/ @@ -720,6 +722,23 @@ body.tag .managelink {padding: 5px;} .mod-indent-15, .mod-indent-huge {margin-left:300px;} +.dir-rtl .mod-indent-1 {margin-right:20px;margin-left:0;} +.dir-rtl .mod-indent-2 {margin-right:40px;margin-left:0;} +.dir-rtl .mod-indent-3 {margin-right:60px;margin-left:0;} +.dir-rtl .mod-indent-4 {margin-right:80px;margin-left:0;} +.dir-rtl .mod-indent-5 {margin-right:100px;margin-left:0;} +.dir-rtl .mod-indent-6 {margin-right:120px;margin-left:0;} +.dir-rtl .mod-indent-7 {margin-right:140px;margin-left:0;} +.dir-rtl .mod-indent-8 {margin-right:160px;margin-left:0;} +.dir-rtl .mod-indent-9 {margin-right:180px;margin-left:0;} +.dir-rtl .mod-indent-10 {margin-right:200px;margin-left:0;} +.dir-rtl .mod-indent-11 {margin-right:220px;margin-left:0;} +.dir-rtl .mod-indent-12 {margin-right:240px;margin-left:0;} +.dir-rtl .mod-indent-13 {margin-right:260px;margin-left:0;} +.dir-rtl .mod-indent-14 {margin-right:280px;margin-left:0;} +.dir-rtl .mod-indent-15, +.dir-rtl .mod-indent-huge {margin-right:300px;margin-left:0;} + .dir-rtl .felement.feditor select {margin-right:18.75%;} /* Resourcelib mp3 player size: only width could be changed here, height hardcoded in JS */ @@ -804,3 +823,53 @@ ul li, ol li, .course-content ul.weeks .content .summary ol li, .course-content ul.topics .content .summary ol li {list-style: decimal outside none;} + +.dir-rtl #adminsettings #id_s__pathtodu, +.dir-rtl #adminsettings #id_s__aspellpath, +.dir-rtl #adminsettings #id_s__pathtodot, +.dir-rtl #adminsettings #id_s__supportemail, +.dir-rtl #adminsettings #id_s__supportpage, +.dir-rtl #adminsettings #id_s__sessioncookie, +.dir-rtl #adminsettings #id_s__sessioncookiepath, +.dir-rtl #adminsettings #id_s__sessioncookiedomain, +.dir-rtl #adminsettings #id_s__proxyhost, +.dir-rtl #adminsettings #id_s__proxyuser, +.dir-rtl #adminsettings #id_s__proxypassword, +.dir-rtl #adminsettings #id_s__proxybypass, +.dir-rtl #adminsettings #id_s__jabberhost, +.dir-rtl #adminsettings #id_s__jabberserver, +.dir-rtl #adminsettings #id_s__jabberusername, +.dir-rtl #adminsettings #id_s__jabberpassword, +.dir-rtl #adminsettings #id_s__additionalhtmlhead, +.dir-rtl #adminsettings #id_s__additionalhtmltopofbody, +.dir-rtl #adminsettings #id_s__additionalhtmlfooter, +.dir-rtl #adminsettings #id_s__docroot, +.dir-rtl #adminsettings #id_s__filter_tex_latexpreamble, +.dir-rtl #adminsettings #id_s__filter_tex_latexbackground, +.dir-rtl #adminsettings #id_s__filter_tex_pathlatex, +.dir-rtl #adminsettings #id_s__filter_tex_pathdvips, +.dir-rtl #adminsettings #id_s__filter_tex_pathconvert, +.dir-rtl #adminsettings #id_s__blockedip, +.dir-rtl #adminsettings #id_s__pathtoclam, +.dir-rtl #adminsettings #id_s__quarantinedir, +.dir-rtl #adminsettings #id_s__sitepolicy, +.dir-rtl #adminsettings #id_s__sitepolicyguest, +.dir-rtl #adminsettings #id_s__cronremotepassword, +.dir-rtl #adminsettings #id_s__allowedip, +.dir-rtl #adminsettings #id_s__blockedip, +.dir-rtl #adminsettings #id_s_enrol_meta_nosyncroleids, +.dir-rtl #adminsettings #id_s_enrol_ldap_host_url, +.dir-rtl #adminsettings #id_s_enrol_ldap_ldapencoding, +.dir-rtl #adminsettings #id_s_enrol_ldap_bind_dn, +.dir-rtl #adminsettings #id_s_enrol_ldap_bind_pw, +.dir-rtl #adminsettings #admin-emoticons .form-text, +.dir-rtl #adminsettings #admin-role_mapping input[type=text], +.dir-rtl #adminsettings #id_s_enrol_paypal_paypalbusiness, +.dir-rtl #adminsettings #id_s_enrol_flatfile_location, +#page-admin-setting-enrolsettingsflatfile.dir-rtl input[type=text], +#page-admin-setting-enrolsettingsdatabase.dir-rtl input[type=text], +#page-admin-auth-db.dir-rtl input[type=text] {direction: ltr;} + +#page-admin-setting-enrolsettingsflatfile.dir-rtl .informationbox {direction: ltr;text-align: left;} + +#page-admin-grade-edit-scale-edit.dir-rtl .error input#id_name {margin-right: 170px;} \ No newline at end of file diff --git a/theme/base/style/course.css b/theme/base/style/course.css index 3062ac629a0..37f9792bf87 100644 --- a/theme/base/style/course.css +++ b/theme/base/style/course.css @@ -27,7 +27,7 @@ .path-course-view li.activity form.togglecompletion .ajaxworking {position:absolute;top:0; left:20px;width: 20px; height: 20px;background: url([[pix:i/ajaxloader]]) no-repeat;} .dir-rtl.path-course-view li.activity {margin-right:0px;margin-left:20px;} .dir-rtl.path-course-view li.activity form.togglecompletion, -.dir-rtl.path-course-view li.activity span.autocompletion {right:auto;left:-20px;} +.dir-rtl.path-course-view li.activity span.autocompletion {right:auto;right:-20px;} .section img.movetarget {height:16px;width:80px;} diff --git a/theme/mymobile/renderers.php b/theme/mymobile/renderers.php index f7c253c5275..3007930d3c9 100644 --- a/theme/mymobile/renderers.php +++ b/theme/mymobile/renderers.php @@ -380,7 +380,7 @@ class theme_mymobile_core_renderer extends core_renderer { return ''; } - $loginapge = ((string)$this->page->url === get_login_url()); + $loginpage = ((string)$this->page->url === get_login_url()); $course = $this->page->course; if (session_is_loggedinas()) { @@ -407,7 +407,7 @@ class theme_mymobile_core_renderer extends core_renderer { } if (isguestuser()) { $loggedinas = $realuserinfo.get_string('loggedinasguest'); - if (!$loginapge) { + if (!$loginpage) { $loggedinas .= " (".get_string('login').')'; } } else if (is_role_switched($course->id)) { // Has switched roles @@ -421,7 +421,7 @@ class theme_mymobile_core_renderer extends core_renderer { } } else { $loggedinas = get_string('loggedinnot', 'moodle'); - if (!$loginapge) { + if (!$loginpage) { $loggedinas .= " (".get_string('login').')'; } } diff --git a/theme/standard/style/core.css b/theme/standard/style/core.css index 12cf9d88514..4b69618ca9f 100644 --- a/theme/standard/style/core.css +++ b/theme/standard/style/core.css @@ -277,6 +277,8 @@ h2.tag-heading {text-align:center;margin-left:auto;margin-right:auto;width:95%;} #tags-management-links, .tag .managelink {text-align:right;} table#tag-management-list {margin: 10px auto;width: 80%;} +#page-tag-index.dir-rtl .relatedpages {text-align:center;} +#page-tag-index.dir-rtl .user-box {float:right;} /** * Overriding base diff --git a/version.php b/version.php index eb0a3297af2..9642f623bed 100644 --- a/version.php +++ b/version.php @@ -30,10 +30,10 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2011111500.00; // YYYYMMDD = weekly release date of this DEV branch +$version = 2011111800.00; // YYYYMMDD = weekly release date of this DEV branch // RR = release increments - 00 in DEV branches // .XX = incremental changes -$release = '2.2beta (Build: 20111115)';// Human-friendly version name +$release = '2.2beta (Build: 20111118)';// Human-friendly version name $maturity = MATURITY_BETA; // this version's maturity level diff --git a/webservice/renderer.php b/webservice/renderer.php index 37b993561ff..75668fd6a8a 100644 --- a/webservice/renderer.php +++ b/webservice/renderer.php @@ -46,15 +46,24 @@ class core_webservice_renderer extends plugin_renderer_base { $table->cellspacing = 0; $table->cellpadding = 0; + // LTR/RTL support, for drawing button arrows in the right direction + if (right_to_left()) { + $addarrow = '▶'; + $removearrow = '◀'; + } else { + $addarrow = '◀'; + $removearrow = '▶'; + } + //create the add and remove button $addinput = html_writer::empty_tag('input', array('name' => 'add', 'id' => 'add', 'type' => 'submit', - 'value' => '◀' . ' ' . get_string('add'), + 'value' => $addarrow . ' ' . get_string('add'), 'title' => get_string('add'))); $addbutton = html_writer::tag('div', $addinput, array('id' => 'addcontrols')); $removeinput = html_writer::empty_tag('input', array('name' => 'remove', 'id' => 'remove', 'type' => 'submit', - 'value' => '▶' . ' ' . get_string('remove'), + 'value' => $removearrow . ' ' . get_string('remove'), 'title' => get_string('remove'))); $removebutton = html_writer::tag('div', $removeinput, array('id' => 'removecontrols'));