From 19bda8aa7e005870f18f512bd5392fb7fac681f3 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Tue, 10 Oct 2017 15:06:23 +0200 Subject: [PATCH 01/20] MDL-60377 webservice: Format correctly text returned by WS --- course/externallib.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/course/externallib.php b/course/externallib.php index 619129741f6..ac028ccf460 100644 --- a/course/externallib.php +++ b/course/externallib.php @@ -256,8 +256,9 @@ class core_course_external extends external_api { if (!empty($cm->showdescription) or $cm->modname == 'label') { // We want to use the external format. However from reading get_formatted_content(), $cm->content format is always FORMAT_HTML. + $options = array('noclean' => true); list($module['description'], $descriptionformat) = external_format_text($cm->content, - FORMAT_HTML, $modcontext->id, $cm->modname, 'intro', $cm->id); + FORMAT_HTML, $modcontext->id, $cm->modname, 'intro', $cm->id, $options); } //url of the module From 40be01a6366445695092689faf5ac1f3bea7bb55 Mon Sep 17 00:00:00 2001 From: Adrian Greeve Date: Wed, 25 Oct 2017 10:42:31 +0800 Subject: [PATCH 02/20] MDL-57055 mod_label: Add label administration for boost. This patch adds the label adminstration settings to the label edit page. --- mod/label/mod_form.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mod/label/mod_form.php b/mod/label/mod_form.php index a892fd2fd00..fec23dfcd57 100644 --- a/mod/label/mod_form.php +++ b/mod/label/mod_form.php @@ -30,6 +30,9 @@ require_once ($CFG->dirroot.'/course/moodleform_mod.php'); class mod_label_mod_form extends moodleform_mod { function definition() { + global $PAGE; + + $PAGE->force_settings_menu(); $mform = $this->_form; From b627bcfd259495734a5ac93d7b03e22531371021 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Fri, 15 Sep 2017 11:17:12 +0200 Subject: [PATCH 03/20] MDL-60265 tool_mobile: Return sitepolicy in tool_mobile_get_config WS --- admin/tool/mobile/classes/api.php | 1 + admin/tool/mobile/tests/externallib_test.php | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/admin/tool/mobile/classes/api.php b/admin/tool/mobile/classes/api.php index 987a58deee2..20c3ceb48a4 100644 --- a/admin/tool/mobile/classes/api.php +++ b/admin/tool/mobile/classes/api.php @@ -203,6 +203,7 @@ class api { } if (empty($section) or $section == 'sitepolicies') { + $settings->sitepolicy = $CFG->sitepolicy; $settings->disableuserimages = $CFG->disableuserimages; } diff --git a/admin/tool/mobile/tests/externallib_test.php b/admin/tool/mobile/tests/externallib_test.php index fbbca7db848..e5a53032ab6 100644 --- a/admin/tool/mobile/tests/externallib_test.php +++ b/admin/tool/mobile/tests/externallib_test.php @@ -121,6 +121,10 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase { require_once($CFG->dirroot . '/course/format/lib.php'); $this->resetAfterTest(true); + + $mysitepolicy = 'http://mysite.is/policy/'; + set_config('sitepolicy', $mysitepolicy); + $result = external::get_config(); $result = external_api::clean_returnvalue(external::get_config_returns(), $result); @@ -141,6 +145,7 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase { array('name' => 'numsections', 'value' => course_get_format($SITE)->get_course()->numsections), array('name' => 'newsitems', 'value' => $SITE->newsitems), array('name' => 'commentsperpage', 'value' => $CFG->commentsperpage), + array('name' => 'sitepolicy', 'value' => $mysitepolicy), array('name' => 'disableuserimages', 'value' => $CFG->disableuserimages), array('name' => 'mygradesurl', 'value' => user_mygrades_url()->out(false)), ); @@ -150,8 +155,7 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase { // Change a value and retrieve filtering by section. set_config('commentsperpage', 1); $expected[10]['value'] = 1; - unset($expected[11]); - unset($expected[12]); + array_splice($expected, 11); $result = external::get_config('frontpagesettings'); $result = external_api::clean_returnvalue(external::get_config_returns(), $result); From ba3904e81b32c852537404066427d3631c06fb9b Mon Sep 17 00:00:00 2001 From: Matt Porritt Date: Fri, 27 Oct 2017 11:11:12 +1100 Subject: [PATCH 04/20] MDL-60360 Global Search: Enable file indexing message update. If you have Global Search set up on a site without file indexing enabled, then at a later date you enable file indexing files associated with existing objects are not added. The only way to have files for existing objects indexed is to run a reindex of all content. This patches updates the file indexing language string in the solr search settings of Global Search to make this clear to the site administrator. --- search/engine/solr/lang/en/search_solr.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/search/engine/solr/lang/en/search_solr.php b/search/engine/solr/lang/en/search_solr.php index b8d7e12b3f5..6addfea4c24 100644 --- a/search/engine/solr/lang/en/search_solr.php +++ b/search/engine/solr/lang/en/search_solr.php @@ -28,7 +28,8 @@ $string['errorcreatingschema'] = 'Error creating the Solr schema: {$a}'; $string['errorvalidatingschema'] = 'Error validating Solr schema: field {$a->fieldname} does not exist. Please follow this link to set up the required fields.'; $string['extensionerror'] = 'The Apache Solr PHP extension is not installed. Please check the documentation.'; $string['fileindexing'] = 'Enable file indexing'; -$string['fileindexing_help'] = 'If your Solr install supports it, this feature allows Moodle to send files to be indexed.'; +$string['fileindexing_help'] = 'If your Solr install supports it, this feature allows Moodle to send files to be indexed.
+You will need to reindex all site contents after enabling this option for all files to be added.'; $string['fileindexsettings'] = 'File indexing settings'; $string['maxindexfilekb'] = 'Maximum file size to index (kB)'; $string['maxindexfilekb_help'] = 'Files larger than this number of kilobytes will not be included in search indexing. If set to zero, files of any size will be indexed.'; From 00c2d21bab87c211d4fe54f50f2408cea11fb8ee Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Tue, 24 Oct 2017 13:24:50 +0200 Subject: [PATCH 05/20] MDL-60572 admin: Fix forgottenpasswordurl for WS We should expect URLs in that field. The tool_mobile change is to not break the WS response if forgottenpasswordurl does not contain a URL. --- admin/tool/mobile/classes/api.php | 2 +- admin/tool/mobile/tests/externallib_test.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/admin/tool/mobile/classes/api.php b/admin/tool/mobile/classes/api.php index 2f1242e4a30..c04c29fdce0 100644 --- a/admin/tool/mobile/classes/api.php +++ b/admin/tool/mobile/classes/api.php @@ -127,7 +127,7 @@ class api { 'rememberusername' => $CFG->rememberusername, 'authloginviaemail' => $CFG->authloginviaemail, 'registerauth' => $CFG->registerauth, - 'forgottenpasswordurl' => $CFG->forgottenpasswordurl, + 'forgottenpasswordurl' => clean_param($CFG->forgottenpasswordurl, PARAM_URL), // We may expect a mailto: here. 'authinstructions' => $authinstructions, 'authnoneenabled' => (int) is_enabled_auth('none'), 'enablewebservices' => $CFG->enablewebservices, diff --git a/admin/tool/mobile/tests/externallib_test.php b/admin/tool/mobile/tests/externallib_test.php index fbbca7db848..f0e90866bfe 100644 --- a/admin/tool/mobile/tests/externallib_test.php +++ b/admin/tool/mobile/tests/externallib_test.php @@ -94,12 +94,14 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase { set_config('typeoflogin', api::LOGIN_VIA_BROWSER, 'tool_mobile'); set_config('logo', 'mock.png', 'core_admin'); set_config('logocompact', 'mock.png', 'core_admin'); + set_config('forgottenpasswordurl', 'mailto:fake@email.zy'); // Test old hack. list($authinstructions, $notusedformat) = external_format_text($authinstructions, FORMAT_MOODLE, $context->id); $expected['registerauth'] = 'email'; $expected['authinstructions'] = $authinstructions; $expected['typeoflogin'] = api::LOGIN_VIA_BROWSER; $expected['launchurl'] = "$CFG->wwwroot/$CFG->admin/tool/mobile/launch.php"; + $expected['forgottenpasswordurl'] = ''; // Expect empty when it's not an URL. if ($logourl = $OUTPUT->get_logo_url()) { $expected['logourl'] = $logourl->out(false); From 14f2ce60a7203ccb4a367bc24372b7201bdff767 Mon Sep 17 00:00:00 2001 From: David Monllao Date: Fri, 27 Oct 2017 12:48:27 +0200 Subject: [PATCH 06/20] weekly release 3.2.5+ --- version.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.php b/version.php index a75b9317283..2c6fd424b09 100644 --- a/version.php +++ b/version.php @@ -29,11 +29,11 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2016120505.10; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2016120505.11; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. -$release = '3.2.5+ (Build: 20171025)'; // Human-friendly version name +$release = '3.2.5+ (Build: 20171027)'; // Human-friendly version name $branch = '32'; // This version's branch. $maturity = MATURITY_STABLE; // This version's maturity level. From 20df9e8faadc28606ed362f76492ca7a504f147e Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Mon, 31 Jul 2017 18:58:29 +0200 Subject: [PATCH 07/20] MDL-59606 quiz responses: fix edge cases in the report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch conbines valuable contributions from Kashmira Nagwekar and Luca Bösch. Many thanks to them. However, the final form of the fix, and hence the blame, falls to me -- Tim. There were several issues here: * The load_questions_usages_by_activity method in question/engine/datalib.php was incorrectly treating the case when no data was returned. (Looks like a historic copy-pase from other methods that fetch one item by unique id, which therefore must exist.) * The report was not correctly handling the display when the 'Which tries' was set to 'with, and without, attempts'. * It was possible to select the 'All tries' option when also saying 'Users without attempts'. This combination makes not sense, so a disabledIf rule was added to the form. --- .../report/overview/tests/behat/basic.feature | 2 +- .../first_or_all_responses_table.php | 26 +++++- mod/quiz/report/responses/responses_form.php | 1 + .../responses/tests/behat/basic.feature | 91 +++++++++++++++++++ question/engine/datalib.php | 8 +- question/type/numerical/tests/helper.php | 27 +++++- 6 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 mod/quiz/report/responses/tests/behat/basic.feature diff --git a/mod/quiz/report/overview/tests/behat/basic.feature b/mod/quiz/report/overview/tests/behat/basic.feature index a61f2aee781..841d40bf6f3 100644 --- a/mod/quiz/report/overview/tests/behat/basic.feature +++ b/mod/quiz/report/overview/tests/behat/basic.feature @@ -1,4 +1,4 @@ -@mod @mod_quiz +@mod @mod_quiz @quiz @quiz_overview Feature: Basic use of the Grades report In order to easily get an overview of quiz attempts As a teacher diff --git a/mod/quiz/report/responses/first_or_all_responses_table.php b/mod/quiz/report/responses/first_or_all_responses_table.php index 4a90a08e40e..4f2600a13c2 100644 --- a/mod/quiz/report/responses/first_or_all_responses_table.php +++ b/mod/quiz/report/responses/first_or_all_responses_table.php @@ -68,6 +68,15 @@ class quiz_first_or_all_responses_table extends quiz_last_responses_table { // Insert an extra field in attempt data and extra rows where necessary. $newrawdata = array(); foreach ($this->rawdata as $attempt) { + if (!isset($this->questionusagesbyactivity[$attempt->usageid])) { + // This is a user without attempts. + $attempt->try = 0; + $attempt->lasttryforallparts = true; + $newrawdata[] = $attempt; + continue; + } + + // We have an attempt, which may require several rows. $maxtriesinanyslot = 1; foreach ($this->questionusagesbyactivity[$attempt->usageid]->get_slots() as $slot) { $tries = $this->get_no_of_tries($attempt, $slot); @@ -230,7 +239,7 @@ class quiz_first_or_all_responses_table extends quiz_last_responses_table { * @return string What to put in the cell for this column, for this row data. */ public function col_email($tablerow) { - if ($tablerow->try != 1) { + if ($tablerow->try > 1) { return ''; } else { return $tablerow->email; @@ -244,18 +253,27 @@ class quiz_first_or_all_responses_table extends quiz_last_responses_table { * @return string What to put in the cell for this column, for this row data. */ public function col_sumgrades($tablerow) { - if (!$tablerow->lasttryforallparts) { + if ($tablerow->try == 0) { + // We are showing a user without a quiz attempt. + return '-'; + } else if (!$tablerow->lasttryforallparts) { + // There are more rows to come for this quiz attempt, so we will show this later. return ''; } else { + // Last row for this attempt. Now is the time to show attempt-related data. return parent::col_sumgrades($tablerow); } } - public function col_state($tablerow) { - if (!$tablerow->lasttryforallparts) { + if ($tablerow->try == 0) { + // We are showing a user without a quiz attempt. + return '-'; + } else if (!$tablerow->lasttryforallparts) { + // There are more rows to come for this quiz attempt, so we will show this later. return ''; } else { + // Last row for this attempt. Now is the time to show attempt-related data. return parent::col_state($tablerow); } } diff --git a/mod/quiz/report/responses/responses_form.php b/mod/quiz/report/responses/responses_form.php index 461dcd2d3e2..4fdbe909a01 100644 --- a/mod/quiz/report/responses/responses_form.php +++ b/mod/quiz/report/responses/responses_form.php @@ -70,6 +70,7 @@ class quiz_responses_settings_form extends mod_quiz_attempts_report_form { question_attempt::ALL_TRIES => get_string('alltries', 'question')) ); $mform->setDefault('whichtries', question_attempt::LAST_TRY); + $mform->disabledIf('whichtries', 'attempts', 'eq', quiz_attempts_report::ENROLLED_WITHOUT); } } } diff --git a/mod/quiz/report/responses/tests/behat/basic.feature b/mod/quiz/report/responses/tests/behat/basic.feature new file mode 100644 index 00000000000..b8344ea20f6 --- /dev/null +++ b/mod/quiz/report/responses/tests/behat/basic.feature @@ -0,0 +1,91 @@ +@mod @mod_quiz @quiz @quiz_reponses +Feature: Basic use of the Responses report + In order to see how my students are progressing + As a teacher + I need to see all their quiz responses + + Background: Using the Responses report + Given the following "users" exist: + | username | firstname | lastname | + | teacher | The | Teacher | + | student1 | Student | One | + | student2 | Student | Two | + And the following "courses" exist: + | fullname | shortname | + | Course 1 | C1 | + And the following "course enrolments" exist: + | user | course | role | + | teacher | C1 | editingteacher | + | student1 | C1 | student | + | student2 | C1 | student | + And the following "question categories" exist: + | contextlevel | reference | name | + | Course | C1 | Test questions | + And the following "activities" exist: + | activity | name | intro | course | idnumber | preferredbehaviour | + | quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 | interactive | + And the following "questions" exist: + | questioncategory | qtype | name | template | + | Test questions | numerical | NQ | pi3tries | + And quiz "Quiz 1" contains the following questions: + | question | page | maxmark | + | NQ | 1 | 3.0 | + + @javascript + Scenario: Report works when there are no attempts + When I log in as "teacher" + And I am on "Course 1" course homepage + And I follow "Quiz 1" + And I navigate to "Results > Responses" in current page administration + Then I should see "Attempts: 0" + And I should see "Nothing to display" + And I set the field "Attempts from" to "enrolled users who have not attempted the quiz" + And I press "Show report" + And "Student One" row "State" column of "responses" table should contain "-" + + @javascript + Scenario: Report works when there are attempts + # Add an attempt + Given I log in as "student1" + And I am on "Course 1" course homepage + And I follow "Quiz 1" + And I press "Attempt quiz now" + And I set the field "Answer" to "1.0" + And I press "Check" + And I press "Try again" + And I set the field "Answer" to "3.0" + And I press "Check" + And I press "Try again" + And I set the field "Answer" to "3.14" + And I press "Check" + And I press "Finish attempt ..." + And I press "Submit all and finish" + And I click on "Submit all and finish" "button" in the "Confirmation" "dialogue" + And I log out + + When I log in as "teacher" + And I am on "Course 1" course homepage + And I follow "Quiz 1" + And I navigate to "Results > Responses" in current page administration + Then I should see "Attempts: 1" + And I should see "Student One" + And I should not see "Student Two" + And I set the field "Attempts from" to "enrolled users who have, or have not, attempted the quiz" + And I set the field "Which tries" to "All tries" + And I press "Show report" + And "Student OneReview attempt" row "Response 1Sort by Response 1 Ascending" column of "responses" table should contain "1.0" + And "Student OneReview attempt" row "State" column of "responses" table should contain "" + And "Finished" row "Grade/100.00Sort by Grade/100.00 Ascending" column of "responses" table should contain "33.33" + And "Finished" row "Response 1Sort by Response 1 Ascending" column of "responses" table should contain "3.14" + And "Student Two" row "State" column of "responses" table should contain "-" + And "Student Two" row "Response 1Sort by Response 1 Ascending" column of "responses" table should contain "-" + + @javascript + Scenario: Report does not allow strange combinations of options + When I log in as "teacher" + And I am on "Course 1" course homepage + And I follow "Quiz 1" + And I navigate to "Results > Responses" in current page administration + And the "Which tries" "select" should be enabled + And I set the field "Attempts from" to "enrolled users who have not attempted the quiz" + Then the "Which tries" "select" should be disabled diff --git a/question/engine/datalib.php b/question/engine/datalib.php index e486e4699ee..0ef5816aba6 100644 --- a/question/engine/datalib.php +++ b/question/engine/datalib.php @@ -525,15 +525,11 @@ ORDER BY qas.sequencenumber ", $qubaids->usage_id_in_params()); - if (!$records->valid()) { - throw new coding_exception('Failed to load questions_usages_by_activity for qubaid_condition :' . $qubaids); - } - $qubas = array(); - do { + while ($records->valid()) { $record = $records->current(); $qubas[$record->qubaid] = question_usage_by_activity::load_from_records($records, $record->qubaid); - } while ($records->valid()); + } $records->close(); diff --git a/question/type/numerical/tests/helper.php b/question/type/numerical/tests/helper.php index 7157dfb3460..650674b423c 100644 --- a/question/type/numerical/tests/helper.php +++ b/question/type/numerical/tests/helper.php @@ -35,7 +35,7 @@ defined('MOODLE_INTERNAL') || die(); */ class qtype_numerical_test_helper extends question_test_helper { public function get_test_questions() { - return array('pi', 'unit', 'currency'); + return array('pi', 'unit', 'currency', 'pi3tries'); } /** @@ -71,6 +71,15 @@ class qtype_numerical_test_helper extends question_test_helper { return $num; } + /** + * Get the form data that corresponds to saving a numerical question. + * + * This question asks for Pi to two decimal places. It has feedback + * for various wrong responses. There is hint data there, but + * it is all blank, so no hints are created if this question is saved. + * + * @return stdClass simulated question form data. + */ public function get_numerical_question_form_data_pi() { $form = new stdClass(); $form->name = 'Pi to two d.p.'; @@ -156,6 +165,22 @@ class qtype_numerical_test_helper extends question_test_helper { return $form; } + /** + * Get the form data that corresponds to saving a numerical question. + * + * Like {@link get_numerical_question_form_data_pi()}, but + * this time with two hints, making this suitable for use + * with the Interactive with multiple tries behaviour. + * + * @return stdClass simulated question form data. + */ + public function get_numerical_question_form_data_pi3tries() { + $form = $this->get_numerical_question_form_data_pi(); + $form->hint[0]['text'] = 'First hint'; + $form->hint[1]['text'] = 'Second hint'; + return $form; + } + public function get_numerical_question_data_pi() { $q = new stdClass(); $q->name = 'Pi to two d.p.'; From 261af24e6100c98b5d184053ca32b11400e6be84 Mon Sep 17 00:00:00 2001 From: David Castro Date: Thu, 26 Oct 2017 14:55:06 -0500 Subject: [PATCH 08/20] MDL-60605 assignment: Added some styles to fix spacing in YUI file tree --- mod/assign/styles.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mod/assign/styles.css b/mod/assign/styles.css index 087102ee8d5..11b64bbfa14 100644 --- a/mod/assign/styles.css +++ b/mod/assign/styles.css @@ -1218,3 +1218,10 @@ position: inherit; } /** End of base fixes **/ + +/** Fix to YUI tree (which is a table) when displayed within grading table. **/ +.path-mod-assign table.generaltable table td.ygtvcell { + border: 0; + padding: 0; +} +/** End of YUI tree fix **/ From 438da5c13b69750bf9305e2fbf19b612921a10bc Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Mon, 30 Oct 2017 13:09:42 +0800 Subject: [PATCH 09/20] MDL-57560 boost: File upload progress Copy / adapt some styles from the bootstrap progress widget to match the DOM used for file upload progress. Bootstrap changed this alot in the current beta so I opted to do a minimal change (affect the scss for boost only), rather than adapt the HTML to something that will change again anyway when we upgrade to the release version of bootstrap 4. --- theme/boost/scss/moodle/course.scss | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/theme/boost/scss/moodle/course.scss b/theme/boost/scss/moodle/course.scss index 8a1475cc1f8..d4f04d9ef6f 100644 --- a/theme/boost/scss/moodle/course.scss +++ b/theme/boost/scss/moodle/course.scss @@ -538,13 +538,20 @@ span.editinstructions { } .dndupload-progress-outer { - @extend progress; + background-color: $progress-bg; + @include border-radius($progress-border-radius); + @include box-shadow($progress-box-shadow); + display: block; + width: 100%; + height: $spacer-y; + margin-bottom: $spacer-y; } .dndupload-progress-inner { - .progress { - @extend .progress-bar; - } + background-color: $progress-bar-color; + @include border-left-radius($progress-border-radius); + display: inline-block; + height: $spacer-y; } .dndupload-hidden { From 966c52eb3df138c0fa11b32f852c5d1cbc20f56e Mon Sep 17 00:00:00 2001 From: Kathrin Osswald Date: Fri, 27 Oct 2017 12:06:20 +0200 Subject: [PATCH 10/20] MDL-60522 message: Removed additional tooltips I removed the additional tooltips because I think that they are just used in the notifications and messages context and duplicate the all over used title tag that gives the same information when hovering. So the whole system stays more consistently. --- .../popup/templates/message_popover.mustache | 40 +++++++------------ .../templates/notification_popover.mustache | 34 ++++++---------- 2 files changed, 26 insertions(+), 48 deletions(-) diff --git a/message/output/popup/templates/message_popover.mustache b/message/output/popup/templates/message_popover.mustache index 666242a875f..03559938f51 100644 --- a/message/output/popup/templates/message_popover.mustache +++ b/message/output/popup/templates/message_popover.mustache @@ -53,33 +53,21 @@ {{$headertext}}{{#str}} messages, message {{/str}}{{/headertext}} {{$headeractions}} - {{< core/hover_tooltip }} - {{$anchor}} - - {{#pix}} t/markasread, core, {{#str}} markallread {{/str}} {{/pix}} - {{> core/loading }} - - {{/anchor}} - {{$tooltip}}{{#str}} markallread {{/str}}{{/tooltip}} - {{/ core/hover_tooltip }} - {{< core/hover_tooltip }} - {{$anchor}} - - {{#pix}} i/settings, core, {{#str}} messagepreferences, message {{/str}} {{/pix}} - - {{/anchor}} - {{$tooltip}}{{#str}} messagepreferences, message {{/str}}{{/tooltip}} - {{/ core/hover_tooltip }} + + {{#pix}} t/markasread, core, {{#str}} markallread {{/str}} {{/pix}} + {{> core/loading }} + + + {{#pix}} i/settings, core, {{#str}} messagepreferences, message {{/str}} {{/pix}} + {{/headeractions}} {{$content}} diff --git a/message/output/popup/templates/notification_popover.mustache b/message/output/popup/templates/notification_popover.mustache index b0684ed3c08..97efbff1638 100644 --- a/message/output/popup/templates/notification_popover.mustache +++ b/message/output/popup/templates/notification_popover.mustache @@ -52,28 +52,18 @@ {{$headertext}}{{#str}} notifications, message {{/str}}{{/headertext}} {{$headeractions}} - {{< core/hover_tooltip }} - {{$anchor}} - - {{#pix}} t/markasread, core, {{#str}} markallread {{/str}} {{/pix}} - {{> core/loading }} - - {{/anchor}} - {{$tooltip}}{{#str}} markallread {{/str}}{{/tooltip}} - {{/ core/hover_tooltip }} - {{< core/hover_tooltip }} - {{$anchor}} - - {{#pix}} i/settings, core, {{#str}} notificationpreferences, message {{/str}} {{/pix}} - - {{/anchor}} - {{$tooltip}}{{#str}} notificationpreferences, message {{/str}}{{/tooltip}} - {{/ core/hover_tooltip }} + + {{#pix}} t/markasread, core, {{#str}} markallread {{/str}} {{/pix}} + {{> core/loading }} + + + {{#pix}} i/settings, core, {{#str}} notificationpreferences, message {{/str}} {{/pix}} + {{/headeractions}} {{$content}} From 263528b0d8180ea7bec8a133daed91a08cf8b064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luca=20B=C3=B6sch?= Date: Sun, 29 Oct 2017 23:23:40 +0000 Subject: [PATCH 11/20] =?UTF-8?q?MDL-52037=20questions:=20=E2=80=9EWhether?= =?UTF-8?q?=20correct=E2=80=9C-feedback=20marks=20adaptive=20width?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- theme/boost/scss/moodle/question.scss | 4 ++++ theme/bootstrapbase/less/moodle/question.less | 3 +++ theme/bootstrapbase/style/moodle.css | 3 +++ 3 files changed, 10 insertions(+) diff --git a/theme/boost/scss/moodle/question.scss b/theme/boost/scss/moodle/question.scss index c306f044440..a28ab91c5bc 100644 --- a/theme/boost/scss/moodle/question.scss +++ b/theme/boost/scss/moodle/question.scss @@ -304,6 +304,10 @@ body.path-question-type { vertical-align: baseline; } +.que.multianswer .formulation .yui3-widget-positioned { + box-sizing: content-box; +} + .path-mod-quiz input[size] { width: auto; max-width: 100%; diff --git a/theme/bootstrapbase/less/moodle/question.less b/theme/bootstrapbase/less/moodle/question.less index afbcdfd644b..055f7aa715a 100644 --- a/theme/bootstrapbase/less/moodle/question.less +++ b/theme/bootstrapbase/less/moodle/question.less @@ -237,6 +237,9 @@ body.path-question-type .fitem_fgroup .accesshide { width: auto; vertical-align: baseline; } +.que.multianswer .formulation .yui3-widget-positioned { + box-sizing: content-box; +} .path-mod-quiz input[size] { width: auto; } diff --git a/theme/bootstrapbase/style/moodle.css b/theme/bootstrapbase/style/moodle.css index 60db968ead7..ec9bcbd6778 100644 --- a/theme/bootstrapbase/style/moodle.css +++ b/theme/bootstrapbase/style/moodle.css @@ -6962,6 +6962,9 @@ body.path-question-type .fitem_fgroup .accesshide { width: auto; vertical-align: baseline; } +.que.multianswer .formulation .yui3-widget-positioned { + box-sizing: content-box; +} .path-mod-quiz input[size] { width: auto; } From 48dbf892529ec38b48cd4cab5cac77acc226331c Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Thu, 26 Oct 2017 18:36:35 +0100 Subject: [PATCH 12/20] MDL-60139 phpunit: Make assertTag verify attribute values with === I had a test that cared about the difference between value="5.0" and value="5". --- lib/phpunit/classes/base_testcase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/phpunit/classes/base_testcase.php b/lib/phpunit/classes/base_testcase.php index 231ce1f4198..fcb8786eaa4 100644 --- a/lib/phpunit/classes/base_testcase.php +++ b/lib/phpunit/classes/base_testcase.php @@ -242,7 +242,7 @@ abstract class base_testcase extends PHPUnit_Framework_TestCase { } } // match by exact string else { - if ($node->getAttribute($name) != $value) { + if ($node->getAttribute($name) !== (string) $value) { $invalid = true; } } From 78e92b3fb171a6486fffe8149dcf71439a835da1 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Thu, 26 Oct 2017 18:37:11 +0100 Subject: [PATCH 13/20] MDL-60139 question manual grading: redisplay mark as typed if editing When the teacher is upgrading a previously entered grade, we re-display exactly what they typed before if possible, rather than displaying with a set number of decimal places. --- .../manualgraded/tests/walkthrough_test.php | 39 ++++++++++++++++++- question/engine/questionattempt.php | 24 +++++++++--- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/question/behaviour/manualgraded/tests/walkthrough_test.php b/question/behaviour/manualgraded/tests/walkthrough_test.php index d63030c9248..c47e2053e1b 100644 --- a/question/behaviour/manualgraded/tests/walkthrough_test.php +++ b/question/behaviour/manualgraded/tests/walkthrough_test.php @@ -18,8 +18,7 @@ * This file contains tests that walks a question through the manual graded * behaviour. * - * @package qbehaviour - * @subpackage manualgraded + * @package qbehaviour_manualgraded * @copyright 2009 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -622,4 +621,40 @@ class qbehaviour_manualgraded_walkthrough_testcase extends qbehaviour_walkthroug new question_pattern_expectation($preg) ); } + + public function test_manual_grading_reshows_exactly_the_mark_input() { + global $PAGE; + + // The current text editor depends on the users profile setting - so it needs a valid user. + $this->setAdminUser(); + // Required to init a text editor. + $PAGE->set_url('/'); + + // Create an essay question graded out of 15 and attempt it. + $essay = test_question_maker::make_an_essay_question(); + $this->start_attempt_at_question($essay, 'deferredfeedback', 15); + $this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML)); + $this->quba->finish_all_questions(); + + // Verify. + $this->check_current_state(question_state::$needsgrading); + $this->check_current_mark(null); + $this->assertEquals('This is my wonderful essay!', + $this->quba->get_response_summary($this->slot)); + + // Try to process a grade where the score will be stored rounded. + $this->manual_grade('Comment', '5.0', FORMAT_HTML); + + // Verify. + $this->check_current_state(question_state::$mangrpartial); + $this->check_current_mark(5); + $this->displayoptions->manualcomment = question_display_options::EDITABLE; + $this->render(); + $this->check_output_contains_text_input('-mark', '5.0'); + + // Rescale what the question is worth, and verify the display. + $this->get_question_attempt()->set_max_mark(1); + $this->render(); + $this->check_output_contains_text_input('-mark', '0.3333333'); + } } diff --git a/question/engine/questionattempt.php b/question/engine/questionattempt.php index fa110dde2ae..d295170e3d3 100644 --- a/question/engine/questionattempt.php +++ b/question/engine/questionattempt.php @@ -649,17 +649,29 @@ class question_attempt { /** * This is used by the manual grading code, particularly in association with - * validation. If there is a mark submitted in the request, then use that, - * otherwise use the latest mark for this question. - * @return number the current manual mark for this question, formatted for display. + * validation. It gets the current manual mark for a question, in exactly the string + * form that the teacher entered it, if possible. This may come from the current + * POST request, if there is one, otherwise from the database. + * + * @return string the current manual mark for this question, in the format the teacher typed, + * if possible. */ public function get_current_manual_mark() { + // Is there a current value in the current POST data? If so, use that. $mark = $this->get_submitted_var($this->get_behaviour_field_name('mark'), PARAM_RAW_TRIMMED); - if (is_null($mark)) { - return format_float($this->get_mark(), 7, true, true); - } else { + if ($mark !== null) { return $mark; } + + // Otherwise, use the stored value. + // If the question max mark has not changed, use the stored value that was input. + $storedmaxmark = $this->get_last_behaviour_var('maxmark'); + if ($storedmaxmark !== null && ($storedmaxmark - $this->get_max_mark()) < 0.0000005) { + return $this->get_last_behaviour_var('mark'); + } + + // The max mark for this question has changed so we must re-scale the current mark. + return format_float($this->get_mark(), 7, true, true); } /** From 828a24f784dbcf50303975ae8171b80a2f5248c6 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Thu, 26 Oct 2017 19:00:46 +0100 Subject: [PATCH 14/20] MDL-60139 question manual grading: move code to reduce confusion Move the variable definition closer to where it is used. --- question/behaviour/rendererbase.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/question/behaviour/rendererbase.php b/question/behaviour/rendererbase.php index ea6a1878afe..b47488726e5 100644 --- a/question/behaviour/rendererbase.php +++ b/question/behaviour/rendererbase.php @@ -126,9 +126,6 @@ abstract class qbehaviour_renderer extends plugin_renderer_base { if (!is_null($currentmark)) { $attributes['value'] = $currentmark; } - $a = new stdClass(); - $a->max = $qa->format_max_mark($options->markdp); - $a->mark = html_writer::empty_tag('input', $attributes); $markrange = html_writer::empty_tag('input', array( 'type' => 'hidden', @@ -152,6 +149,9 @@ abstract class qbehaviour_renderer extends plugin_renderer_base { array('class' => 'error')) . html_writer::empty_tag('br'); } + $a = new stdClass(); + $a->max = $qa->format_max_mark($options->markdp); + $a->mark = html_writer::empty_tag('input', $attributes); $mark = html_writer::tag('div', html_writer::tag('div', html_writer::tag('label', get_string('mark', 'question'), array('for' => $markfield)), From c860ca5c91793aa2e987320721bc91c5f70c0d95 Mon Sep 17 00:00:00 2001 From: Michael Aherne Date: Fri, 20 Oct 2017 14:13:19 +0100 Subject: [PATCH 15/20] MDL-60528 grading: Accurate class names in PHPDoc --- grade/grading/lib.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grade/grading/lib.php b/grade/grading/lib.php index 46fd52a83c0..d7c82946a18 100644 --- a/grade/grading/lib.php +++ b/grade/grading/lib.php @@ -489,7 +489,7 @@ class grading_manager { * Returns the given method's controller in the gradable area * * @param string $method the method name, eg 'rubric' (must be available) - * @return grading_controller + * @return gradingform_controller */ public function get_controller($method) { global $CFG, $DB; @@ -534,7 +534,7 @@ class grading_manager { /** * Returns the controller for the active method if it is available * - * @return null|grading_controller + * @return null|gradingform_controller */ public function get_active_controller() { if ($gradingmethod = $this->get_active_method()) { From a0b2649a1699b2c55e78157b94ac14d6466c9ae7 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Mon, 30 Oct 2017 13:34:15 +0000 Subject: [PATCH 16/20] MDL-60607 quiz reports: move duplicate code to superclass method --- mod/quiz/report/attemptsreport.php | 40 ++++++++++++++++++++++++++++ mod/quiz/report/overview/report.php | 31 +++------------------ mod/quiz/report/responses/report.php | 31 +++------------------ 3 files changed, 48 insertions(+), 54 deletions(-) diff --git a/mod/quiz/report/attemptsreport.php b/mod/quiz/report/attemptsreport.php index 4545a7099f5..7e82e545f28 100644 --- a/mod/quiz/report/attemptsreport.php +++ b/mod/quiz/report/attemptsreport.php @@ -138,6 +138,46 @@ abstract class quiz_attempts_report extends quiz_default_report { return array($currentgroup, $studentsjoins, $groupstudentsjoins, $groupstudentsjoins); } + /** + * Outputs the things you commonly want at the top of a quiz report. + * + * Calls through to {@link print_header_and_tabs()} and then + * outputs the standard group selector, number of attempts summary, + * and messages to cover common cases when the report can't be shown. + * + * @param stdClass $cm the course_module information. + * @param stdClass $coures the course settings. + * @param stdClass $quiz the quiz settings. + * @param mod_quiz_attempts_report_options $options the current report settings. + * @param int $currentgroup the current group. + * @param bool $hasquestions whether there are any questions in the quiz. + * @param bool $hasstudents whether there are any relevant students. + */ + protected function print_standard_header_and_messages($cm, $course, $quiz, + $options, $currentgroup, $hasquestions, $hasstudents) { + global $OUTPUT; + + $this->print_header_and_tabs($cm, $course, $quiz, $this->mode); + + if (groups_get_activity_groupmode($cm)) { + // Groups are being used, so output the group selector if we are not downloading. + groups_print_activity_menu($cm, $options->get_url()); + } + + // Print information on the number of existing attempts. + if ($strattemptnum = quiz_num_attempt_summary($quiz, $cm, true, $currentgroup)) { + echo '
' . $strattemptnum . '
'; + } + + if (!$hasquestions) { + echo quiz_no_questions_message($quiz, $cm, $this->context); + } else if (!$hasstudents) { + echo $OUTPUT->notification(get_string('nostudentsyet')); + } else if ($currentgroup && !$this->hasgroupstudents) { + echo $OUTPUT->notification(get_string('nostudentsingroup')); + } + } + /** * Add all the user-related columns to the $columns and $headers arrays. * @param table_sql $table the table being constructed. diff --git a/mod/quiz/report/overview/report.php b/mod/quiz/report/overview/report.php index d5d47bd07d0..e5c0e932836 100644 --- a/mod/quiz/report/overview/report.php +++ b/mod/quiz/report/overview/report.php @@ -98,36 +98,13 @@ class quiz_overview_report extends quiz_attempts_report { $this->course = $course; // Hack to make this available in process_actions. $this->process_actions($quiz, $cm, $currentgroup, $groupstudentsjoins, $allowedjoins, $options->get_url()); + $hasquestions = quiz_has_questions($quiz->id); + // Start output. if (!$table->is_downloading()) { // Only print headers if not asked to download data. - $this->print_header_and_tabs($cm, $course, $quiz, $this->mode); - } - - if ($groupmode = groups_get_activity_groupmode($cm)) { - // Groups are being used, so output the group selector if we are not downloading. - if (!$table->is_downloading()) { - groups_print_activity_menu($cm, $options->get_url()); - } - } - - // Print information on the number of existing attempts. - if (!$table->is_downloading()) { - // Do not print notices when downloading. - if ($strattemptnum = quiz_num_attempt_summary($quiz, $cm, true, $currentgroup)) { - echo '
' . $strattemptnum . '
'; - } - } - - $hasquestions = quiz_has_questions($quiz->id); - if (!$table->is_downloading()) { - if (!$hasquestions) { - echo quiz_no_questions_message($quiz, $cm, $this->context); - } else if (!$hasstudents) { - echo $OUTPUT->notification(get_string('nostudentsyet')); - } else if ($currentgroup && !$this->hasgroupstudents) { - echo $OUTPUT->notification(get_string('nostudentsingroup')); - } + $this->print_standard_header_and_messages($cm, $course, $quiz, + $options, $currentgroup, $hasquestions, $hasstudents); // Print the display options. $this->form->display(); diff --git a/mod/quiz/report/responses/report.php b/mod/quiz/report/responses/report.php index f72fdbbdfd9..693029eeb6e 100644 --- a/mod/quiz/report/responses/report.php +++ b/mod/quiz/report/responses/report.php @@ -111,36 +111,13 @@ class quiz_responses_report extends quiz_attempts_report { $this->process_actions($quiz, $cm, $currentgroup, $groupstudentsjoins, $allowedjoins, $options->get_url()); + $hasquestions = quiz_has_questions($quiz->id); + // Start output. if (!$table->is_downloading()) { // Only print headers if not asked to download data. - $this->print_header_and_tabs($cm, $course, $quiz, $this->mode); - } - - if ($groupmode = groups_get_activity_groupmode($cm)) { - // Groups are being used, so output the group selector if we are not downloading. - if (!$table->is_downloading()) { - groups_print_activity_menu($cm, $options->get_url()); - } - } - - // Print information on the number of existing attempts. - if (!$table->is_downloading()) { - // Do not print notices when downloading. - if ($strattemptnum = quiz_num_attempt_summary($quiz, $cm, true, $currentgroup)) { - echo '
' . $strattemptnum . '
'; - } - } - - $hasquestions = quiz_has_questions($quiz->id); - if (!$table->is_downloading()) { - if (!$hasquestions) { - echo quiz_no_questions_message($quiz, $cm, $this->context); - } else if (!$hasstudents) { - echo $OUTPUT->notification(get_string('nostudentsyet')); - } else if ($currentgroup && !$this->hasgroupstudents) { - echo $OUTPUT->notification(get_string('nostudentsingroup')); - } + $this->print_standard_header_and_messages($cm, $course, $quiz, + $options, $currentgroup, $hasquestions, $hasstudents); // Print the display options. $this->form->display(); From b18cde34506296c74599ffecbe638fb9e0361860 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Mon, 30 Oct 2017 13:41:56 +0000 Subject: [PATCH 17/20] MDL-60607 quiz reports: catch separate groups & user has no group case --- mod/quiz/report/attemptsreport.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mod/quiz/report/attemptsreport.php b/mod/quiz/report/attemptsreport.php index 7e82e545f28..06614ebc858 100644 --- a/mod/quiz/report/attemptsreport.php +++ b/mod/quiz/report/attemptsreport.php @@ -146,7 +146,7 @@ abstract class quiz_attempts_report extends quiz_default_report { * and messages to cover common cases when the report can't be shown. * * @param stdClass $cm the course_module information. - * @param stdClass $coures the course settings. + * @param stdClass $course the course settings. * @param stdClass $quiz the quiz settings. * @param mod_quiz_attempts_report_options $options the current report settings. * @param int $currentgroup the current group. @@ -171,6 +171,8 @@ abstract class quiz_attempts_report extends quiz_default_report { if (!$hasquestions) { echo quiz_no_questions_message($quiz, $cm, $this->context); + } else if ($currentgroup == self::NO_GROUPS_ALLOWED) { + echo $OUTPUT->notification(get_string('notingroup')); } else if (!$hasstudents) { echo $OUTPUT->notification(get_string('nostudentsyet')); } else if ($currentgroup && !$this->hasgroupstudents) { From 042dd6335d3e9fb431e5ea0d2ccfd03aef280359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luca=20B=C3=B6sch?= Date: Tue, 31 Oct 2017 09:33:24 +0000 Subject: [PATCH 18/20] =?UTF-8?q?MDL-52037=20questions:=20=E2=80=9EWhether?= =?UTF-8?q?=20correct=E2=80=9C-feedback=20marks=20adaptive=20width?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- theme/bootstrapbase/less/moodle/question.less | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/theme/bootstrapbase/less/moodle/question.less b/theme/bootstrapbase/less/moodle/question.less index 055f7aa715a..603ba09df75 100644 --- a/theme/bootstrapbase/less/moodle/question.less +++ b/theme/bootstrapbase/less/moodle/question.less @@ -237,8 +237,9 @@ body.path-question-type .fitem_fgroup .accesshide { width: auto; vertical-align: baseline; } -.que.multianswer .formulation .yui3-widget-positioned { +.que.multianswer .formulation .yui3-widget-positioned .feedbackspan { box-sizing: content-box; + padding-bottom: 0; } .path-mod-quiz input[size] { width: auto; From a7f16bb296cae22420f306a9031ddd6745da8de7 Mon Sep 17 00:00:00 2001 From: David Monllao Date: Tue, 31 Oct 2017 11:03:55 +0100 Subject: [PATCH 19/20] MDL-52037 theme: Update compiled css --- theme/bootstrapbase/style/moodle.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/theme/bootstrapbase/style/moodle.css b/theme/bootstrapbase/style/moodle.css index ec9bcbd6778..c0a30f0960b 100644 --- a/theme/bootstrapbase/style/moodle.css +++ b/theme/bootstrapbase/style/moodle.css @@ -6962,8 +6962,9 @@ body.path-question-type .fitem_fgroup .accesshide { width: auto; vertical-align: baseline; } -.que.multianswer .formulation .yui3-widget-positioned { +.que.multianswer .formulation .yui3-widget-positioned .feedbackspan { box-sizing: content-box; + padding-bottom: 0; } .path-mod-quiz input[size] { width: auto; From baab74b9d339aa9bb81e6b50e31979d8d8a9fc10 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Wed, 1 Nov 2017 10:22:32 +0800 Subject: [PATCH 20/20] MDL-52037 theme: Correct question feedback width This relates to the cloze question answer when hovered. --- theme/boost/scss/moodle/question.scss | 4 ++++ theme/bootstrapbase/less/moodle/question.less | 2 ++ theme/bootstrapbase/style/moodle.css | 2 ++ 3 files changed, 8 insertions(+) diff --git a/theme/boost/scss/moodle/question.scss b/theme/boost/scss/moodle/question.scss index a28ab91c5bc..e92f97a6bf7 100644 --- a/theme/boost/scss/moodle/question.scss +++ b/theme/boost/scss/moodle/question.scss @@ -306,6 +306,10 @@ body.path-question-type { .que.multianswer .formulation .yui3-widget-positioned { box-sizing: content-box; + .feedbackspan { + width: inherit; + max-width: inherit; + } } .path-mod-quiz input[size] { diff --git a/theme/bootstrapbase/less/moodle/question.less b/theme/bootstrapbase/less/moodle/question.less index 603ba09df75..a6bb21cf8a3 100644 --- a/theme/bootstrapbase/less/moodle/question.less +++ b/theme/bootstrapbase/less/moodle/question.less @@ -240,6 +240,8 @@ body.path-question-type .fitem_fgroup .accesshide { .que.multianswer .formulation .yui3-widget-positioned .feedbackspan { box-sizing: content-box; padding-bottom: 0; + max-width: inherit; + width: inherit; } .path-mod-quiz input[size] { width: auto; diff --git a/theme/bootstrapbase/style/moodle.css b/theme/bootstrapbase/style/moodle.css index c0a30f0960b..77fc4f353be 100644 --- a/theme/bootstrapbase/style/moodle.css +++ b/theme/bootstrapbase/style/moodle.css @@ -6965,6 +6965,8 @@ body.path-question-type .fitem_fgroup .accesshide { .que.multianswer .formulation .yui3-widget-positioned .feedbackspan { box-sizing: content-box; padding-bottom: 0; + max-width: inherit; + width: inherit; } .path-mod-quiz input[size] { width: auto;