Merge branch 'MDL-69136-master' of git://github.com/andrewnicols/moodle
authorJake Dallimore <jake@moodle.com>
Mon, 6 Jul 2020 02:44:10 +0000 (10:44 +0800)
committerJake Dallimore <jake@moodle.com>
Mon, 6 Jul 2020 02:44:10 +0000 (10:44 +0800)
50 files changed:
admin/cli/install.php
admin/tasklogs.php
admin/tool/customlang/lang/en/tool_customlang.php
config-dist.php
course/amd/build/activitychooser.min.js
course/amd/build/activitychooser.min.js.map
course/amd/src/activitychooser.js
enrol/imsenterprise/lang/en/enrol_imsenterprise.php
grade/grading/classes/privacy/gradingform_legacy_polyfill.php
grade/grading/classes/privacy/gradingform_provider.php [deleted file]
grade/grading/classes/privacy/provider.php
grade/grading/form/upgrade.txt
grade/grading/tests/privacy_legacy_polyfill_test.php
grade/tests/behat/behat_grade.php
install.php
install/lang/ko/moodle.php
install/lang/zh_cn/langconfig.php
lang/en/badges.php
lang/en/h5p.php
lang/en/install.php
lang/en/mnet.php
lib/amd/build/modal.min.js
lib/amd/build/modal.min.js.map
lib/amd/build/modal_factory.min.js
lib/amd/build/modal_factory.min.js.map
lib/amd/src/modal.js
lib/amd/src/modal_factory.js
lib/classes/event/message_contact_blocked.php [deleted file]
lib/classes/event/message_contact_unblocked.php [deleted file]
lib/clilib.php
lib/cronlib.php
lib/db/install.xml
lib/db/upgrade.php
lib/dml/auroramysql_native_moodle_database.php [new file with mode: 0644]
lib/editor/atto/plugins/image/lang/en/atto_image.php
lib/tests/behat/behat_hooks.php
lib/upgrade.txt
message/output/airnotifier/requestaccesskey.php
message/output/airnotifier/settings.php
mod/choice/lang/en/choice.php
mod/quiz/autosave.ajax.php
mod/quiz/module.js
mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-debug.js
mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-min.js
mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave.js
mod/quiz/yui/src/autosave/js/autosave.js
mod/scorm/tests/behat/behat_mod_scorm.php [deleted file]
question/export_form.php
report/competency/index.php
version.php

index cfc8ea0..5c68d52 100644 (file)
@@ -215,6 +215,7 @@ define('SITEID', 1);
 
 //Database types
 $databases = array('mysqli' => moodle_database::get_driver_instance('mysqli', 'native'),
+                   'auroramysql' => moodle_database::get_driver_instance('auroramysql', 'native'),
                    'mariadb'=> moodle_database::get_driver_instance('mariadb', 'native'),
                    'pgsql'  => moodle_database::get_driver_instance('pgsql',  'native'),
                    'oci'    => moodle_database::get_driver_instance('oci',    'native'),
index 796f323..a91faa3 100644 (file)
@@ -66,7 +66,7 @@ echo $OUTPUT->header();
 // Output the search form.
 echo $OUTPUT->render_from_template('core_admin/tasklogs', (object) [
     'action' => $pageurl->out(),
-    'filter' => $filter,
+    'filter' => htmlentities($filter),
     'resultfilteroptions' => [
         (object) [
             'value' => -1,
index 9e7a2d6..a7375f1 100644 (file)
@@ -36,7 +36,7 @@ $string['customlang:view'] = 'View local translation';
 $string['filter'] = 'Filter strings';
 $string['filtercomponent'] = 'Show strings of these components';
 $string['filtercustomized'] = 'Customised only';
-$string['filtermodified'] = 'Modified only';
+$string['filtermodified'] = 'Modified in this session only';
 $string['filteronlyhelps'] = 'Help only';
 $string['filtershowstrings'] = 'Show strings';
 $string['filterstringid'] = 'String identifier';
index 995e0df..ecd56b6 100644 (file)
@@ -898,13 +898,6 @@ $CFG->admin = 'admin';
 //     ),
 // );
 //
-// You can force the browser session (not user's sessions) to restart after N seconds. This could
-// be useful if you are using a cloud-based service with time restrictions in the browser side.
-// Setting this value the browser session that Behat is using will be restarted. Set the time in
-// seconds. Is not recommended to use this setting if you don't explicitly need it.
-// Example:
-//   $CFG->behat_restart_browser_after = 7200;     // Restarts the browser session after 2 hours
-//
 // All this page's extra Moodle settings are compared against a white list of allowed settings
 // (the basic and behat_* ones) to avoid problems with production environments. This setting can be
 // used to expand the default white list with an array of extra settings.
index b49f3dc..0ea080e 100644 (file)
Binary files a/course/amd/build/activitychooser.min.js and b/course/amd/build/activitychooser.min.js differ
index a409f76..6519c83 100644 (file)
Binary files a/course/amd/build/activitychooser.min.js.map and b/course/amd/build/activitychooser.min.js.map differ
index 5de0576..a1d1bbc 100644 (file)
@@ -248,6 +248,7 @@ const buildModal = (bodyPromise, footer) => {
         body: bodyPromise,
         footer: footer.customfootertemplate,
         large: true,
+        scrollable: false,
         templateContext: {
             classes: 'modchooser'
         }
index f1c3cdb..d9c1ada 100644 (file)
@@ -32,7 +32,7 @@ $string['categoryseparator'] = 'Category separator character';
 $string['categoryseparator_desc'] = 'Required when "Category idnumber" is enabled. Character to separate the category name and idnumber.';
 $string['coursesettings'] = 'Course data options';
 $string['createnewcategories'] = 'Create new (hidden) course categories if not found in Moodle';
-$string['createnewcategories_desc'] = 'If the <org><orgunit> element is present in a course\'s incoming data, its content will be used to specify a category if the course is to be created from scratch. The plugin will NOT re-categorise existing courses.
+$string['createnewcategories_desc'] = 'If the &lt;org&gt;&lt;orgunit&gt; element is present in a course\'s incoming data, its content will be used to specify a category if the course is to be created from scratch. The plugin will NOT re-categorise existing courses.
 
 If no category exists with the desired name, then a hidden category will be created.';
 $string['createnewcourses'] = 'Create new (hidden) courses if not found in Moodle';
index 5973857..b6f24a3 100644 (file)
@@ -53,48 +53,4 @@ trait gradingform_legacy_polyfill {
     public static function delete_gradingform_for_instances(array $instanceids) {
         static::_delete_gradingform_for_instances($instanceids);
     }
-
-    /**
-     * This method is used to export any user data this sub-plugin has using the object to get the context and userid.
-     *
-     * @deprecated Since Moodle 3.6 MDL-62535 Please use the methods in the gradingform_provider_v2 interface.
-     * @todo MDL-63167 remove this method.
-     *
-     * @param context $context Context owner of the data.
-     * @param stdClass $definition Grading definition entry to export.
-     * @param int $userid The user whose information is to be exported.
-     *
-     * @return stdClass The data to export.
-     */
-    public static function get_gradingform_export_data(\context $context, $definition, int $userid) {
-        debugging('This method is deprecated. Please use the gradingform_provider_v2 interface', DEBUG_DEVELOPER);
-        return static::_get_gradingform_export_data($context, $definition, $userid);
-    }
-
-    /**
-     * Any call to this method should delete all user data for the context defined.
-     *
-     * @deprecated Since Moodle 3.6 MDL-62535 Please use the methods in the gradingform_provider_v2 interface.
-     * @todo MDL-63167 remove this method.
-     *
-     * @param context $context Context owner of the data.
-     */
-    public static function delete_gradingform_for_context(\context $context) {
-        debugging('This method is deprecated. Please use the gradingform_provider_v2 interface', DEBUG_DEVELOPER);
-        static::_delete_gradingform_for_context($context);
-    }
-
-    /**
-     * A call to this method should delete user data (where practicle) from the userid and context.
-     *
-     * @deprecated Since Moodle 3.6 MDL-62535 Please use the methods in the gradingform_provider_v2 interface.
-     * @todo MDL-63167 remove this method.
-     *
-     * @param int $userid The user whose information is to be deleted.
-     * @param context $context Context owner of the data.
-     */
-    public static function delete_gradingform_for_userid(int $userid, \context $context) {
-        debugging('This method is deprecated. Please use the gradingform_provider_v2 interface', DEBUG_DEVELOPER);
-        static::_delete_gradingform_for_userid($userid, $context);
-    }
 }
diff --git a/grade/grading/classes/privacy/gradingform_provider.php b/grade/grading/classes/privacy/gradingform_provider.php
deleted file mode 100644 (file)
index 1602956..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * This file contains the grading method interface.
- *
- * Grading method plugins should implement this if they store personal information.
- *
- * @deprecated since Moodle 3.6 MDL-62535 Please use the gradingform_provider_v2 interface
- * @todo MDL-63167 Remove this file.
- *
- * @package    core_grading
- * @copyright  2018 Sara Arjona <sara@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_grading\privacy;
-
-defined('MOODLE_INTERNAL') || die();
-
-use core_privacy\local\request\approved_contextlist;
-
-interface gradingform_provider extends
-    \core_privacy\local\request\plugin\subsystem_provider,
-    \core_privacy\local\deprecated {
-
-    /**
-     * This method is used to export any user data this sub-plugin has using the object to get the context and userid.
-     *
-     * @deprecated since Moodle 3.6 MDL-62535 Please use the methods in the gradingform_provider_v2 interface
-     * @todo MDL-63167 Remove this file.
-     *
-     * @param \context $context Context owner of the data.
-     * @param \stdClass $definition Grading definition entry to export.
-     * @param  int $userid The user whose information is to be exported.
-     *
-     * @return \stdClass The data to export.
-     */
-    public static function get_gradingform_export_data(\context $context, $definition, int $userid);
-
-    /**
-     * Any call to this method should delete all user data for the context defined.
-     *
-     * @deprecated since Moodle 3.6 MDL-62535 Please use the methods in the gradingform_provider_v2 interface
-     * @todo MDL-63167 Remove this file.
-     *
-     * @param \context $context Context owner of the data.
-     */
-    public static function delete_gradingform_for_context(\context $context);
-
-    /**
-     * A call to this method should delete user data (where practicle) from the userid and context.
-     *
-     * @deprecated since Moodle 3.6 MDL-62535 Please use the methods in the gradingform_provider_v2 interface
-     * @todo MDL-63167 Remove this file.
-     *
-     * @param int $userid The user to delete.
-     * @param \context $context the context to refine the deletion.
-     */
-    public static function delete_gradingform_for_userid(int $userid, \context $context);
-}
index 54eb9ed..646c050 100644 (file)
@@ -304,19 +304,6 @@ class provider implements
                 $tmpdata['timecopied'] = transform::datetime($definition->timecopied);
             }
 
-            // MDL-63167 - This section is to be removed with the final deprecation of the gradingform_provider interface.
-            // Export gradingform information (if needed).
-            $instancedata = manager::component_class_callback(
-                "gradingform_{$definition->method}",
-                gradingform_provider::class,
-                'get_gradingform_export_data',
-                [$context, $definition, $userid]
-            );
-            if (null !== $instancedata) {
-                $tmpdata = array_merge($tmpdata, $instancedata);
-            }
-            // End of section to be removed with deprecation.
-
             $defdata[] = (object) $tmpdata;
 
             // Export grading_instances information.
@@ -378,14 +365,7 @@ class provider implements
      * @param \context $context the context to delete in.
      */
     public static function delete_data_for_all_users_in_context(\context $context) {
-        // MDL-63167 - This section is to be removed with the final deprecation of the gradingform_provider interface.
-        manager::plugintype_class_callback(
-            'gradingform',
-            gradingform_provider::class,
-            'delete_gradingform_for_context',
-            [$context]
-        );
-        // End of section to be removed for final deprecation.
+        // The only information left to be deleted here is the grading definitions. Currently we are not deleting these.
     }
 
     /**
@@ -395,14 +375,7 @@ class provider implements
      * @param approved_contextlist $contextlist a list of contexts approved for deletion.
      */
     public static function delete_data_for_user(approved_contextlist $contextlist) {
-        // MDL-63167 - This section is to be removed with the final deprecation of the gradingform_provider interface.
-        manager::plugintype_class_callback(
-            'gradingform',
-            gradingform_provider::class,
-            'delete_gradingform_for_userid',
-            [$contextlist]
-        );
-        // End of section to be removed for final deprecation.
+        // The only information left to be deleted here is the grading definitions. Currently we are not deleting these.
     }
 
     /**
index 0bca314..7a8be2d 100644 (file)
@@ -1,6 +1,14 @@
 This files describes API changes in /grade/grading/form/* - Advanced grading methods
 information provided here is intended especially for developers.
 
+=== 4.0 ===
+
+* Removed gradingform_provider.
+* Removed the following deprecated functions:
+    get_gradingform_export_data
+    delete_gradingform_for_context
+    delete_gradingform_for_userid
+
 === 3.6 ===
 
 * The privacy interface gradingform_provider has been deprecated. Please use
index 63465c8..c424d57 100644 (file)
@@ -69,56 +69,6 @@ class gradeform_privacy_legacy_polyfill_test extends advanced_testcase {
         test_legacy_polyfill_gradingform_provider::$mock = $mock;
         test_legacy_polyfill_gradingform_provider::delete_gradingform_for_instances([3, 17]);
     }
-
-    /**
-     * Test the __get_gradingform_export_data shim.
-     */
-    public function test_get_gradingform_export_data() {
-        $userid = 476;
-        $context = context_system::instance();
-
-        $mock = $this->createMock(test_gradingform_legacy_polyfill_mock_wrapper::class);
-        $mock->expects($this->once())
-            ->method('get_return_value')
-            ->with('_get_gradingform_export_data', [$context, (object)[], $userid]);
-
-        test_legacy_polyfill_gradingform_provider::$mock = $mock;
-        test_legacy_polyfill_gradingform_provider::get_gradingform_export_data($context, (object)[], $userid);
-        $this->assertDebuggingCalled();
-    }
-
-    /**
-     * Test the _delete_gradingform_for_context shim.
-     */
-    public function test_delete_gradingform_for_context() {
-        $context = context_system::instance();
-
-        $mock = $this->createMock(test_gradingform_legacy_polyfill_mock_wrapper::class);
-        $mock->expects($this->once())
-            ->method('get_return_value')
-            ->with('_delete_gradingform_for_context', [$context]);
-
-        test_legacy_polyfill_gradingform_provider::$mock = $mock;
-        test_legacy_polyfill_gradingform_provider::delete_gradingform_for_context($context);
-        $this->assertDebuggingCalled();
-    }
-
-    /**
-     * Test the _delete_gradingform_for_userid shim.
-     */
-    public function test_delete_gradingform_for_user() {
-        $userid = 696;
-        $context = \context_system::instance();
-
-        $mock = $this->createMock(test_gradingform_legacy_polyfill_mock_wrapper::class);
-        $mock->expects($this->once())
-            ->method('get_return_value')
-            ->with('_delete_gradingform_for_userid', [$userid, $context]);
-
-        test_legacy_polyfill_gradingform_provider::$mock = $mock;
-        test_legacy_polyfill_gradingform_provider::delete_gradingform_for_userid($userid, $context);
-        $this->assertDebuggingCalled();
-    }
 }
 
 /**
@@ -129,7 +79,6 @@ class gradeform_privacy_legacy_polyfill_test extends advanced_testcase {
  */
 class test_legacy_polyfill_gradingform_provider implements
     \core_privacy\local\metadata\provider,
-    \core_grading\privacy\gradingform_provider,
     \core_grading\privacy\gradingform_provider_v2 {
 
     use \core_grading\privacy\gradingform_legacy_polyfill;
@@ -169,47 +118,6 @@ class test_legacy_polyfill_gradingform_provider implements
     protected static function _get_metadata(\core_privacy\local\metadata\collection $collection) {
         return $collection;
     }
-
-    /**
-     * This method is used to export any user data this sub-plugin has using the object to get the context and userid.
-     *
-     * @deprecated Since Moodle 3.6 MDL-62535 Please use the methods in the gradingform_provider_v2 interface.
-     * @todo MDL-63167 remove this method.
-     *
-     * @param context $context Context owner of the data.
-     * @param stdClass $definition Grading definition entry to export.
-     * @param int $userid The user whose information is to be exported.
-     *
-     * @return stdClass The data to export.
-     */
-    protected static function _get_gradingform_export_data(\context $context, $definition, int $userid) {
-        static::$mock->get_return_value(__FUNCTION__, func_get_args());
-    }
-
-    /**
-     * Any call to this method should delete all user data for the context defined.
-     *
-     * @deprecated Since Moodle 3.6 MDL-62535 Please use the methods in the gradingform_provider_v2 interface.
-     * @todo MDL-63167 remove this method.
-     *
-     * @param context $context Context owner of the data.
-     */
-    protected static function _delete_gradingform_for_context(\context $context) {
-        static::$mock->get_return_value(__FUNCTION__, func_get_args());
-    }
-
-    /**
-     * A call to this method should delete user data (where practicle) from the userid and context.
-     *
-     * @deprecated Since Moodle 3.6 MDL-62535 Please use the methods in the gradingform_provider_v2 interface.
-     * @todo MDL-63167 remove this method.
-     *
-     * @param int $userid The user whose information is to be deleted.
-     * @param context $context Context owner of the data.
-     */
-    protected static function _delete_gradingform_for_userid(int $userid, \context $context) {
-        static::$mock->get_return_value(__FUNCTION__, func_get_args());
-    }
 }
 
 /**
index b5e6dde..02a4d1e 100644 (file)
@@ -226,7 +226,7 @@ class behat_grade extends behat_base {
             $inputxpath = "//input[@class='idnumber'][" .
                     "parent::li[@class='item'][text()='" . $gradeitem . "']" .
                     " | " .
-                    "parent::li[@class='categoryitem' | @class='courseitem']" .
+                    "parent::li[@class='categoryitem' or @class='courseitem']" .
                     "/parent::ul/parent::li[starts-with(text(),'" . $gradeitem . "')]" .
                     "]";
             $this->execute('behat_forms::i_set_the_field_with_xpath_to', array($inputxpath, $idnumber));
index 973f833..82c5e35 100644 (file)
@@ -493,6 +493,7 @@ if ($config->stage == INSTALL_DATABASETYPE) {
                                   get_string('databasetypesub', 'install'));
 
     $databases = array('mysqli' => moodle_database::get_driver_instance('mysqli', 'native'),
+                       'auroramysql' => moodle_database::get_driver_instance('auroramysql', 'native'),
                        'mariadb'=> moodle_database::get_driver_instance('mariadb', 'native'),
                        'pgsql'  => moodle_database::get_driver_instance('pgsql',  'native'),
                        'oci'    => moodle_database::get_driver_instance('oci',    'native'),
index 358febe..392abd3 100644 (file)
@@ -31,6 +31,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 $string['language'] = '언어';
+$string['moodlelogo'] = '무들 로고';
 $string['next'] = '다음';
 $string['previous'] = '이전으로';
 $string['reload'] = '다시 로딩';
index a0956b4..0bc12e9 100644 (file)
@@ -31,5 +31,5 @@
 defined('MOODLE_INTERNAL') || die();
 
 $string['parentlanguage'] = '';
-$string['thisdirection'] = '符号(ltr)';
+$string['thisdirection'] = 'ltr';
 $string['thislanguage'] = '简体中文';
index 85abc8a..f0343ee 100644 (file)
@@ -82,7 +82,7 @@ $string['awardoncron'] = 'Access to the badges was successfully enabled. Too man
 $string['awards'] = 'Recipients';
 $string['backpackavailability'] = 'External badge verification';
 $string['backpackconnectionok'] = 'Backpack connection successfully established';
-$string['backpackconnectionnottested'] = 'Connection can not be tested for this backpack because only OBv2.0 backpacks support it.';
+$string['backpackconnectionnottested'] = 'The connection cannot be tested for this backpack because only Open Badges v2.0 backpacks support it.';
 $string['backpackneedsupdate'] = 'The backpack connected to this profile does not match the backpack for the site. You need to disconnect and reconnect the backpack.';
 $string['backpackavailability_help'] = 'For badge recipients to be able to prove they earned their badges from you, an external backpack service should be able to access your site and verify badges issued from it. Your site does not currently appear to be accessible, which means that badges you have already issued or will issue in the future cannot be verified.
 
index f2d73f2..f4b6a8f 100644 (file)
@@ -92,7 +92,7 @@ $string['h5ptitle'] = 'Visit h5p.org to check out more content.';
 $string['h5pfilenotfound'] = 'H5P file not found';
 $string['h5pinvalidurl'] = 'Invalid H5P content URL.';
 $string['h5plibraryhandler'] = 'H5P framework handler';
-$string['h5plibraryhandler_help'] = 'The H5P framework used to display any H5P content.';
+$string['h5plibraryhandler_help'] = 'The H5P framework used to display H5P content. The latest version is recommended.';
 $string['h5pprivatefile'] = 'This H5P content can\'t be displayed because you don\'t have access to the .h5p file.';
 $string['h5pmanage'] = 'Manage H5P content types';
 $string['h5poverview'] = 'H5P overview';
index 047c1cb..681807f 100644 (file)
@@ -169,6 +169,12 @@ $string['memorylimithelp'] = '<p>The PHP memory limit for your server is current
     (you will see errors when you look at pages) so you\'ll have to remove the .htaccess file.</p></li>
 </ol>';
 $string['mysqliextensionisnotpresentinphp'] = 'PHP has not been properly configured with the MySQLi extension for it to communicate with MySQL. Please check your php.ini file or recompile PHP.';
+$string['nativeauroramysql'] = 'Aurora MySQL (native/auroramysql)';
+$string['nativeauroramysqlhelp'] = '<p>The database is where most of the Moodle settings and data are stored and must be configured here.</p>
+<p>The database name, username, and password are required fields; table prefix is optional.</p>
+<p>The database name may contain only alphanumeric characters, dollar ($) and underscore (_).</p>
+<p>If the database currently does not exist, and the user you specify has permission, Moodle will attempt to create a new database with the correct permissions and settings.</p>
+<p>This driver is not compatible with legacy MyISAM engine.</p>';
 $string['nativemariadb'] = 'MariaDB (native/mariadb)';
 $string['nativemariadbhelp'] = '<p>The database is where most of the Moodle settings and data are stored and must be configured here.</p>
 <p>The database name, username, and password are required fields; table prefix is optional.</p>
index bb57111..b73fd12 100644 (file)
@@ -61,7 +61,7 @@ $string['enterausername'] = 'Please enter a username, or a list of usernames sep
 $string['error7020'] = 'This error normally occurs if the remote site has created a record for you with the wrong wwwroot, for example, https://yoursite.com instead of https://www.yoursite.com. Please contact the administrator of the remote site with your wwwroot (as specified in config.php) and ask them to update the record for your host.';
 $string['error7022'] = 'The message you sent to the remote site was encrypted properly, but not signed. This is very unexpected; you should probably file a bug if this occurs (giving as much information as possible about the application versions in question etc).';
 $string['error7023'] = 'The remote site has tried to decrypt your message with all the keys it has on record for your site. They have all failed. You might be able to fix this problem by manually re-keying with the remote site. This is unlikely to occur unless you\'ve been out of communication with the remote site for a few months.';
-$string['error7024'] = 'You send an unencrypted message to the remote site, but the remote site doesn\'t accept unencrypted communication from your site. This is very unexpected; you should probably file a bug if this occurs (giving as much information as possible about the application versions in question, etc.';
+$string['error7024'] = 'You send an unencrypted message to the remote site, but the remote site doesn\'t accept unencrypted communication from your site. This is very unexpected; you should probably file a bug if this occurs (giving as much information as possible about the application versions in question, etc).';
 $string['error7026'] = 'The key that your message was signed with differs from the key that the remote host has on file for your server. Further, the remote host attempted to fetch your current key and failed to do so. Please manually re-key with the remote host and try again.';
 $string['error709'] = 'The remote site failed to obtain a SSL key from you.';
 $string['eventaccesscontrolcreated'] = 'Access control created';
index 1b3b1b4..69414dc 100644 (file)
Binary files a/lib/amd/build/modal.min.js and b/lib/amd/build/modal.min.js differ
index 6ebad58..19c89db 100644 (file)
Binary files a/lib/amd/build/modal.min.js.map and b/lib/amd/build/modal.min.js.map differ
index 00c5ea4..3e6d275 100644 (file)
Binary files a/lib/amd/build/modal_factory.min.js and b/lib/amd/build/modal_factory.min.js differ
index e994255..8b2bf04 100644 (file)
Binary files a/lib/amd/build/modal_factory.min.js.map and b/lib/amd/build/modal_factory.min.js.map differ
index d04924f..442c5b0 100644 (file)
@@ -553,6 +553,22 @@ define([
         return !this.getModal().hasClass('modal-lg');
     };
 
+    /**
+     * Set this modal to be scrollable or not.
+     *
+     * @method setScrollable
+     * @param {bool} value Whether the modal is scrollable or not
+     */
+    Modal.prototype.setScrollable = function(value) {
+        if (!value) {
+            this.getModal()[0].classList.remove('modal-dialog-scrollable');
+            return;
+        }
+
+        this.getModal()[0].classList.add('modal-dialog-scrollable');
+    };
+
+
     /**
      * Determine the highest z-index value currently on the page.
      *
index 1e27c2f..e96681a 100644 (file)
@@ -161,6 +161,8 @@ define(['jquery', 'core/modal_events', 'core/modal_registry', 'core/modal',
     var create = function(modalConfig, triggerElement) {
         var type = modalConfig.type || TYPES.DEFAULT;
         var isLarge = modalConfig.large ? true : false;
+        // If 'scrollable' is not configured, set the modal to be scrollable by default.
+        var isScrollable = modalConfig.hasOwnProperty('scrollable') ? modalConfig.scrollable : true;
         var registryConf = null;
         var templateContext = {};
 
@@ -203,6 +205,8 @@ define(['jquery', 'core/modal_events', 'core/modal_registry', 'core/modal',
                     modal.setRemoveOnClose(modalConfig.removeOnClose);
                 }
 
+                modal.setScrollable(isScrollable);
+
                 return modal;
             });
 
diff --git a/lib/classes/event/message_contact_blocked.php b/lib/classes/event/message_contact_blocked.php
deleted file mode 100644 (file)
index 1c207a3..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Message contact blocked event.
- *
- * @package    core
- * @copyright  2014 Mark Nelson <markn@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core\event;
-
-defined('MOODLE_INTERNAL') || die();
-
-debugging('core\\event\\message_contact_blocked has been deprecated. Please use
-        core\\event\\message_user_blocked instead', DEBUG_DEVELOPER);
-
-/**
- * Message contact blocked event class.
- *
- * @package    core
- * @since      Moodle 2.7
- * @copyright  2014 Mark Nelson <markn@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class message_contact_blocked extends base {
-
-    /**
-     * Init method.
-     */
-    protected function init() {
-        $this->data['objecttable'] = 'message_contacts';
-        $this->data['crud'] = 'u';
-        $this->data['edulevel'] = self::LEVEL_OTHER;
-    }
-
-    /**
-     * Returns localised general event name.
-     *
-     * @return string
-     */
-    public static function get_name() {
-        return get_string('eventmessagecontactblocked', 'message');
-    }
-
-    /**
-     * Returns relevant URL.
-     *
-     * @return \moodle_url
-     */
-    public function get_url() {
-        return new \moodle_url('/message/index.php', array('user1' => $this->userid, 'user2' => $this->relateduserid));
-    }
-
-    /**
-     * Returns description of what happened.
-     *
-     * @return string
-     */
-    public function get_description() {
-        return "The user with id '$this->userid' blocked the user with id '$this->relateduserid' on their contact list.";
-    }
-
-    /**
-     * Return legacy data for add_to_log().
-     *
-     * @return array
-     */
-    protected function get_legacy_logdata() {
-        return array(SITEID, 'message', 'block contact', 'index.php?user1=' . $this->relateduserid . '&amp;user2=' .
-            $this->userid, $this->relateduserid);
-    }
-
-    /**
-     * Custom validation.
-     *
-     * @throws \coding_exception
-     */
-    protected function validate_data() {
-        parent::validate_data();
-
-        if (!isset($this->relateduserid)) {
-            throw new \coding_exception('The \'relateduserid\' must be set.');
-        }
-    }
-
-    public static function get_objectid_mapping() {
-        // Messaging contacts are not backed up, so no need to map them on restore.
-        return array('db' => 'message_contacts', 'restore' => base::NOT_MAPPED);
-    }
-
-    /**
-     * This event has been deprecated.
-     *
-     * @return boolean
-     */
-    public static function is_deprecated() {
-        return true;
-    }
-}
diff --git a/lib/classes/event/message_contact_unblocked.php b/lib/classes/event/message_contact_unblocked.php
deleted file mode 100644 (file)
index 61f83e6..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Message contact unblocked event.
- *
- * @package    core
- * @copyright  2014 Mark Nelson <markn@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core\event;
-
-defined('MOODLE_INTERNAL') || die();
-
-debugging('core\\event\\message_contact_unblocked has been deperecated. Please use
-        core\\event\\message_user_unblocked instead', DEBUG_DEVELOPER);
-
-/**
- * Message contact unblocked event class.
- *
- * @package    core
- * @since      Moodle 2.7
- * @copyright  2014 Mark Nelson <markn@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class message_contact_unblocked extends base {
-
-    /**
-     * Init method.
-     */
-    protected function init() {
-        $this->data['objecttable'] = 'message_contacts';
-        $this->data['crud'] = 'u';
-        $this->data['edulevel'] = self::LEVEL_OTHER;
-    }
-
-    /**
-     * Returns localised general event name.
-     *
-     * @return string
-     */
-    public static function get_name() {
-        return get_string('eventmessagecontactunblocked', 'message');
-    }
-
-    /**
-     * Returns relevant URL.
-     *
-     * @return \moodle_url
-     */
-    public function get_url() {
-        return new \moodle_url('/message/index.php', array('user1' => $this->userid, 'user2' => $this->relateduserid));
-    }
-
-    /**
-     * Returns description of what happened.
-     *
-     * @return string
-     */
-    public function get_description() {
-        return "The user with id '$this->userid' unblocked the user with id '$this->relateduserid' on their contact list.";
-    }
-
-    /**
-     * Return legacy data for add_to_log().
-     *
-     * @return array
-     */
-    protected function get_legacy_logdata() {
-        return array(SITEID, 'message', 'unblock contact', 'index.php?user1=' . $this->relateduserid .
-            '&amp;user2=' . $this->userid, $this->relateduserid);
-    }
-
-    /**
-     * Custom validation.
-     *
-     * @throws \coding_exception
-     */
-    protected function validate_data() {
-        parent::validate_data();
-
-        if (!isset($this->relateduserid)) {
-            throw new \coding_exception('The \'relateduserid\' must be set.');
-        }
-    }
-
-    public static function get_objectid_mapping() {
-        // Messaging contacts are not backed up, so no need to map them on restore.
-        return array('db' => 'message_contacts', 'restore' => base::NOT_MAPPED);
-    }
-
-    /**
-     * This event has been deprecated.
-     *
-     * @return boolean
-     */
-    public static function is_deprecated() {
-        return true;
-    }
-}
index 63b5e43..6cc767f 100644 (file)
@@ -145,6 +145,21 @@ function cli_get_params(array $longoptions, array $shortmapping=null) {
     return array($options, $unrecognized);
 }
 
+/**
+ * This sets the cli process title suffix
+ *
+ * An example is appending current Task API info so a sysadmin can immediately
+ * see what task a cron process is running at any given moment.
+ *
+ * @param string $suffix process suffix
+ */
+function cli_set_process_title_suffix(string $suffix) {
+    if (CLI_SCRIPT && function_exists('cli_set_process_title') && isset($_SERVER['argv'])) {
+        $command = join(' ', $_SERVER['argv']);
+        @cli_set_process_title("php $command ($suffix)");
+    }
+}
+
 /**
  * Print or return section separator string
  * @param bool $return false means print, true return as string
index 88c7463..74a83d5 100644 (file)
@@ -196,10 +196,12 @@ function cron_run_adhoc_tasks(int $timenow, $keepalive = 0, $checklimits = true)
             }
             $waiting = false;
             cron_run_inner_adhoc_task($task);
+            cron_set_process_title("Waiting for next adhoc task");
             $taskcount++;
             unset($task);
         } else {
-            if (time() >= $finishtime) {
+            $timeleft = $finishtime - time();
+            if ($timeleft <= 0) {
                 break;
             }
             if (!$waiting) {
@@ -208,6 +210,7 @@ function cron_run_adhoc_tasks(int $timenow, $keepalive = 0, $checklimits = true)
                 mtrace('.', '');
             }
             $waiting = true;
+            cron_set_process_title("Waiting {$timeleft}s for next adhoc task");
             sleep(1);
         }
     }
@@ -238,6 +241,7 @@ function cron_run_inner_scheduled_task(\core\task\task_base $task) {
 
     $fullname = $task->get_name() . ' (' . get_class($task) . ')';
     mtrace('Execute scheduled task: ' . $fullname);
+    cron_set_process_title('Scheduled task: ' . get_class($task));
     cron_trace_time_and_memory();
     $predbqueries = null;
     $predbqueries = $DB->perf_get_queries();
@@ -277,6 +281,7 @@ function cron_run_inner_scheduled_task(\core\task\task_base $task) {
     } finally {
         // Reset back to the standard admin user.
         cron_setup_user();
+        cron_set_process_title('Waiting for next scheduled task');
         cron_prepare_core_renderer(true);
     }
     get_mailer('close');
@@ -293,6 +298,7 @@ function cron_run_inner_adhoc_task(\core\task\adhoc_task $task) {
     \core\task\logmanager::start_logging($task);
 
     mtrace("Execute adhoc task: " . get_class($task));
+    cron_set_process_title('Adhoc task: ' . $task->get_id() . ' ' . get_class($task));
     cron_trace_time_and_memory();
     $predbqueries = null;
     $predbqueries = $DB->perf_get_queries();
@@ -367,6 +373,23 @@ function cron_run_inner_adhoc_task(\core\task\adhoc_task $task) {
     get_mailer('close');
 }
 
+/**
+ * Sets the process title
+ *
+ * This makes it very easy for a sysadmin to immediately see what task
+ * a cron process is running at any given moment.
+ *
+ * @param string $title process status title
+ */
+function cron_set_process_title(string $title) {
+    global $CFG;
+    if (CLI_SCRIPT) {
+        require_once($CFG->libdir . '/clilib.php');
+        $datetime = userdate(time(), '%b %d, %H:%M:%S');
+        cli_set_process_title_suffix("$datetime $title");
+    }
+}
+
 /**
  * Output some standard information during cron runs. Specifically current time
  * and memory usage. This method also does gc_collect_cycles() (before displaying
index 8f91417..0e0c4f0 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20200504" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20200625" COMMENT="XMLDB file for core Moodle tables"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
 >
         <KEY NAME="contextid" TYPE="foreign" FIELDS="contextid" REFTABLE="context" REFFIELDS="id"/>
         <KEY NAME="creatorid" TYPE="foreign" FIELDS="creatorid" REFTABLE="user" REFFIELDS="id"/>
       </KEYS>
+      <INDEXES>
+        <INDEX NAME="token" UNIQUE="false" FIELDS="token" COMMENT="This index will be used anytime a token is validated."/>
+      </INDEXES>
     </TABLE>
     <TABLE NAME="blog_association" COMMENT="Associations of blog entries with courses and module instances">
       <FIELDS>
index dcc2f48..977d7df 100644 (file)
@@ -2486,5 +2486,17 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2020061500.02);
     }
 
+    if ($oldversion < 2020062600.01) {
+        // Add index to the token field in the external_tokens table.
+        $table = new xmldb_table('external_tokens');
+        $index = new xmldb_index('token', XMLDB_INDEX_NOTUNIQUE, ['token']);
+
+        if (!$dbman->index_exists($table, $index)) {
+            $dbman->add_index($table, $index);
+        }
+
+        upgrade_main_savepoint(true, 2020062600.01);
+    }
+
     return true;
 }
diff --git a/lib/dml/auroramysql_native_moodle_database.php b/lib/dml/auroramysql_native_moodle_database.php
new file mode 100644 (file)
index 0000000..3000913
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Native Aurora MySQL class representing moodle database interface.
+ *
+ * @package    core_dml
+ * @copyright  2020 Lafayette College ITS
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once(__DIR__.'/moodle_database.php');
+require_once(__DIR__.'/mysqli_native_moodle_database.php');
+require_once(__DIR__.'/mysqli_native_moodle_recordset.php');
+require_once(__DIR__.'/mysqli_native_moodle_temptables.php');
+
+/**
+ * Native Aurora MySQL class representing moodle database interface.
+ *
+ * @package    core_dml
+ * @copyright  2020 Lafayette College ITS
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class auroramysql_native_moodle_database extends mysqli_native_moodle_database {
+
+    /** @var bool is compressed row format supported cache */
+    protected $compressedrowformatsupported = false;
+
+    /**
+     * Returns localised database type name.
+     *
+     * Returns localised database type name. Can be used before connect().
+     * @return string
+     */
+    public function get_name(): ?string {
+        return get_string('nativeauroramysql', 'install');
+    }
+
+    /**
+     * Returns localised database configuration help.
+     *
+     * Returns localised database configuration help. Can be used before connect().
+     * @return string
+     */
+    public function get_configuration_help(): ?string {
+        return get_string('nativeauroramysql', 'install');
+    }
+
+    /**
+     * Returns the database vendor.
+     *
+     * Returns the database vendor. Can be used before connect().
+     * @return string The db vendor name, usually the same as db family name.
+     */
+    public function get_dbvendor(): ?string {
+        return 'mysql';
+    }
+
+    /**
+     * Returns more specific database driver type
+     *
+     * Returns more specific database driver type. Can be used before connect().
+     * @return string db type mysqli, pgsql, oci, mssql, sqlsrv
+     */
+    protected function get_dbtype(): ?string {
+        return 'auroramysql';
+    }
+
+    /**
+     * It is time to require transactions everywhere.
+     *
+     * MyISAM is NOT supported!
+     *
+     * @return bool
+     */
+    protected function transactions_supported(): ?bool {
+        if ($this->external) {
+            return parent::transactions_supported();
+        }
+        return true;
+    }
+
+
+}
index 624247e..b5e540d 100644 (file)
@@ -38,7 +38,7 @@ $string['height'] = 'Height';
 $string['imageproperties'] = 'Image properties';
 $string['presentation'] = 'This image is decorative only';
 $string['pluginname'] = 'Insert or edit image';
-$string['presentationoraltrequired'] = 'Images must have a description, except if the description is marked as not necessary.';
+$string['presentationoraltrequired'] = 'An image must have a description, unless it is marked as decorative only.';
 $string['preview'] = 'Preview';
 $string['saveimage'] = 'Save image';
 $string['size'] = 'Size';
index 115765a..2ba7b37 100644 (file)
@@ -63,11 +63,6 @@ use Behat\Testwork\Hook\Scope\BeforeSuiteScope,
  */
 class behat_hooks extends behat_base {
 
-    /**
-     * @var Last browser session start time.
-     */
-    protected static $lastbrowsersessionstart = 0;
-
     /**
      * @var For actions that should only run once.
      */
@@ -196,12 +191,6 @@ class behat_hooks extends behat_base {
         // Avoid parallel tests execution, it continues when the previous lock is released.
         test_lock::acquire('behat');
 
-        // Store the browser reset time if reset after N seconds is specified in config.php.
-        if (!empty($CFG->behat_restart_browser_after)) {
-            // Store the initial browser session opening.
-            self::$lastbrowsersessionstart = time();
-        }
-
         if (!empty($CFG->behat_faildump_path) && !is_writable($CFG->behat_faildump_path)) {
             throw new behat_stop_exception('You set $CFG->behat_faildump_path to a non-writable directory');
         }
@@ -381,15 +370,6 @@ class behat_hooks extends behat_base {
         $user = $DB->get_record('user', array('username' => 'admin'));
         \core\session\manager::set_user($user);
 
-        // Reset the browser if specified in config.php.
-        if (!empty($CFG->behat_restart_browser_after) && $this->running_javascript()) {
-            $now = time();
-            if (self::$lastbrowsersessionstart + $CFG->behat_restart_browser_after < $now) {
-                $session->restart();
-                self::$lastbrowsersessionstart = $now;
-            }
-        }
-
         // Set the theme if not default.
         if ($suitename !== "default") {
             set_config('theme', $suitename);
@@ -576,25 +556,13 @@ class behat_hooks extends behat_base {
     }
 
     /**
-     * Executed after scenario having switch window to restart session.
-     * This is needed to close all extra browser windows and starting
-     * one browser window.
+     * Reset the session between each scenario.
      *
      * @param AfterScenarioScope $scope scope passed by event fired after scenario.
-     * @AfterScenario @_switch_window
+     * @AfterScenario
      */
-    public function after_scenario_switchwindow(AfterScenarioScope $scope) {
-        for ($count = 0; $count < behat_base::get_extended_timeout(); $count++) {
-            try {
-                $this->getSession()->restart();
-                break;
-            } catch (DriverException $e) {
-                // Wait for timeout and try again.
-                sleep(self::get_timeout());
-            }
-        }
-        // If session is not restarted above then it will try to start session before next scenario
-        // and if that fails then exception will be thrown.
+    public function reset_webdriver_between_scenarios(AfterScenarioScope $scope) {
+        $this->getSession()->stop();
     }
 
     /**
index 4e79b93..fd1f97d 100644 (file)
@@ -1,6 +1,14 @@
 This files describes API changes in core libraries and APIs,
 information provided here is intended especially for developers.
 
+=== 4.0 ===
+* Added function setScrollable in core/modal. This function can be used to set the modal's body to be scrollable or not
+  when the modal's height exceeds the browser's height. This is also supported in core/modal_factory through the
+  'scrollable' config parameter which can be set to either true or false. If not explicitly defined, the default value
+  of 'scrollable' is true.
+* The `$CFG->behat_retart_browser_after` configuration setting has been removed.
+  The browser session is now restarted between all tests.
+
 === 3.9 ===
 * Following function has been deprecated, please use \core\task\manager::run_from_cli().
     - cron_run_single_task()
index 90f397c..fe33a55 100644 (file)
@@ -59,6 +59,8 @@ if (strpos($CFG->airnotifierurl, message_airnotifier_manager::AIRNOTIFIER_PUBLIC
     }
 }
 
+echo $OUTPUT->header();
+
 $manager = new message_airnotifier_manager();
 $warnings = [];
 
@@ -107,6 +109,5 @@ foreach ($warnings as $warning) {
 
 $msg .= $OUTPUT->continue_button($returl);
 
-echo $OUTPUT->header();
 echo $OUTPUT->box($msg, 'generalbox ');
 echo $OUTPUT->footer();
index 558c6ae..91766bc 100644 (file)
@@ -26,7 +26,8 @@ defined('MOODLE_INTERNAL') || die;
 if ($ADMIN->fulltree) {
 
     $notify = new \core\output\notification(
-        get_string('moodleappsportallimitswarning', 'message_airnotifier'),
+        get_string('moodleappsportallimitswarning', 'message_airnotifier',
+            (new moodle_url('https://apps.moodle.com'))->out()),
         \core\output\notification::NOTIFY_WARNING);
     $settings->add(new admin_setting_heading('tool_mobile/moodleappsportalfeaturesappearance', '', $OUTPUT->render($notify)));
 
index 0288cb1..a56b121 100644 (file)
@@ -88,7 +88,7 @@ $string['limitanswers'] = 'Limit the number of responses allowed';
 $string['modulename'] = 'Choice';
 $string['modulename_help'] = 'The choice activity module enables a teacher to ask a single question and offer a selection of possible responses.
 
-Choice results may be published after students have answered, after a certain date, or not at all. Results may be published with student names or anonymously.
+Choice results may be published after students have answered, after a certain date, or not at all. Results may be published with student names or anonymously (though teachers always see student names and their responses).
 
 A choice activity may be used
 
index ebf3499..c2f3817 100644 (file)
@@ -61,4 +61,15 @@ if ($attemptobj->is_finished()) {
 
 $attemptobj->process_auto_save($timenow);
 $transaction->allow_commit();
-echo 'OK';
+
+// Calculate time remaining.
+$timeleft = $attemptobj->get_time_left_display($timenow);
+
+// Build response, only returning timeleft if quiz in-progress
+// has a time limit.
+$r = new stdClass();
+$r->status = "OK";
+if ($timeleft !== false) {
+    $r->timeleft = $timeleft;
+}
+echo json_encode($r);
index 46f5ab8..9ede921 100644 (file)
@@ -58,6 +58,9 @@ M.mod_quiz.timer = {
     // so we can cancel.
     timeoutid: null,
 
+    // Threshold for updating time remaining, in milliseconds.
+    threshold: 3000,
+
     /**
      * @param Y the YUI object
      * @param start, the timer starting time, in seconds.
@@ -130,6 +133,18 @@ M.mod_quiz.timer = {
 
         // Arrange for this method to be called again soon.
         M.mod_quiz.timer.timeoutid = setTimeout(M.mod_quiz.timer.update, 100);
+    },
+
+    // Allow the end time of the quiz to be updated.
+    updateEndTime: function(timeleft) {
+        var newtimeleft = new Date().getTime() + timeleft * 1000;
+
+        // Only update if change is greater than the threshold, so the
+        // time doesn't bounce around unnecessarily.
+        if (Math.abs(newtimeleft - M.mod_quiz.timer.endtime) > M.mod_quiz.timer.threshold) {
+            M.mod_quiz.timer.endtime = newtimeleft;
+            M.mod_quiz.timer.update();
+        }
     }
 };
 
index 99e5e2d..403fb96 100644 (file)
Binary files a/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-debug.js and b/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-debug.js differ
index 6d79032..393c2d9 100644 (file)
Binary files a/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-min.js and b/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-min.js differ
index 21abfc5..674d946 100644 (file)
Binary files a/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave.js and b/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave.js differ
index cce5017..9633f73 100644 (file)
@@ -356,13 +356,19 @@ M.mod_quiz.autosave = {
     },
 
     save_done: function(transactionid, response) {
-        if (response.responseText !== 'OK') {
+        var autosavedata = JSON.parse(response.responseText);
+        if (autosavedata.status !== 'OK') {
             // Because IIS is useless, Moodle can't send proper HTTP response
             // codes, so we have to detect failures manually.
             this.save_failed(transactionid, response);
             return;
         }
 
+        if (typeof autosavedata.timeleft !== 'undefined') {
+            Y.log('Updating timer: ' + autosavedata.timeleft + ' seconds remain.', 'debug', 'moodle-mod_quiz-timer');
+            M.mod_quiz.timer.updateEndTime(autosavedata.timeleft);
+        }
+
         Y.log('Save completed.', 'debug', 'moodle-mod_quiz-autosave');
         this.save_transaction = null;
 
diff --git a/mod/scorm/tests/behat/behat_mod_scorm.php b/mod/scorm/tests/behat/behat_mod_scorm.php
deleted file mode 100644 (file)
index ed7e16f..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Steps definitions related to the SCORM activity module.
- *
- * @package    mod_scorm
- * @category   test
- * @copyright  2019 Andrew Nicols <andrew@nicols.co.uk>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
-
-require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
-
-use Behat\Behat\Hook\Scope\AfterScenarioScope;
-
-/**
- * Steps definitions related to the SCORM activity module.
- *
- * @package    mod_scorm
- * @category   test
- * @copyright  2019 Andrew Nicols <andrew@nicols.co.uk>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class behat_mod_scorm extends behat_base {
-
-    /**
-     * Restart the Seleium Session after each mod_scorm Scenario.
-     *
-     * This prevents issues with the scorm player's onbeforeunload event, and cached SCORM content being served to the
-     * browser in subsequent tests.
-     *
-     * @AfterScenario @mod_scorm
-     * @param AfterScenarioScope $scope The scenario scope
-     */
-    public function reset_after_scorm(AfterScenarioScope $scope) {
-        $this->getSession()->stop();
-    }
-}
index 9528995..0d5ade3 100644 (file)
@@ -58,7 +58,7 @@ class question_export_form extends moodleform {
             if (get_string_manager()->string_exists('pluginname_help', 'qformat_' . $shortname)) {
                 $separator .= $OUTPUT->help_icon('pluginname', 'qformat_' . $shortname);
             }
-            $separator .= '<br>';
+            $separator .= '<div class="w-100"></div>';
             $separators[] = $separator;
         }
 
index 6c1fc25..ccf0eb8 100644 (file)
@@ -76,7 +76,7 @@ $output = $PAGE->get_renderer('report_competency');
 echo $output->header();
 $baseurl = new moodle_url('/report/competency/index.php');
 $nav = new \report_competency\output\user_course_navigation($currentuser, $course->id, $baseurl, $currentmodule);
-echo $output->render($nav);
+$top = $output->render($nav);
 if ($currentuser > 0) {
     $user = core_user::get_user($currentuser);
     $usercontext = context_user::instance($currentuser);
@@ -88,9 +88,9 @@ if ($currentuser > 0) {
     if ($currentmodule > 0) {
         $title = get_string('filtermodule', 'report_competency', format_string($cm->name));
     }
-    echo $output->context_header($userheading, 3);
+    $top .= $output->context_header($userheading, 3);
 }
-echo $output->container('', 'clearfix');
+echo $output->container($top, 'clearfix');
 echo $output->heading($title, 3);
 
 if ($currentuser > 0) {
index 104a139..002a769 100644 (file)
@@ -29,9 +29,9 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2020062600.00;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2020070400.00;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.
-$release  = '4.0dev (Build: 20200626)'; // Human-friendly version name
+$release  = '4.0dev (Build: 20200704)'; // Human-friendly version name
 $branch   = '40';                       // This version's branch.
 $maturity = MATURITY_ALPHA;             // This version's maturity level.