Merge branch 'MDL-70059-master' of git://github.com/sarjona/moodle
authorAndrew Nicols <andrew@nicols.co.uk>
Tue, 3 Nov 2020 04:03:49 +0000 (12:03 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Tue, 3 Nov 2020 04:03:49 +0000 (12:03 +0800)
15 files changed:
admin/tool/xmldb/actions/XMLDBCheckAction.class.php
course/amd/build/downloadcontent.min.js
course/amd/build/downloadcontent.min.js.map
course/amd/src/downloadcontent.js
course/classes/output/content_export_link.php
lib/amd/build/tree.min.js
lib/amd/build/tree.min.js.map
lib/amd/src/tree.js
lib/db/upgrade.php
lib/dml/mysqli_native_moodle_database.php
lib/dml/tests/dml_test.php
lib/moodlelib.php
lib/setuplib.php
message/output/airnotifier/classes/manager.php
mod/lti/openid-configuration.php

index 2514c61..5ffc70e 100644 (file)
@@ -149,7 +149,7 @@ abstract class XMLDBCheckAction extends XMLDBAction {
                                 continue;
                             }
                             // Fetch metadata from physical DB. All the columns info.
-                            if (!$metacolumns = $DB->get_columns($xmldb_table->getName())) {
+                            if (!$metacolumns = $DB->get_columns($xmldb_table->getName(), false)) {
                                 // / Skip table if no metacolumns is available for it
                                 continue;
                             }
index 8e99320..901c7c8 100644 (file)
Binary files a/course/amd/build/downloadcontent.min.js and b/course/amd/build/downloadcontent.min.js differ
index 29cc63d..b6c5863 100644 (file)
Binary files a/course/amd/build/downloadcontent.min.js.map and b/course/amd/build/downloadcontent.min.js.map differ
index 67a6d83..2c5899a 100644 (file)
@@ -27,6 +27,7 @@ import CustomEvents from 'core/custom_interaction_events';
 import * as ModalFactory from 'core/modal_factory';
 import jQuery from 'jquery';
 import Pending from 'core/pending';
+import {enter, space} from 'core/key_codes';
 
 /**
  * Set up listener to trigger the download course content modal.
@@ -36,12 +37,11 @@ import Pending from 'core/pending';
 export const init = () => {
     const pendingPromise = new Pending();
 
-    document.addEventListener('click', (e) => {
-        const downloadModalTrigger = e.target.closest('[data-downloadcourse]');
-
-        if (downloadModalTrigger) {
+    // Add event listeners for click and enter/space keys.
+    jQuery('[data-downloadcourse]').on('click keydown', (e) => {
+        if (e.type === 'click' || e.which === enter || e.which === space) {
             e.preventDefault();
-            displayDownloadConfirmation(downloadModalTrigger);
+            displayDownloadConfirmation(e.currentTarget);
         }
     });
 
index 6b3deea..cfa667a 100644 (file)
@@ -53,6 +53,7 @@ class content_export_link {
             'data-download-button-text' => get_string('download'),
             'data-download-link' => $downloadlink->out(false),
             'data-download-title' => get_string('downloadcoursecontent', 'course'),
+            'data-overrides-tree-activation-key-handler' => 1,
         ];
 
         return $downloadattr;
index d3f99e0..d92786b 100644 (file)
Binary files a/lib/amd/build/tree.min.js and b/lib/amd/build/tree.min.js differ
index 9ff2c7d..3eed2c1 100644 (file)
Binary files a/lib/amd/build/tree.min.js.map and b/lib/amd/build/tree.min.js.map differ
index af051da..4ba8615 100644 (file)
@@ -351,7 +351,6 @@ define(['jquery'], function($) {
      * @method handleKeyDown
      * @param {Object} item is the jquery id of the parent item of the group.
      * @param {Event} e The event.
-     * @return {Boolean}
      */
      // This function should be simplified. In the meantime..
      // eslint-disable-next-line complexity
@@ -360,7 +359,7 @@ define(['jquery'], function($) {
 
         if ((e.altKey || e.ctrlKey || e.metaKey) || (e.shiftKey && e.keyCode != this.keys.tab)) {
             // Do nothing.
-            return true;
+            return;
         }
 
         switch (e.keyCode) {
@@ -368,21 +367,24 @@ define(['jquery'], function($) {
                 // Jump to first item in tree.
                 this.getVisibleItems().first().focus();
 
-                e.stopPropagation();
-                return false;
+                e.preventDefault();
+                return;
             }
             case this.keys.end: {
                 // Jump to last visible item.
                 this.getVisibleItems().last().focus();
 
-                e.stopPropagation();
-                return false;
+                e.preventDefault();
+                return;
             }
             case this.keys.enter: {
                 var links = item.children('a').length ? item.children('a') : item.children().not(SELECTORS.GROUP).find('a');
                 if (links.length) {
-                    // See if we have a callback.
-                    if (typeof this.enterCallback === 'function') {
+                    if (links.first().data('overrides-tree-activation-key-handler')) {
+                        // If the link overrides handling of activation keys, let it do so.
+                        links.first().triggerHandler(e);
+                    } else if (typeof this.enterCallback === 'function') {
+                        // Use callback if there is one.
                         this.enterCallback(item);
                     } else {
                         window.location.href = links.first().attr('href');
@@ -391,16 +393,22 @@ define(['jquery'], function($) {
                     this.toggleGroup(item, true);
                 }
 
-                e.stopPropagation();
-                return false;
+                e.preventDefault();
+                return;
             }
             case this.keys.space: {
                 if (this.isGroupItem(item)) {
                     this.toggleGroup(item, true);
+                } else if (item.children('a').length) {
+                    var firstLink = item.children('a').first();
+
+                    if (firstLink.data('overrides-tree-activation-key-handler')) {
+                        firstLink.triggerHandler(e);
+                    }
                 }
 
-                e.stopPropagation();
-                return false;
+                e.preventDefault();
+                return;
             }
             case this.keys.left: {
                 var focusParent = function(tree) {
@@ -422,8 +430,8 @@ define(['jquery'], function($) {
                     focusParent(this);
                 }
 
-                e.stopPropagation();
-                return false;
+                e.preventDefault();
+                return;
             }
             case this.keys.right: {
                 // If this is a group item then expand it and focus the first child item
@@ -437,8 +445,8 @@ define(['jquery'], function($) {
                     }
                 }
 
-                e.stopPropagation();
-                return false;
+                e.preventDefault();
+                return;
             }
             case this.keys.up: {
 
@@ -448,8 +456,8 @@ define(['jquery'], function($) {
                     prev.focus();
                 }
 
-                e.stopPropagation();
-                return false;
+                e.preventDefault();
+                return;
             }
             case this.keys.down: {
 
@@ -459,17 +467,16 @@ define(['jquery'], function($) {
                     next.focus();
                 }
 
-                e.stopPropagation();
-                return false;
+                e.preventDefault();
+                return;
             }
             case this.keys.asterisk: {
                 // Expand all groups.
                 this.expandAllGroups();
-                e.stopPropagation();
-                return false;
+                e.preventDefault();
+                return;
             }
         }
-        return true;
     };
 
     /**
@@ -478,7 +485,6 @@ define(['jquery'], function($) {
      * @method handleClick
      * @param {Object} item The jquery id of the parent item of the group.
      * @param {Event} e The event.
-     * @return {Boolean}
      */
     Tree.prototype.handleClick = function(item, e) {
 
@@ -502,14 +508,10 @@ define(['jquery'], function($) {
      * @method handleFocus
      * @param {Object} item The jquery id of the parent item of the group.
      * @param {Event} e The event.
-     * @return {Boolean}
      */
-    Tree.prototype.handleFocus = function(item, e) {
+    Tree.prototype.handleFocus = function(item) {
 
         this.setActiveItem(item);
-
-        e.stopPropagation();
-        return true;
     };
 
     /**
@@ -529,8 +531,8 @@ define(['jquery'], function($) {
             keydown: function(e) {
               return thisObj.handleKeyDown($(this), e);
             },
-            focus: function(e) {
-              return thisObj.handleFocus($(this), e);
+            focus: function() {
+              return thisObj.handleFocus($(this));
             },
         }, SELECTORS.ITEM);
     };
index 2edf311..e3e2548 100644 (file)
@@ -2911,10 +2911,10 @@ function xmldb_main_upgrade($oldversion) {
             }
 
             $dbman->drop_field($table, $field);
-
-            // Main savepoint reached.
-            upgrade_main_savepoint(true, 2021052500.33);
         }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2021052500.33);
     }
 
     if ($oldversion < 2021052500.36) {
index 599913a..4459502 100644 (file)
@@ -886,7 +886,7 @@ class mysqli_native_moodle_database extends moodle_database {
         $info->type           = $rawcolumn->data_type;
         $info->meta_type      = $this->mysqltype2moodletype($rawcolumn->data_type);
         if ($this->has_breaking_change_quoted_defaults()) {
-            $info->default_value = trim($rawcolumn->column_default, "'");
+            $info->default_value = is_null($rawcolumn->column_default) ? null : trim($rawcolumn->column_default, "'");
             if ($info->default_value === 'NULL') {
                 $info->default_value = null;
             }
index bff9b97..5a36826 100644 (file)
@@ -722,9 +722,13 @@ EOD;
         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, 'lala');
         $table->add_field('description', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
+        $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
+        $table->add_field('oneintnodefault', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null);
         $table->add_field('enumfield', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'test2');
         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
-        $table->add_field('onefloat', XMLDB_TYPE_FLOAT, '10,2', null, null, null, 300);
+        $table->add_field('onenumnodefault', XMLDB_TYPE_NUMBER, '10,2', null, null, null);
+        $table->add_field('onefloat', XMLDB_TYPE_FLOAT, '10,2', null, XMLDB_NOTNULL, null, 300);
+        $table->add_field('onefloatnodefault', XMLDB_TYPE_FLOAT, '10,2', null, XMLDB_NOTNULL, null);
         $table->add_field('anotherfloat', XMLDB_TYPE_FLOAT, null, null, null, null, 400);
         $table->add_field('negativedfltint', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '-1');
         $table->add_field('negativedfltnumber', XMLDB_TYPE_NUMBER, '10', null, XMLDB_NOTNULL, null, '-2');
@@ -785,6 +789,20 @@ EOD;
         $this->assertNull($field->default_value);
         $this->assertFalse($field->not_null);
 
+        $field = $columns['oneint'];
+        $this->assertSame('I', $field->meta_type);
+        $this->assertFalse($field->auto_increment);
+        $this->assertTrue($field->has_default);
+        $this->assertEquals(0, $field->default_value);
+        $this->assertTrue($field->not_null);
+
+        $field = $columns['oneintnodefault'];
+        $this->assertSame('I', $field->meta_type);
+        $this->assertFalse($field->auto_increment);
+        $this->assertFalse($field->has_default);
+        $this->assertNull($field->default_value);
+        $this->assertTrue($field->not_null);
+
         $field = $columns['enumfield'];
         $this->assertSame('C', $field->meta_type);
         $this->assertFalse($field->auto_increment);
@@ -800,12 +818,28 @@ EOD;
         $this->assertEquals(200.0, $field->default_value);
         $this->assertFalse($field->not_null);
 
+        $field = $columns['onenumnodefault'];
+        $this->assertSame('N', $field->meta_type);
+        $this->assertFalse($field->auto_increment);
+        $this->assertEquals(10, $field->max_length);
+        $this->assertEquals(2, $field->scale);
+        $this->assertFalse($field->has_default);
+        $this->assertNull($field->default_value);
+        $this->assertFalse($field->not_null);
+
         $field = $columns['onefloat'];
         $this->assertSame('N', $field->meta_type);
         $this->assertFalse($field->auto_increment);
         $this->assertTrue($field->has_default);
         $this->assertEquals(300.0, $field->default_value);
-        $this->assertFalse($field->not_null);
+        $this->assertTrue($field->not_null);
+
+        $field = $columns['onefloatnodefault'];
+        $this->assertSame('N', $field->meta_type);
+        $this->assertFalse($field->auto_increment);
+        $this->assertFalse($field->has_default);
+        $this->assertNull($field->default_value);
+        $this->assertTrue($field->not_null);
 
         $field = $columns['anotherfloat'];
         $this->assertSame('N', $field->meta_type);
index 4f543ea..590777e 100644 (file)
@@ -9862,7 +9862,7 @@ function rename_to_unused_name(string $filepath, string $prefix = '_temp_') {
  * @return bool success, true also if dir does not exist
  */
 function remove_dir($dir, $contentonly=false) {
-    if (!file_exists($dir)) {
+    if (!is_dir($dir)) {
         // Nothing to do.
         return true;
     }
index b28c58a..9bfb3e3 100644 (file)
@@ -1498,8 +1498,9 @@ function make_unique_writable_directory($basedir, $exceptiononerror = true) {
     }
 
     do {
-        // Generate a new (hopefully unique) directory name.
-        $uniquedir = $basedir . DIRECTORY_SEPARATOR . \core\uuid::generate();
+        // Let's use uniqid() because it's "unique enough" (microtime based). The loop does handle repetitions.
+        // Windows and old PHP don't like very long paths, so try to keep this shorter. See MDL-69975.
+        $uniquedir = $basedir . DIRECTORY_SEPARATOR . uniqid();
     } while (
             // Ensure that basedir is still writable - if we do not check, we could get stuck in a loop here.
             is_writable($basedir) &&
@@ -1635,7 +1636,12 @@ function get_request_storage_directory($exceptiononerror = true, bool $forcecrea
     $createnewdirectory = $forcecreate || !$writabledirectoryexists;
 
     if ($createnewdirectory) {
-        $basedir = "{$CFG->localrequestdir}/{$CFG->siteidentifier}";
+
+        // Let's add the first chars of siteidentifier only. This is to help separate
+        // paths on systems which host multiple moodles. We don't use the full id
+        // as Windows and old PHP don't like very long paths. See MDL-69975.
+        $basedir = $CFG->localrequestdir . '/' . substr($CFG->siteidentifier, 0, 4);
+
         make_writable_directory($basedir);
         protect_directory($basedir);
 
index 1654857..f75ee85 100644 (file)
@@ -92,7 +92,7 @@ class message_airnotifier_manager {
                         array('userdeviceid' => $device->id))) {
 
                     // We have to create the device token in airnotifier.
-                    if (! $this->create_token($device->pushid)) {
+                    if (! $this->create_token($device->pushid, $device->platform)) {
                         continue;
                     }
 
@@ -149,9 +149,10 @@ class message_airnotifier_manager {
     /**
      * Create a device token in the Airnotifier instance
      * @param string $token The token to be created
+     * @param string $deviceplatform The device platform (Android, iOS, iOS-fcm, etc...)
      * @return bool True if all was right
      */
-    private function create_token($token) {
+    private function create_token($token, $deviceplatform = '') {
         global $CFG;
 
         if (!$this->is_system_configured()) {
@@ -165,7 +166,10 @@ class message_airnotifier_manager {
             'X-AN-APP-KEY: ' . $CFG->airnotifieraccesskey);
         $curl = new curl;
         $curl->setHeader($header);
-        $params = array();
+        $params = [];
+        if (!empty($deviceplatform)) {
+            $params["device"] = $deviceplatform;
+        }
         $resp = $curl->post($serverurl, $params);
 
         if ($token = json_decode($resp, true)) {
index e30b121..6de875d 100644 (file)
@@ -41,13 +41,14 @@ $conf = [
     'token_endpoint_auth_methods_supported' => ['private_key_jwt'],
     'token_endpoint_auth_signing_alg_values_supported' => ['RS256'],
     'jwks_uri' => (new moodle_url('/mod/lti/certs.php'))->out(false),
+    'authorization_endpoint' => (new moodle_url('/mod/lti/auth.php'))->out(false),
     'registration_endpoint' => (new moodle_url('/mod/lti/openid-registration.php'))->out(false),
     'scopes_supported' => $scopes,
     'response_types_supported' => ['id_token'],
     'subject_types_supported' => ['public', 'pairwise'],
     'id_token_signing_alg_values_supported' => ['RS256'],
     'claims_supported' => ['sub', 'iss', 'name', 'given_name', 'family_name', 'email'],
-    'https://purl.imsglobal.org/spec/lti-platform-configuration ' => [
+    'https://purl.imsglobal.org/spec/lti-platform-configuration' => [
         'product_family_code' => 'moodle',
         'version' => $CFG->release,
         'messages_supported' => ['LtiResourceLink', 'LtiDeepLinkingRequest'],