Merge branch 'MDL-69658-310' of https://github.com/paulholden/moodle into MOODLE_310_...
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 14 Sep 2020 20:56:42 +0000 (22:56 +0200)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 14 Sep 2020 20:56:42 +0000 (22:56 +0200)
140 files changed:
admin/classes/local/settings/autocomplete.php [new file with mode: 0644]
admin/settings/server.php
admin/templates/local/settings/autocomplete.mustache [new file with mode: 0644]
admin/tool/dataprivacy/lang/en/deprecated.txt [deleted file]
admin/tool/dataprivacy/lang/en/tool_dataprivacy.php
admin/tool/log/store/legacy/classes/log/store.php
admin/tool/mobile/classes/api.php
admin/tool/mobile/lang/en/tool_mobile.php
admin/tool/mobile/settings.php
admin/tool/mobile/tests/externallib_test.php
admin/tool/mobile/version.php
admin/tool/moodlenet/classes/external.php
admin/tool/moodlenet/classes/profile_manager.php
admin/tool/moodlenet/db/upgrade.php
admin/tool/moodlenet/version.php
admin/tool/task/classes/run_from_cli.php
admin/tool/task/cli/adhoc_task.php
admin/tool/task/cli/schedule_task.php
analytics/classes/local/analyser/base.php
analytics/classes/manager.php
auth/cas/db/upgrade.php
auth/cas/settings.php
auth/cas/version.php
auth/ldap/auth.php
auth/ldap/db/upgrade.php
auth/ldap/lang/en/auth_ldap.php
auth/ldap/settings.php
auth/ldap/version.php
backup/util/ui/classes/copy/copy.php
backup/util/ui/classes/output/copy_form.php
blocks/myoverview/lang/en/block_myoverview.php
blocks/myoverview/lang/en/deprecated.txt
calendar/classes/external/calendar_event_exporter.php
calendar/lib.php
course/classes/local/repository/content_item_readonly_repository.php
enrol/database/cli/sync.php
enrol/ldap/lang/en/enrol_ldap.php
enrol/ldap/lib.php
enrol/ldap/settings.php
filter/activitynames/filter.php
filter/activitynames/tests/filter_test.php
lang/en/backup.php
lang/en/badges.php
lang/en/deprecated.txt
lang/en/message.php
lang/en/moodle.php
lang/en/repository.php
lang/en/user.php
lib/accesslib.php
lib/adminlib.php
lib/amd/build/checkbox-toggleall.min.js
lib/amd/build/checkbox-toggleall.min.js.map
lib/amd/build/form-autocomplete.min.js
lib/amd/build/form-autocomplete.min.js.map
lib/amd/src/checkbox-toggleall.js
lib/amd/src/form-autocomplete.js
lib/behat/behat_base.php
lib/blocklib.php
lib/classes/lock/db_record_lock_factory.php
lib/classes/lock/file_lock_factory.php
lib/classes/lock/installation_lock_factory.php
lib/classes/lock/lock.php
lib/classes/lock/lock_factory.php
lib/classes/lock/mysql_lock_factory.php
lib/classes/lock/postgres_lock_factory.php
lib/classes/task/asynchronous_copy_task.php
lib/classes/task/legacy_plugin_cron_task.php
lib/db/services.php
lib/deprecatedlib.php
lib/filestorage/stored_file.php
lib/form/filemanager.js
lib/licenselib.php
lib/outputcomponents.php
lib/plagiarismlib.php
lib/templates/action_menu_link.mustache
lib/templates/action_menu_trigger.mustache
lib/tests/accesslib_has_capability_test.php
lib/tests/accesslib_test.php
lib/tests/behat/behat_deprecated.php
lib/upgrade.txt
media/player/videojs/amd/build/loader.min.js
media/player/videojs/amd/build/loader.min.js.map
media/player/videojs/amd/src/loader.js
media/player/videojs/classes/external/get_language.php [new file with mode: 0644]
media/player/videojs/classes/plugin.php
media/player/videojs/db/services.php [new file with mode: 0644]
media/player/videojs/version.php
message/amd/build/message_repository.min.js
message/amd/build/message_repository.min.js.map
message/amd/build/toggle_contact_button.min.js
message/amd/build/toggle_contact_button.min.js.map
message/amd/src/message_repository.js
message/amd/src/toggle_contact_button.js
message/classes/api.php
message/classes/helper.php
message/classes/output/messagearea/contact.php [deleted file]
message/classes/output/messagearea/contacts.php [deleted file]
message/classes/output/messagearea/message.php [deleted file]
message/classes/output/messagearea/message_area.php [deleted file]
message/classes/output/messagearea/messages.php [deleted file]
message/classes/output/messagearea/profile.php [deleted file]
message/classes/output/messagearea/user_search_results.php [deleted file]
message/defaultoutputs.php
message/externallib.php
message/lib.php
message/tests/api_test.php
message/tests/events_test.php
message/tests/externallib_test.php
message/tests/messagelib_test.php
message/upgrade.txt
mod/book/edit_form.php
mod/folder/backup/moodle2/backup_folder_stepslib.php
mod/folder/db/access.php
mod/folder/db/install.xml
mod/folder/db/upgrade.php
mod/folder/lang/en/folder.php
mod/folder/lib.php
mod/folder/mod_form.php
mod/folder/renderer.php
mod/folder/version.php
mod/forum/deprecatedlib.php
mod/forum/externallib.php
mod/forum/tests/externallib_test.php
mod/lti/lib.php
mod/quiz/accessrule/seb/classes/quiz_settings.php
mod/quiz/accessrule/seb/tests/helper_test.php
mod/quiz/accessrule/seb/tests/quiz_settings_test.php
question/type/multichoice/amd/build/clearchoice.min.js
question/type/multichoice/amd/build/clearchoice.min.js.map
question/type/multichoice/amd/src/clearchoice.js
question/type/multichoice/renderer.php
repository/draftfiles_ajax.php
repository/filepicker.js
search/classes/manager.php
tag/manage.php
theme/boost/scss/moodle/forms.scss
theme/boost/style/moodle.css
theme/classic/style/moodle.css
user/editlib.php
version.php

diff --git a/admin/classes/local/settings/autocomplete.php b/admin/classes/local/settings/autocomplete.php
new file mode 100644 (file)
index 0000000..ad2b3cd
--- /dev/null
@@ -0,0 +1,206 @@
+<?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/>.
+
+/**
+ * Auto complete admin setting.
+ *
+ * @package    core_admin
+ * @copyright  2020 The Open University
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core_admin\local\settings;
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->libdir . '/adminlib.php');
+/**
+ * Auto complete setting class.
+ *
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @copyright 2020 The Open University
+ */
+class autocomplete extends \admin_setting_configmultiselect {
+    /** @var boolean Should we allow typing new entries to the field? */
+    protected $tags = false;
+    /** @var string Name of an AMD module to send/process ajax requests. */
+    protected $ajax = '';
+    /** @var string Placeholder text for an empty list. */
+    protected $placeholder = '';
+    /** @var bool Whether the search has to be case-sensitive. */
+    protected $casesensitive = false;
+    /** @var bool Show suggestions by default - but this can be turned off. */
+    protected $showsuggestions = true;
+    /** @var string String that is shown when there are no selections. */
+    protected $noselectionstring = '';
+    /** @var string Delimiter to store values in database. */
+    protected $delimiter = ',';
+    /** @var string Should be multiple choices? */
+    protected $multiple = true;
+    /** @var string The link to manage choices. */
+    protected $manageurl = true;
+    /** @var string The text to display in manage link. */
+    protected $managetext = true;
+
+    /**
+     * Constructor
+     *
+     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
+     * for ones in config_plugins.
+     * @param string $visiblename localised
+     * @param string $description long localised info
+     * @param array $defaultsetting array of selected items
+     * @param array $choices options for autocomplete field
+     * @param array $attributes settings for autocomplete field
+     */
+    public function __construct($name, $visiblename, $description, $defaultsetting, $choices, $attributes = null) {
+
+        if ($attributes === null) {
+            $attributes = [];
+        }
+
+        $this->placeholder = get_string('search');
+        $this->noselectionstring = get_string('noselection', 'form');
+        $defaultattributes = [
+                'tags',
+                'showsuggestions',
+                'placeholder',
+                'noselectionstring',
+                'ajax',
+                'casesensitive',
+                'delimiter',
+                'multiple',
+                'manageurl',
+                'managetext'
+        ];
+
+        foreach ($defaultattributes as $attributename) {
+            if (isset($attributes[$attributename])) {
+                $this->$attributename = $attributes[$attributename];
+            }
+        }
+
+        parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
+    }
+
+    /**
+     * Returns the select setting(s)
+     *
+     * @return mixed null or array. Null if no settings else array of setting(s)
+     */
+    public function get_setting() {
+        $result = $this->config_read($this->name);
+        if (is_null($result)) {
+            return null;
+        }
+        if ($result === '') {
+            return [];
+        }
+        return explode($this->delimiter, $result);
+    }
+
+    /**
+     * Saves setting(s) provided through $data
+     *
+     * @param array $data
+     */
+    public function write_setting($data) {
+        if (!is_array($data)) {
+            return ''; // Ignore it.
+        }
+        if (!$this->load_choices() || empty($this->choices)) {
+            return '';
+        }
+
+        unset($data['xxxxx']);
+
+        $save = [];
+        foreach ($data as $value) {
+            if (!array_key_exists($value, $this->choices)) {
+                continue; // Ignore it.
+            }
+            $save[] = $value;
+        }
+
+        return ($this->config_write($this->name, implode($this->delimiter, $save)) ? '' : get_string('errorsetting', 'admin'));
+    }
+
+    /**
+     * Returns XHTML autocomplete field
+     *
+     * @param array $data Array of values to select by default
+     * @param string $query
+     * @return string XHTML autocomplete field
+     */
+    public function output_html($data, $query = '') {
+        global $OUTPUT;
+
+        if (!$this->load_choices() or empty($this->choices)) {
+            return '';
+        }
+
+        $default = $this->get_defaultsetting();
+        if (empty($default)) {
+            $default = [];
+        }
+
+        if (is_null($data)) {
+            $data = [];
+        }
+
+        $context = [
+                'id' => $this->get_id(),
+                'name' => $this->get_full_name()
+        ];
+
+        $defaults = [];
+        $options = [];
+        $template = 'core_admin/local/settings/autocomplete';
+
+        foreach ($this->choices as $value => $name) {
+            if (in_array($value, $default)) {
+                $defaults[] = $name;
+            }
+            $options[] = [
+                    'value' => $value,
+                    'text' => $name,
+                    'selected' => in_array($value, $data),
+                    'disabled' => false
+            ];
+        }
+
+        $context['options'] = $options;
+        $context['tags'] = $this->tags;
+        $context['placeholder'] = $this->placeholder;
+        $context['casesensitive'] = $this->casesensitive;
+        $context['multiple'] = $this->multiple;
+        $context['showsuggestions'] = $this->showsuggestions;
+        $context['manageurl'] = $this->manageurl;
+        $context['managetext'] = $this->managetext;
+
+        if (is_null($default)) {
+            $defaultinfo = null;
+        } if (!empty($defaults)) {
+            $defaultinfo = implode(', ', $defaults);
+        } else {
+            $defaultinfo = get_string('none');
+        }
+
+        $element = $OUTPUT->render_from_template($template, $context);
+
+        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
+    }
+}
index e9fac46..1c3ee18 100644 (file)
 <?php
+// This file is part of Moodle - https://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/>.
+
+/**
+ * Defines settingpages and externalpages under the "server" category.
+ *
+ * @package     core
+ * @category    admin
+ * @copyright   2006 Martin Dougiamas
+ * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+if ($hassiteconfig) {
+    // System paths.
+    $temp = new admin_settingpage('systempaths', new lang_string('systempaths', 'admin'));
+    $temp->add(new admin_setting_configexecutable('pathtophp', new lang_string('pathtophp', 'admin'),
+        new lang_string('configpathtophp', 'admin'), ''));
+    $temp->add(new admin_setting_configexecutable('pathtodu', new lang_string('pathtodu', 'admin'),
+        new lang_string('configpathtodu', 'admin'), ''));
+    $temp->add(new admin_setting_configexecutable('aspellpath', new lang_string('aspellpath', 'admin'),
+        new lang_string('edhelpaspellpath'), ''));
+    $temp->add(new admin_setting_configexecutable('pathtodot', new lang_string('pathtodot', 'admin'),
+        new lang_string('pathtodot_help', 'admin'), ''));
+    $temp->add(new admin_setting_configexecutable('pathtogs', new lang_string('pathtogs', 'admin'),
+        new lang_string('pathtogs_help', 'admin'), '/usr/bin/gs'));
+    $temp->add(new admin_setting_configexecutable('pathtopython', new lang_string('pathtopython', 'admin'),
+        new lang_string('pathtopythondesc', 'admin'), ''));
+    $ADMIN->add('server', $temp);
 
-// This file defines settingpages and externalpages under the "server" category
-
-if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
-
-// "systempaths" settingpage
-$temp = new admin_settingpage('systempaths', new lang_string('systempaths','admin'));
-$temp->add(new admin_setting_configexecutable('pathtophp', new lang_string('pathtophp', 'admin'),
-    new lang_string('configpathtophp', 'admin'), ''));
-$temp->add(new admin_setting_configexecutable('pathtodu', new lang_string('pathtodu', 'admin'), new lang_string('configpathtodu', 'admin'), ''));
-$temp->add(new admin_setting_configexecutable('aspellpath', new lang_string('aspellpath', 'admin'), new lang_string('edhelpaspellpath'), ''));
-$temp->add(new admin_setting_configexecutable('pathtodot', new lang_string('pathtodot', 'admin'), new lang_string('pathtodot_help', 'admin'), ''));
-$temp->add(new admin_setting_configexecutable('pathtogs', new lang_string('pathtogs', 'admin'), new lang_string('pathtogs_help', 'admin'), '/usr/bin/gs'));
-$temp->add(new admin_setting_configexecutable('pathtopython', new lang_string('pathtopython', 'admin'),
-    new lang_string('pathtopythondesc', 'admin'), ''));
-$ADMIN->add('server', $temp);
-
-
-
-// "supportcontact" settingpage
-$temp = new admin_settingpage('supportcontact', new lang_string('supportcontact','admin'));
-$primaryadmin = get_admin();
-if ($primaryadmin) {
-    $primaryadminemail = $primaryadmin->email;
-    $primaryadminname  = fullname($primaryadmin, true);
-} else {
-    // no defaults during installation - admin user must be created first
-    $primaryadminemail = NULL;
-    $primaryadminname  = NULL;
-}
-$temp->add(new admin_setting_configtext('supportname', new lang_string('supportname', 'admin'),
-    new lang_string('configsupportname', 'admin'), $primaryadminname, PARAM_NOTAGS));
-$setting = new admin_setting_configtext('supportemail', new lang_string('supportemail', 'admin'),
-    new lang_string('configsupportemail', 'admin'), $primaryadminemail, PARAM_EMAIL);
-$setting->set_force_ltr(true);
-$temp->add($setting);
-$temp->add(new admin_setting_configtext('supportpage', new lang_string('supportpage', 'admin'), new lang_string('configsupportpage', 'admin'), '', PARAM_URL));
-$ADMIN->add('server', $temp);
-
-
-// "sessionhandling" settingpage
-$temp = new admin_settingpage('sessionhandling', new lang_string('sessionhandling', 'admin'));
-if (empty($CFG->session_handler_class) and $DB->session_lock_supported()) {
-    $temp->add(new admin_setting_configcheckbox('dbsessions', new lang_string('dbsessions', 'admin'), new lang_string('configdbsessions', 'admin'), 0));
-}
+    // Support contact.
+    $temp = new admin_settingpage('supportcontact', new lang_string('supportcontact', 'admin'));
+    $primaryadmin = get_admin();
+    if ($primaryadmin) {
+        $primaryadminemail = $primaryadmin->email;
+        $primaryadminname = fullname($primaryadmin, true);
+    } else {
+        // No defaults during installation - admin user must be created first.
+        $primaryadminemail = null;
+        $primaryadminname = null;
+    }
+    $temp->add(new admin_setting_configtext('supportname', new lang_string('supportname', 'admin'),
+        new lang_string('configsupportname', 'admin'), $primaryadminname, PARAM_NOTAGS));
+    $setting = new admin_setting_configtext('supportemail', new lang_string('supportemail', 'admin'),
+        new lang_string('configsupportemail', 'admin'), $primaryadminemail, PARAM_EMAIL);
+    $setting->set_force_ltr(true);
+    $temp->add($setting);
+    $temp->add(new admin_setting_configtext('supportpage', new lang_string('supportpage', 'admin'),
+        new lang_string('configsupportpage', 'admin'), '', PARAM_URL));
+    $ADMIN->add('server', $temp);
 
-$temp->add(new admin_setting_configduration('sessiontimeout', new lang_string('sessiontimeout', 'admin'),
-    new lang_string('configsessiontimeout', 'admin'), 8 * 60 * 60));
-
-$temp->add(new admin_setting_configtext('sessioncookie', new lang_string('sessioncookie', 'admin'), new lang_string('configsessioncookie', 'admin'), '', PARAM_ALPHANUM));
-$temp->add(new admin_setting_configtext('sessioncookiepath', new lang_string('sessioncookiepath', 'admin'), new lang_string('configsessioncookiepath', 'admin'), '', PARAM_RAW));
-$temp->add(new admin_setting_configtext('sessioncookiedomain', new lang_string('sessioncookiedomain', 'admin'), new lang_string('configsessioncookiedomain', 'admin'), '', PARAM_RAW, 50));
-$ADMIN->add('server', $temp);
-
-
-// "stats" settingpage
-$temp = new admin_settingpage('stats', new lang_string('stats'), 'moodle/site:config', empty($CFG->enablestats));
-$temp->add(new admin_setting_configselect('statsfirstrun', new lang_string('statsfirstrun', 'admin'), new lang_string('configstatsfirstrun', 'admin'), 'none', array('none' => new lang_string('none'),
-                                                                                                                                                           60*60*24*7 => new lang_string('numweeks','moodle',1),
-                                                                                                                                                           60*60*24*14 => new lang_string('numweeks','moodle',2),
-                                                                                                                                                           60*60*24*21 => new lang_string('numweeks','moodle',3),
-                                                                                                                                                           60*60*24*28 => new lang_string('nummonths','moodle',1),
-                                                                                                                                                           60*60*24*56 => new lang_string('nummonths','moodle',2),
-                                                                                                                                                           60*60*24*84 => new lang_string('nummonths','moodle',3),
-                                                                                                                                                           60*60*24*112 => new lang_string('nummonths','moodle',4),
-                                                                                                                                                           60*60*24*140 => new lang_string('nummonths','moodle',5),
-                                                                                                                                                           60*60*24*168 => new lang_string('nummonths','moodle',6),
-                                                                                                                                                           'all' => new lang_string('all') )));
-$temp->add(new admin_setting_configselect('statsmaxruntime', new lang_string('statsmaxruntime', 'admin'), new lang_string('configstatsmaxruntime3', 'admin'), 0, array(0 => new lang_string('untilcomplete'),
-                                                                                                                                                            60*30 => '10 '.new lang_string('minutes'),
-                                                                                                                                                            60*30 => '30 '.new lang_string('minutes'),
-                                                                                                                                                            60*60 => '1 '.new lang_string('hour'),
-                                                                                                                                                            60*60*2 => '2 '.new lang_string('hours'),
-                                                                                                                                                            60*60*3 => '3 '.new lang_string('hours'),
-                                                                                                                                                            60*60*4 => '4 '.new lang_string('hours'),
-                                                                                                                                                            60*60*5 => '5 '.new lang_string('hours'),
-                                                                                                                                                            60*60*6 => '6 '.new lang_string('hours'),
-                                                                                                                                                            60*60*7 => '7 '.new lang_string('hours'),
-                                                                                                                                                            60*60*8 => '8 '.new lang_string('hours') )));
-$temp->add(new admin_setting_configtext('statsruntimedays', new lang_string('statsruntimedays', 'admin'), new lang_string('configstatsruntimedays', 'admin'), 31, PARAM_INT));
-$temp->add(new admin_setting_configtext('statsuserthreshold', new lang_string('statsuserthreshold', 'admin'), new lang_string('configstatsuserthreshold', 'admin'), 0, PARAM_INT));
-$ADMIN->add('server', $temp);
-
-
-// "http" settingpage
-$temp = new admin_settingpage('http', new lang_string('http', 'admin'));
-$temp->add(new admin_setting_configcheckbox('slasharguments', new lang_string('slasharguments', 'admin'), new lang_string('configslasharguments', 'admin'), 1));
-$temp->add(new admin_setting_heading('reverseproxy', new lang_string('reverseproxy', 'admin'), '', ''));
-$options = array(
-    0 => 'HTTP_CLIENT_IP, HTTP_X_FORWARDED_FOR, REMOTE_ADDR',
-    GETREMOTEADDR_SKIP_HTTP_CLIENT_IP => 'HTTP_X_FORWARDED_FOR, REMOTE_ADDR',
-    GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR => 'HTTP_CLIENT, REMOTE_ADDR',
-    GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR|GETREMOTEADDR_SKIP_HTTP_CLIENT_IP => 'REMOTE_ADDR');
-$temp->add(new admin_setting_configselect('getremoteaddrconf', new lang_string('getremoteaddrconf', 'admin'),
-    new lang_string('configgetremoteaddrconf', 'admin'),
-    GETREMOTEADDR_SKIP_DEFAULT, $options));
-$temp->add(new admin_setting_configtext('reverseproxyignore', new lang_string('reverseproxyignore', 'admin'), new lang_string('configreverseproxyignore', 'admin'), ''));
-
-$temp->add(new admin_setting_heading('webproxy', new lang_string('webproxy', 'admin'), new lang_string('webproxyinfo', 'admin')));
-$temp->add(new admin_setting_configtext('proxyhost', new lang_string('proxyhost', 'admin'), new lang_string('configproxyhost', 'admin'), '', PARAM_HOST));
-$temp->add(new admin_setting_configtext('proxyport', new lang_string('proxyport', 'admin'), new lang_string('configproxyport', 'admin'), 0, PARAM_INT));
-$options = array('HTTP'=>'HTTP');
-if (defined('CURLPROXY_SOCKS5')) {
-    $options['SOCKS5'] = 'SOCKS5';
-}
-$temp->add(new admin_setting_configselect('proxytype', new lang_string('proxytype', 'admin'), new lang_string('configproxytype','admin'), 'HTTP', $options));
-$temp->add(new admin_setting_configtext('proxyuser', new lang_string('proxyuser', 'admin'), new lang_string('configproxyuser', 'admin'), ''));
-$temp->add(new admin_setting_configpasswordunmask('proxypassword', new lang_string('proxypassword', 'admin'), new lang_string('configproxypassword', 'admin'), ''));
-$temp->add(new admin_setting_configtext('proxybypass', new lang_string('proxybypass', 'admin'), new lang_string('configproxybypass', 'admin'), 'localhost, 127.0.0.1'));
-$ADMIN->add('server', $temp);
-
-$temp = new admin_settingpage('maintenancemode', new lang_string('sitemaintenancemode', 'admin'));
-$options = array(0=>new lang_string('disable'), 1=>new lang_string('enable'));
-$temp->add(new admin_setting_configselect('maintenance_enabled', new lang_string('sitemaintenancemode', 'admin'),
-                                          new lang_string('helpsitemaintenance', 'admin'), 0, $options));
-$temp->add(new admin_setting_confightmleditor('maintenance_message', new lang_string('optionalmaintenancemessage', 'admin'),
-                                              '', ''));
-$ADMIN->add('server', $temp);
-
-$temp = new admin_settingpage('cleanup', new lang_string('cleanup', 'admin'));
-$temp->add(new admin_setting_configselect('deleteunconfirmed', new lang_string('deleteunconfirmed', 'admin'), new lang_string('configdeleteunconfirmed', 'admin'), 168, array(0 => new lang_string('never'),
-                                                                                                                                                                    168 => new lang_string('numdays', '', 7),
-                                                                                                                                                                    144 => new lang_string('numdays', '', 6),
-                                                                                                                                                                    120 => new lang_string('numdays', '', 5),
-                                                                                                                                                                    96 => new lang_string('numdays', '', 4),
-                                                                                                                                                                    72 => new lang_string('numdays', '', 3),
-                                                                                                                                                                    48 => new lang_string('numdays', '', 2),
-                                                                                                                                                                    24 => new lang_string('numdays', '', 1),
-                                                                                                                                                                    12 => new lang_string('numhours', '', 12),
-                                                                                                                                                                    6 => new lang_string('numhours', '', 6),
-                                                                                                                                                                    1 => new lang_string('numhours', '', 1))));
-
-$temp->add(new admin_setting_configselect('deleteincompleteusers', new lang_string('deleteincompleteusers', 'admin'), new lang_string('configdeleteincompleteusers', 'admin'), 0, array(0 => new lang_string('never'),
-                                                                                                                                                                    168 => new lang_string('numdays', '', 7),
-                                                                                                                                                                    144 => new lang_string('numdays', '', 6),
-                                                                                                                                                                    120 => new lang_string('numdays', '', 5),
-                                                                                                                                                                    96 => new lang_string('numdays', '', 4),
-                                                                                                                                                                    72 => new lang_string('numdays', '', 3),
-                                                                                                                                                                    48 => new lang_string('numdays', '', 2),
-                                                                                                                                                                    24 => new lang_string('numdays', '', 1))));
-
-
-$temp->add(new admin_setting_configcheckbox('disablegradehistory', new lang_string('disablegradehistory', 'grades'),
-                                            new lang_string('disablegradehistory_help', 'grades'), 0));
-
-$temp->add(new admin_setting_configselect('gradehistorylifetime', new lang_string('gradehistorylifetime', 'grades'),
-                                          new lang_string('gradehistorylifetime_help', 'grades'), 0, array(0 => new lang_string('neverdeletehistory', 'grades'),
-                                                                                                   1000 => new lang_string('numdays', '', 1000),
-                                                                                                    365 => new lang_string('numdays', '', 365),
-                                                                                                    180 => new lang_string('numdays', '', 180),
-                                                                                                    150 => new lang_string('numdays', '', 150),
-                                                                                                    120 => new lang_string('numdays', '', 120),
-                                                                                                     90 => new lang_string('numdays', '', 90),
-                                                                                                     60 => new lang_string('numdays', '', 60),
-                                                                                                     30 => new lang_string('numdays', '', 30))));
-
-$temp->add(new admin_setting_configselect('tempdatafoldercleanup', new lang_string('tempdatafoldercleanup', 'admin'),
-        new lang_string('configtempdatafoldercleanup', 'admin'), 168, array(
+    // Session handling.
+    $temp = new admin_settingpage('sessionhandling', new lang_string('sessionhandling', 'admin'));
+    if (empty($CFG->session_handler_class) and $DB->session_lock_supported()) {
+        $temp->add(new admin_setting_configcheckbox('dbsessions', new lang_string('dbsessions', 'admin'),
+            new lang_string('configdbsessions', 'admin'), 0));
+    }
+
+    $temp->add(new admin_setting_configduration('sessiontimeout', new lang_string('sessiontimeout', 'admin'),
+        new lang_string('configsessiontimeout', 'admin'), 8 * 60 * 60));
+
+    $temp->add(new admin_setting_configtext('sessioncookie', new lang_string('sessioncookie', 'admin'),
+        new lang_string('configsessioncookie', 'admin'), '', PARAM_ALPHANUM));
+    $temp->add(new admin_setting_configtext('sessioncookiepath', new lang_string('sessioncookiepath', 'admin'),
+        new lang_string('configsessioncookiepath', 'admin'), '', PARAM_RAW));
+    $temp->add(new admin_setting_configtext('sessioncookiedomain', new lang_string('sessioncookiedomain', 'admin'),
+        new lang_string('configsessioncookiedomain', 'admin'), '', PARAM_RAW, 50));
+    $ADMIN->add('server', $temp);
+
+    // Statistics.
+    $temp = new admin_settingpage('stats', new lang_string('stats'), 'moodle/site:config', empty($CFG->enablestats));
+    $temp->add(new admin_setting_configselect('statsfirstrun', new lang_string('statsfirstrun', 'admin'),
+        new lang_string('configstatsfirstrun', 'admin'), 'none',
+        [
+            'none' => new lang_string('none'),
+            60 * 60 * 24 * 7 => new lang_string('numweeks', 'moodle', 1),
+            60 * 60 * 24 * 14 => new lang_string('numweeks', 'moodle', 2),
+            60 * 60 * 24 * 21 => new lang_string('numweeks', 'moodle', 3),
+            60 * 60 * 24 * 28 => new lang_string('nummonths', 'moodle', 1),
+            60 * 60 * 24 * 56 => new lang_string('nummonths', 'moodle', 2),
+            60 * 60 * 24 * 84 => new lang_string('nummonths', 'moodle', 3),
+            60 * 60 * 24 * 112 => new lang_string('nummonths', 'moodle', 4),
+            60 * 60 * 24 * 140 => new lang_string('nummonths', 'moodle', 5),
+            60 * 60 * 24 * 168 => new lang_string('nummonths', 'moodle', 6),
+            'all' => new lang_string('all')
+        ]
+    ));
+    $temp->add(new admin_setting_configselect('statsmaxruntime', new lang_string('statsmaxruntime', 'admin'),
+        new lang_string('configstatsmaxruntime3', 'admin'), 0,
+        [
+            0 => new lang_string('untilcomplete'),
+            60 * 30 => '10 ' . new lang_string('minutes'),
+            60 * 30 => '30 ' . new lang_string('minutes'),
+            60 * 60 => '1 ' . new lang_string('hour'),
+            60 * 60 * 2 => '2 ' . new lang_string('hours'),
+            60 * 60 * 3 => '3 ' . new lang_string('hours'),
+            60 * 60 * 4 => '4 ' . new lang_string('hours'),
+            60 * 60 * 5 => '5 ' . new lang_string('hours'),
+            60 * 60 * 6 => '6 ' . new lang_string('hours'),
+            60 * 60 * 7 => '7 ' . new lang_string('hours'),
+            60 * 60 * 8 => '8 ' . new lang_string('hours'),
+        ]
+    ));
+    $temp->add(new admin_setting_configtext('statsruntimedays', new lang_string('statsruntimedays', 'admin'),
+        new lang_string('configstatsruntimedays', 'admin'), 31, PARAM_INT));
+    $temp->add(new admin_setting_configtext('statsuserthreshold', new lang_string('statsuserthreshold', 'admin'),
+        new lang_string('configstatsuserthreshold', 'admin'), 0, PARAM_INT));
+    $ADMIN->add('server', $temp);
+
+    // HTTP.
+    $temp = new admin_settingpage('http', new lang_string('http', 'admin'));
+    $temp->add(new admin_setting_configcheckbox('slasharguments', new lang_string('slasharguments', 'admin'),
+        new lang_string('configslasharguments', 'admin'), 1));
+    $temp->add(new admin_setting_heading('reverseproxy', new lang_string('reverseproxy', 'admin'), '', ''));
+    $options = [
+        0 => 'HTTP_CLIENT_IP, HTTP_X_FORWARDED_FOR, REMOTE_ADDR',
+        GETREMOTEADDR_SKIP_HTTP_CLIENT_IP => 'HTTP_X_FORWARDED_FOR, REMOTE_ADDR',
+        GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR => 'HTTP_CLIENT, REMOTE_ADDR',
+        GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR | GETREMOTEADDR_SKIP_HTTP_CLIENT_IP => 'REMOTE_ADDR'
+    ];
+    $temp->add(new admin_setting_configselect('getremoteaddrconf', new lang_string('getremoteaddrconf', 'admin'),
+        new lang_string('configgetremoteaddrconf', 'admin'),
+        GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR | GETREMOTEADDR_SKIP_HTTP_CLIENT_IP, $options));
+    $temp->add(new admin_setting_configtext('reverseproxyignore', new lang_string('reverseproxyignore', 'admin'),
+        new lang_string('configreverseproxyignore', 'admin'), ''));
+
+    $temp->add(new admin_setting_heading('webproxy', new lang_string('webproxy', 'admin'),
+        new lang_string('webproxyinfo', 'admin')));
+    $temp->add(new admin_setting_configtext('proxyhost', new lang_string('proxyhost', 'admin'),
+        new lang_string('configproxyhost', 'admin'), '', PARAM_HOST));
+    $temp->add(new admin_setting_configtext('proxyport', new lang_string('proxyport', 'admin'),
+        new lang_string('configproxyport', 'admin'), 0, PARAM_INT));
+    $options = ['HTTP' => 'HTTP'];
+    if (defined('CURLPROXY_SOCKS5')) {
+        $options['SOCKS5'] = 'SOCKS5';
+    }
+    $temp->add(new admin_setting_configselect('proxytype', new lang_string('proxytype', 'admin'),
+        new lang_string('configproxytype', 'admin'), 'HTTP', $options));
+    $temp->add(new admin_setting_configtext('proxyuser', new lang_string('proxyuser', 'admin'),
+        new lang_string('configproxyuser', 'admin'), ''));
+    $temp->add(new admin_setting_configpasswordunmask('proxypassword', new lang_string('proxypassword', 'admin'),
+        new lang_string('configproxypassword', 'admin'), ''));
+    $temp->add(new admin_setting_configtext('proxybypass', new lang_string('proxybypass', 'admin'),
+        new lang_string('configproxybypass', 'admin'), 'localhost, 127.0.0.1'));
+    $ADMIN->add('server', $temp);
+
+    $temp = new admin_settingpage('maintenancemode', new lang_string('sitemaintenancemode', 'admin'));
+    $options = [0 => new lang_string('disable'), 1 => new lang_string('enable')];
+    $temp->add(new admin_setting_configselect('maintenance_enabled', new lang_string('sitemaintenancemode', 'admin'),
+        new lang_string('helpsitemaintenance', 'admin'), 0, $options));
+    $temp->add(new admin_setting_confightmleditor('maintenance_message', new lang_string('optionalmaintenancemessage', 'admin'),
+        '', ''));
+    $ADMIN->add('server', $temp);
+
+    // Cleanup.
+    $temp = new admin_settingpage('cleanup', new lang_string('cleanup', 'admin'));
+    $temp->add(new admin_setting_configselect('deleteunconfirmed', new lang_string('deleteunconfirmed', 'admin'),
+        new lang_string('configdeleteunconfirmed', 'admin'), 168,
+        [
+            0 => new lang_string('never'),
+            168 => new lang_string('numdays', '', 7),
+            144 => new lang_string('numdays', '', 6),
+            120 => new lang_string('numdays', '', 5),
+            96 => new lang_string('numdays', '', 4),
+            72 => new lang_string('numdays', '', 3),
+            48 => new lang_string('numdays', '', 2),
+            24 => new lang_string('numdays', '', 1),
+            12 => new lang_string('numhours', '', 12),
+            6 => new lang_string('numhours', '', 6),
+            1 => new lang_string('numhours', '', 1),
+        ]
+    ));
+
+    $temp->add(new admin_setting_configselect('deleteincompleteusers', new lang_string('deleteincompleteusers', 'admin'),
+        new lang_string('configdeleteincompleteusers', 'admin'), 0,
+        [
+            0 => new lang_string('never'),
+            168 => new lang_string('numdays', '', 7),
+            144 => new lang_string('numdays', '', 6),
+            120 => new lang_string('numdays', '', 5),
+            96 => new lang_string('numdays', '', 4),
+            72 => new lang_string('numdays', '', 3),
+            48 => new lang_string('numdays', '', 2),
+            24 => new lang_string('numdays', '', 1),
+        ]
+    ));
+
+    $temp->add(new admin_setting_configcheckbox('disablegradehistory', new lang_string('disablegradehistory', 'grades'),
+        new lang_string('disablegradehistory_help', 'grades'), 0));
+
+    $temp->add(new admin_setting_configselect('gradehistorylifetime', new lang_string('gradehistorylifetime', 'grades'),
+        new lang_string('gradehistorylifetime_help', 'grades'), 0,
+        [
+            0 => new lang_string('neverdeletehistory', 'grades'),
+            1000 => new lang_string('numdays', '', 1000),
+            365 => new lang_string('numdays', '', 365),
+            180 => new lang_string('numdays', '', 180),
+            150 => new lang_string('numdays', '', 150),
+            120 => new lang_string('numdays', '', 120),
+            90 => new lang_string('numdays', '', 90),
+            60 => new lang_string('numdays', '', 60),
+            30 => new lang_string('numdays', '', 30),
+        ]
+    ));
+
+    $temp->add(new admin_setting_configselect('tempdatafoldercleanup', new lang_string('tempdatafoldercleanup', 'admin'),
+        new lang_string('configtempdatafoldercleanup', 'admin'), 168,
+        [
             1 => new lang_string('numhours', '', 1),
             3 => new lang_string('numhours', '', 3),
             6 => new lang_string('numhours', '', 6),
@@ -166,237 +229,283 @@ $temp->add(new admin_setting_configselect('tempdatafoldercleanup', new lang_stri
             24 => new lang_string('numhours', '', 24),
             48 => new lang_string('numdays', '', 2),
             168 => new lang_string('numdays', '', 7),
-)));
+        ]
+    ));
 
-$ADMIN->add('server', $temp);
+    $ADMIN->add('server', $temp);
 
     $temp->add(new admin_setting_configduration('filescleanupperiod',
         new lang_string('filescleanupperiod', 'admin'),
         new lang_string('filescleanupperiod_help', 'admin'),
         86400));
 
-$ADMIN->add('server', new admin_externalpage('environment', new lang_string('environment','admin'), "$CFG->wwwroot/$CFG->admin/environment.php"));
-$ADMIN->add('server', new admin_externalpage('phpinfo', new lang_string('phpinfo'), "$CFG->wwwroot/$CFG->admin/phpinfo.php"));
-$ADMIN->add('server', new admin_externalpage('testoutgoingmailconf', new lang_string('testoutgoingmailconf', 'admin'),
-            new moodle_url("$CFG->wwwroot/$CFG->admin/testoutgoingmailconf.php"), 'moodle/site:config', true));
+    // Environment.
+    $ADMIN->add('server', new admin_externalpage('environment', new lang_string('environment', 'admin'),
+        "{$CFG->wwwroot}/{$CFG->admin}/environment.php"));
+
+    // PHP info.
+    $ADMIN->add('server', new admin_externalpage('phpinfo', new lang_string('phpinfo'),
+        "{$CFG->wwwroot}/{$CFG->admin}/phpinfo.php"));
+
+    // Test outgoing mail configuration (hidden, accessed via direct link from the settings page).
+    $ADMIN->add('server', new admin_externalpage('testoutgoingmailconf', new lang_string('testoutgoingmailconf', 'admin'),
+        new moodle_url('/admin/testoutgoingmailconf.php'), 'moodle/site:config', true));
+
+    // Performance.
+    $temp = new admin_settingpage('performance', new lang_string('performance', 'admin'));
+
+    // Memory limit options for large administration tasks.
+    $memoryoptions = [
+        '64M' => '64M',
+        '128M' => '128M',
+        '256M' => '256M',
+        '512M' => '512M',
+        '1024M' => '1024M',
+        '2048M' => '2048M',
+    ];
+
+    // Allow larger memory usage for 64-bit sites only.
+    if (PHP_INT_SIZE === 8) {
+        $memoryoptions['3072M'] = '3072M';
+        $memoryoptions['4096M'] = '4096M';
+    }
+
+    $temp->add(new admin_setting_configselect('extramemorylimit', new lang_string('extramemorylimit', 'admin'),
+        new lang_string('configextramemorylimit', 'admin'), '512M', $memoryoptions));
+
+    $temp->add(new admin_setting_configtext('maxtimelimit', new lang_string('maxtimelimit', 'admin'),
+        new lang_string('maxtimelimit_desc', 'admin'), 0, PARAM_INT));
 
+    $temp->add(new admin_setting_configtext('curlcache', new lang_string('curlcache', 'admin'),
+        new lang_string('configcurlcache', 'admin'), 120, PARAM_INT));
 
-// "performance" settingpage
-$temp = new admin_settingpage('performance', new lang_string('performance', 'admin'));
+    $temp->add(new admin_setting_configtext('curltimeoutkbitrate', new lang_string('curltimeoutkbitrate', 'admin'),
+        new lang_string('curltimeoutkbitrate_help', 'admin'), 56, PARAM_INT));
 
-// Memory limit options for large administration tasks.
-$memoryoptions = array(
-    '64M' => '64M',
-    '128M' => '128M',
-    '256M' => '256M',
-    '512M' => '512M',
-    '1024M' => '1024M',
-    '2048M' => '2048M');
+    $ADMIN->add('server', $temp);
 
-// Allow larger memory usage for 64-bit sites only.
-if (PHP_INT_SIZE === 8) {
-    $memoryoptions['3072M'] = '3072M';
-    $memoryoptions['4096M'] = '4096M';
-}
+    // Tasks.
+    $ADMIN->add('server', new admin_category('taskconfig', new lang_string('taskadmintitle', 'admin')));
 
-$temp->add(new admin_setting_configselect('extramemorylimit', new lang_string('extramemorylimit', 'admin'),
-                                          new lang_string('configextramemorylimit', 'admin'), '512M',
-                                          $memoryoptions));
-$temp->add(new admin_setting_configtext('maxtimelimit', new lang_string('maxtimelimit', 'admin'),
-        new lang_string('maxtimelimit_desc', 'admin'), 0, PARAM_INT));
+    // Task processing.
+    $temp = new admin_settingpage('taskprocessing', new lang_string('taskprocessing', 'admin'));
 
-$temp->add(new admin_setting_configtext('curlcache', new lang_string('curlcache', 'admin'),
-                                        new lang_string('configcurlcache', 'admin'), 120, PARAM_INT));
-
-$temp->add(new admin_setting_configtext('curltimeoutkbitrate', new lang_string('curltimeoutkbitrate', 'admin'),
-                                        new lang_string('curltimeoutkbitrate_help', 'admin'), 56, PARAM_INT));
-
-$ADMIN->add('server', $temp);
-
-
-$ADMIN->add('server', new admin_category('taskconfig', new lang_string('taskadmintitle', 'admin')));
-$temp = new admin_settingpage('taskprocessing', new lang_string('taskprocessing','admin'));
-
-$setting = new admin_setting_configcheckbox(
-    'cron_enabled',
-    new lang_string('cron_enabled', 'admin'),
-    new lang_string('cron_enabled_desc', 'admin'),
-    1
-);
-$setting->set_updatedcallback('theme_reset_static_caches');
-$temp->add($setting);
-
-$temp->add(
-    new admin_setting_configtext(
-        'task_scheduled_concurrency_limit',
-        new lang_string('task_scheduled_concurrency_limit', 'admin'),
-        new lang_string('task_scheduled_concurrency_limit_desc', 'admin'),
-        3,
-        PARAM_INT
-    )
-);
-
-$temp->add(
-    new admin_setting_configduration(
-        'task_scheduled_max_runtime',
-        new lang_string('task_scheduled_max_runtime', 'admin'),
-        new lang_string('task_scheduled_max_runtime_desc', 'admin'),
-        30 * MINSECS
-    )
-);
-
-$temp->add(
-    new admin_setting_configtext(
-        'task_adhoc_concurrency_limit',
-        new lang_string('task_adhoc_concurrency_limit', 'admin'),
-        new lang_string('task_adhoc_concurrency_limit_desc', 'admin'),
-        3,
-        PARAM_INT
-    )
-);
-
-$temp->add(
-    new admin_setting_configduration(
-        'task_adhoc_max_runtime',
-        new lang_string('task_adhoc_max_runtime', 'admin'),
-        new lang_string('task_adhoc_max_runtime_desc', 'admin'),
-        30 * MINSECS
-    )
-);
-$ADMIN->add('taskconfig', $temp);
-
-$temp = new admin_settingpage('tasklogging', new lang_string('tasklogging','admin'));
-$temp->add(
-    new admin_setting_configselect(
-        'task_logmode',
-        new lang_string('task_logmode', 'admin'),
-        new lang_string('task_logmode_desc', 'admin'),
-        \core\task\logmanager::MODE_ALL,
-        [
-            \core\task\logmanager::MODE_ALL => new lang_string('task_logmode_all', 'admin'),
-            \core\task\logmanager::MODE_FAILONLY => new lang_string('task_logmode_failonly', 'admin'),
-            \core\task\logmanager::MODE_NONE => new lang_string('task_logmode_none', 'admin'),
-        ]
-    )
-);
-$temp->add(
-    new admin_setting_configcheckbox(
-        'task_logtostdout',
-        new lang_string('task_logtostdout', 'admin'),
-        new lang_string('task_logtostdout_desc', 'admin'),
+    $setting = new admin_setting_configcheckbox(
+        'cron_enabled',
+        new lang_string('cron_enabled', 'admin'),
+        new lang_string('cron_enabled_desc', 'admin'),
         1
-    )
-);
+    );
+    $setting->set_updatedcallback('theme_reset_static_caches');
+    $temp->add($setting);
+
+    $temp->add(
+        new admin_setting_configtext(
+            'task_scheduled_concurrency_limit',
+            new lang_string('task_scheduled_concurrency_limit', 'admin'),
+            new lang_string('task_scheduled_concurrency_limit_desc', 'admin'),
+            3,
+            PARAM_INT
+        )
+    );
 
-if (\core\task\logmanager::uses_standard_settings()) {
     $temp->add(
         new admin_setting_configduration(
-            'task_logretention',
-            new \lang_string('task_logretention', 'admin'),
-            new \lang_string('task_logretention_desc', 'admin'),
-            28 * DAYSECS
+            'task_scheduled_max_runtime',
+            new lang_string('task_scheduled_max_runtime', 'admin'),
+            new lang_string('task_scheduled_max_runtime_desc', 'admin'),
+            30 * MINSECS
         )
     );
 
     $temp->add(
         new admin_setting_configtext(
-            'task_logretainruns',
-            new \lang_string('task_logretainruns', 'admin'),
-            new \lang_string('task_logretainruns_desc', 'admin'),
-            20,
+            'task_adhoc_concurrency_limit',
+            new lang_string('task_adhoc_concurrency_limit', 'admin'),
+            new lang_string('task_adhoc_concurrency_limit_desc', 'admin'),
+            3,
             PARAM_INT
         )
     );
-}
-$ADMIN->add('taskconfig', $temp);
 
-if (\core\task\logmanager::uses_standard_settings()) {
-    $ADMIN->add('taskconfig', new admin_externalpage(
-        'tasklogs',
-        new lang_string('tasklogs','admin'),
-        "{$CFG->wwwroot}/{$CFG->admin}/tasklogs.php"
-    ));
-}
+    $temp->add(
+        new admin_setting_configduration(
+            'task_adhoc_max_runtime',
+            new lang_string('task_adhoc_max_runtime', 'admin'),
+            new lang_string('task_adhoc_max_runtime_desc', 'admin'),
+            30 * MINSECS
+        )
+    );
+    $ADMIN->add('taskconfig', $temp);
 
-// E-mail settings.
-$ADMIN->add('server', new admin_category('email', new lang_string('categoryemail', 'admin')));
-
-$temp = new admin_settingpage('outgoingmailconfig', new lang_string('outgoingmailconfig', 'admin'));
-
-$temp->add(new admin_setting_heading('smtpheading', new lang_string('smtp', 'admin'),
-            new lang_string('smtpdetail', 'admin')));
-$temp->add(new admin_setting_configtext('smtphosts', new lang_string('smtphosts', 'admin'),
-            new lang_string('configsmtphosts', 'admin'), '', PARAM_RAW));
-$options = array('' => new lang_string('none', 'admin'), 'ssl' => 'SSL', 'tls' => 'TLS');
-$temp->add(new admin_setting_configselect('smtpsecure', new lang_string('smtpsecure', 'admin'),
-            new lang_string('configsmtpsecure', 'admin'), '', $options));
-$authtypeoptions = array('LOGIN' => 'LOGIN', 'PLAIN' => 'PLAIN', 'NTLM' => 'NTLM', 'CRAM-MD5' => 'CRAM-MD5');
-$temp->add(new admin_setting_configselect('smtpauthtype', new lang_string('smtpauthtype', 'admin'),
-            new lang_string('configsmtpauthtype', 'admin'), 'LOGIN', $authtypeoptions));
-$temp->add(new admin_setting_configtext('smtpuser', new lang_string('smtpuser', 'admin'),
-            new lang_string('configsmtpuser', 'admin'), '', PARAM_NOTAGS));
-$temp->add(new admin_setting_configpasswordunmask('smtppass', new lang_string('smtppass', 'admin'),
-            new lang_string('configsmtpuser', 'admin'), ''));
-$temp->add(new admin_setting_configtext('smtpmaxbulk', new lang_string('smtpmaxbulk', 'admin'),
-           new lang_string('configsmtpmaxbulk', 'admin'), 1, PARAM_INT));
-$temp->add(new admin_setting_heading('noreplydomainheading', new lang_string('noreplydomain', 'admin'),
+    // Task log configuration.
+    $temp = new admin_settingpage('tasklogging', new lang_string('tasklogging', 'admin'));
+    $temp->add(
+        new admin_setting_configselect(
+            'task_logmode',
+            new lang_string('task_logmode', 'admin'),
+            new lang_string('task_logmode_desc', 'admin'),
+            \core\task\logmanager::MODE_ALL,
+            [
+                \core\task\logmanager::MODE_ALL => new lang_string('task_logmode_all', 'admin'),
+                \core\task\logmanager::MODE_FAILONLY => new lang_string('task_logmode_failonly', 'admin'),
+                \core\task\logmanager::MODE_NONE => new lang_string('task_logmode_none', 'admin'),
+            ]
+        )
+    );
+    $temp->add(
+        new admin_setting_configcheckbox(
+            'task_logtostdout',
+            new lang_string('task_logtostdout', 'admin'),
+            new lang_string('task_logtostdout_desc', 'admin'),
+            1
+        )
+    );
+
+    if (\core\task\logmanager::uses_standard_settings()) {
+        $temp->add(
+            new admin_setting_configduration(
+                'task_logretention',
+                new \lang_string('task_logretention', 'admin'),
+                new \lang_string('task_logretention_desc', 'admin'),
+                28 * DAYSECS
+            )
+        );
+
+        $temp->add(
+            new admin_setting_configtext(
+                'task_logretainruns',
+                new \lang_string('task_logretainruns', 'admin'),
+                new \lang_string('task_logretainruns_desc', 'admin'),
+                20,
+                PARAM_INT
+            )
+        );
+    }
+    $ADMIN->add('taskconfig', $temp);
+
+    // Task logs.
+    if (\core\task\logmanager::uses_standard_settings()) {
+        $ADMIN->add('taskconfig', new admin_externalpage(
+            'tasklogs',
+            new lang_string('tasklogs', 'admin'),
+            "{$CFG->wwwroot}/{$CFG->admin}/tasklogs.php"
+        ));
+    }
+
+    // Email.
+    $ADMIN->add('server', new admin_category('email', new lang_string('categoryemail', 'admin')));
+
+    // Outgoing mail configuration.
+    $temp = new admin_settingpage('outgoingmailconfig', new lang_string('outgoingmailconfig', 'admin'));
+
+    $temp->add(new admin_setting_heading('smtpheading', new lang_string('smtp', 'admin'),
+        new lang_string('smtpdetail', 'admin')));
+
+    $temp->add(new admin_setting_configtext('smtphosts', new lang_string('smtphosts', 'admin'),
+        new lang_string('configsmtphosts', 'admin'), '', PARAM_RAW));
+
+    $options = [
+        '' => new lang_string('none', 'admin'),
+        'ssl' => 'SSL',
+        'tls' => 'TLS',
+    ];
+
+    $temp->add(new admin_setting_configselect('smtpsecure', new lang_string('smtpsecure', 'admin'),
+        new lang_string('configsmtpsecure', 'admin'), '', $options));
+
+    $authtypeoptions = [
+        'LOGIN' => 'LOGIN',
+        'PLAIN' => 'PLAIN',
+        'NTLM' => 'NTLM',
+        'CRAM-MD5' => 'CRAM-MD5',
+    ];
+
+    $temp->add(new admin_setting_configselect('smtpauthtype', new lang_string('smtpauthtype', 'admin'),
+        new lang_string('configsmtpauthtype', 'admin'), 'LOGIN', $authtypeoptions));
+
+    $temp->add(new admin_setting_configtext('smtpuser', new lang_string('smtpuser', 'admin'),
+        new lang_string('configsmtpuser', 'admin'), '', PARAM_NOTAGS));
+
+    $temp->add(new admin_setting_configpasswordunmask('smtppass', new lang_string('smtppass', 'admin'),
+        new lang_string('configsmtpuser', 'admin'), ''));
+
+    $temp->add(new admin_setting_configtext('smtpmaxbulk', new lang_string('smtpmaxbulk', 'admin'),
+        new lang_string('configsmtpmaxbulk', 'admin'), 1, PARAM_INT));
+
+    $temp->add(new admin_setting_heading('noreplydomainheading', new lang_string('noreplydomain', 'admin'),
         new lang_string('noreplydomaindetail', 'admin')));
-$temp->add(new admin_setting_configtext('noreplyaddress', new lang_string('noreplyaddress', 'admin'),
-          new lang_string('confignoreplyaddress', 'admin'), 'noreply@' . get_host_from_url($CFG->wwwroot), PARAM_EMAIL));
-$temp->add(new admin_setting_configtextarea('allowedemaildomains',
+
+    $temp->add(new admin_setting_configtext('noreplyaddress', new lang_string('noreplyaddress', 'admin'),
+        new lang_string('confignoreplyaddress', 'admin'), 'noreply@' . get_host_from_url($CFG->wwwroot), PARAM_EMAIL));
+
+    $temp->add(new admin_setting_configtextarea('allowedemaildomains',
         new lang_string('allowedemaildomains', 'admin'),
         new lang_string('configallowedemaildomains', 'admin'),
         ''));
-$url = new moodle_url('/admin/testoutgoingmailconf.php');
-$link = html_writer::link($url, get_string('testoutgoingmailconf', 'admin'));
-$temp->add(new admin_setting_heading('testoutgoinmailc', new lang_string('testoutgoingmailconf', 'admin'),
+
+    $url = new moodle_url('/admin/testoutgoingmailconf.php');
+    $link = html_writer::link($url, get_string('testoutgoingmailconf', 'admin'));
+    $temp->add(new admin_setting_heading('testoutgoinmailc', new lang_string('testoutgoingmailconf', 'admin'),
         new lang_string('testoutgoingmaildetail', 'admin', $link)));
-$temp->add(new admin_setting_heading('emaildoesnotfit', new lang_string('doesnotfit', 'admin'),
+
+    $temp->add(new admin_setting_heading('emaildoesnotfit', new lang_string('doesnotfit', 'admin'),
         new lang_string('doesnotfitdetail', 'admin')));
-$charsets = get_list_of_charsets();
-unset($charsets['UTF-8']); // Not needed here.
-$options = array();
-$options['0'] = 'UTF-8';
-$options = array_merge($options, $charsets);
-$temp->add(new admin_setting_configselect('sitemailcharset', new lang_string('sitemailcharset', 'admin'),
-          new lang_string('configsitemailcharset','admin'), '0', $options));
-$temp->add(new admin_setting_configcheckbox('allowusermailcharset', new lang_string('allowusermailcharset', 'admin'),
-          new lang_string('configallowusermailcharset', 'admin'), 0));
-$temp->add(new admin_setting_configcheckbox('allowattachments', new lang_string('allowattachments', 'admin'),
-          new lang_string('configallowattachments', 'admin'), 1));
-$options = array('LF' => 'LF', 'CRLF' => 'CRLF');
-$temp->add(new admin_setting_configselect('mailnewline', new lang_string('mailnewline', 'admin'),
-          new lang_string('configmailnewline', 'admin'), 'LF', $options));
-
-$choices = array(new lang_string('never', 'admin'),
-                 new lang_string('always', 'admin'),
-                 new lang_string('onlynoreply', 'admin'));
-$temp->add(new admin_setting_configselect('emailfromvia', new lang_string('emailfromvia', 'admin'),
-          new lang_string('configemailfromvia', 'admin'), 1, $choices));
-
-$temp->add(new admin_setting_configtext('emailsubjectprefix', new lang_string('emailsubjectprefix', 'admin'),
+
+    $charsets = get_list_of_charsets();
+    unset($charsets['UTF-8']);
+    $options = [
+        '0' => 'UTF-8',
+    ];
+    $options = array_merge($options, $charsets);
+    $temp->add(new admin_setting_configselect('sitemailcharset', new lang_string('sitemailcharset', 'admin'),
+        new lang_string('configsitemailcharset', 'admin'), '0', $options));
+
+    $temp->add(new admin_setting_configcheckbox('allowusermailcharset', new lang_string('allowusermailcharset', 'admin'),
+        new lang_string('configallowusermailcharset', 'admin'), 0));
+
+    $temp->add(new admin_setting_configcheckbox('allowattachments', new lang_string('allowattachments', 'admin'),
+        new lang_string('configallowattachments', 'admin'), 1));
+
+    $options = [
+        'LF' => 'LF',
+        'CRLF' => 'CRLF',
+    ];
+    $temp->add(new admin_setting_configselect('mailnewline', new lang_string('mailnewline', 'admin'),
+        new lang_string('configmailnewline', 'admin'), 'LF', $options));
+
+    $choices = [
+        new lang_string('never', 'admin'),
+        new lang_string('always', 'admin'),
+        new lang_string('onlynoreply', 'admin'),
+    ];
+    $temp->add(new admin_setting_configselect('emailfromvia', new lang_string('emailfromvia', 'admin'),
+        new lang_string('configemailfromvia', 'admin'), 1, $choices));
+
+    $temp->add(new admin_setting_configtext('emailsubjectprefix', new lang_string('emailsubjectprefix', 'admin'),
         new lang_string('configemailsubjectprefix', 'admin'), '', PARAM_RAW));
-$temp->add(new admin_setting_configtextarea('emailheaders', new lang_string('emailheaders', 'admin'),
+
+    $temp->add(new admin_setting_configtextarea('emailheaders', new lang_string('emailheaders', 'admin'),
         new lang_string('configemailheaders', 'admin'), '', PARAM_RAW, '50', '3'));
 
-$ADMIN->add('email', $temp);
-
-// "update notifications" settingpage
-if (empty($CFG->disableupdatenotifications)) {
-    $temp = new admin_settingpage('updatenotifications', new lang_string('updatenotifications', 'core_admin'));
-    $temp->add(new admin_setting_configcheckbox('updateautocheck', new lang_string('updateautocheck', 'core_admin'),
-                                                new lang_string('updateautocheck_desc', 'core_admin'), 1));
-    $temp->add(new admin_setting_configselect('updateminmaturity', new lang_string('updateminmaturity', 'core_admin'),
-                                              new lang_string('updateminmaturity_desc', 'core_admin'), MATURITY_STABLE,
-                                              array(
-                                                  MATURITY_ALPHA  => new lang_string('maturity'.MATURITY_ALPHA, 'core_admin'),
-                                                  MATURITY_BETA   => new lang_string('maturity'.MATURITY_BETA, 'core_admin'),
-                                                  MATURITY_RC     => new lang_string('maturity'.MATURITY_RC, 'core_admin'),
-                                                  MATURITY_STABLE => new lang_string('maturity'.MATURITY_STABLE, 'core_admin'),
-                                              )));
-    $temp->add(new admin_setting_configcheckbox('updatenotifybuilds', new lang_string('updatenotifybuilds', 'core_admin'),
-                                                new lang_string('updatenotifybuilds_desc', 'core_admin'), 0));
-    $ADMIN->add('server', $temp);
+    $ADMIN->add('email', $temp);
+
+    // Update notifications.
+    if (empty($CFG->disableupdatenotifications)) {
+        $temp = new admin_settingpage('updatenotifications', new lang_string('updatenotifications', 'core_admin'));
+        $temp->add(new admin_setting_configcheckbox('updateautocheck', new lang_string('updateautocheck', 'core_admin'),
+            new lang_string('updateautocheck_desc', 'core_admin'), 1));
+        $temp->add(new admin_setting_configselect('updateminmaturity', new lang_string('updateminmaturity', 'core_admin'),
+            new lang_string('updateminmaturity_desc', 'core_admin'), MATURITY_STABLE,
+            [
+                MATURITY_ALPHA => new lang_string('maturity'.MATURITY_ALPHA, 'core_admin'),
+                MATURITY_BETA => new lang_string('maturity'.MATURITY_BETA, 'core_admin'),
+                MATURITY_RC => new lang_string('maturity'.MATURITY_RC, 'core_admin'),
+                MATURITY_STABLE => new lang_string('maturity'.MATURITY_STABLE, 'core_admin'),
+            ]
+        ));
+        $temp->add(new admin_setting_configcheckbox('updatenotifybuilds', new lang_string('updatenotifybuilds', 'core_admin'),
+            new lang_string('updatenotifybuilds_desc', 'core_admin'), 0));
+        $ADMIN->add('server', $temp);
+    }
 }
-
-} // end of speedup
diff --git a/admin/templates/local/settings/autocomplete.mustache b/admin/templates/local/settings/autocomplete.mustache
new file mode 100644 (file)
index 0000000..d0a4cad
--- /dev/null
@@ -0,0 +1,87 @@
+{{!
+    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/>.
+}}
+{{!
+    @template core_admin\local\settings\autocomplete
+
+    Admin setting auto complete.
+
+    Context variables required for this template:
+    * name - element name
+    * id - element id
+    * options - choices
+    * multiple - is multiple choices?
+    * manageurl - url to manage choices
+    * managetext - string to display manage url
+    * tags - Should we allow typing new entries to the field?
+    * ajax - Name of an AMD module to send/process ajax requests
+    * placeholder - Placeholder text for an empty list
+    * casesensitive - Whether the search has to be case-sensitive
+    * showsuggestions - Show suggestions by default - but this can be turned off
+    * noselectionstring - String that is shown when there are no selections
+
+
+    Example context (json):
+    {
+        "name": "name0",
+        "id": "id0",
+        "options": [{
+            "value": "1",
+            "text": "option 1",
+            "selected": true,
+            "disabled": false
+        }],
+        "multiple": true,
+        "manageurl": "",
+        "managetext": "",
+        "tags": false,
+        "ajax": "",
+        "placeholder": "",
+        "casesensitive": false,
+        "showsuggestions": true,
+        "noselectionstring": ""
+    }
+}}
+{{!
+    Setting autocomplete.
+}}
+
+<div class="form-select defaultsnext">
+    <input type="hidden" name="{{name}}[xxxxx]" value="_qf__force_multiselect_submission">
+    <select class="custom-select" name="{{name}}[]"
+            id="{{id}}"
+            {{#multiple}}multiple{{/multiple}}>
+        {{#options}}
+            <option value="{{value}}" {{#selected}}selected{{/selected}}>{{{text}}}</option>
+        {{/options}}
+    </select>
+    {{#manageurl}}
+        <a href="{{manageurl}}">{{managetext}}</a>
+    {{/manageurl}}
+</div>
+<br/>
+
+{{#js}}
+    require(['core/form-autocomplete'], function(module) {
+        module.enhance({{#quote}}#{{id}}{{/quote}},
+        {{tags}},
+        {{#quote}}{{ajax}}{{/quote}},
+        {{#quote}}{{placeholder}}{{/quote}},
+        {{casesensitive}},
+        {{showsuggestions}},
+        {{#quote}}{{noselectionstring}}{{/quote}});
+    });
+{{/js}}
diff --git a/admin/tool/dataprivacy/lang/en/deprecated.txt b/admin/tool/dataprivacy/lang/en/deprecated.txt
deleted file mode 100644 (file)
index c400ae8..0000000
+++ /dev/null
@@ -1 +0,0 @@
-statuspreprocessing,tool_dataprivacy
index 54d9c5b..46d7af2 100644 (file)
@@ -344,6 +344,3 @@ $string['role_help'] = 'The role which the override should apply to.';
 $string['duplicaterole'] = 'Role already specified';
 $string['purposeoverview'] = 'A purpose describes the intended use and retention policy for stored data. The basis for storing and retaining that data is also described in the purpose.';
 $string['roleoverrideoverview'] = 'The default retention policy can be overridden for specific user roles, allowing you to specify a longer, or a shorter, retention policy. A user is only expired when all of their roles have expired.';
-
-// Deprecated since Moodle 3.6.
-$string['statuspreprocessing'] = 'Pre-processing';
index bcb1d54..aa4a685 100644 (file)
@@ -17,7 +17,7 @@
 /**
  * Legacy log reader.
  * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
- * @todo  MDL-52805 This is to be removed in Moodle 4.0
+ * @todo  MDL-52805 This is to be removed in Moodle 3.10
  *
  * @package    logstore_legacy
  * @copyright  2013 Petr Skoda {@link http://skodak.org}
@@ -34,7 +34,7 @@ class store implements \tool_log\log\store, \core\log\sql_reader {
 
     /**
      * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
-     * @todo  MDL-52805 This is to be removed in Moodle 4.0
+     * @todo  MDL-52805 This is to be removed in Moodle 3.10
      *
      * @param \tool_log\log\manager $manager
      */
@@ -93,7 +93,7 @@ class store implements \tool_log\log\store, \core\log\sql_reader {
 
     /**
      * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
-     * @todo MDL-52805 This will be removed in Moodle 4.0
+     * @todo MDL-52805 This will be removed in Moodle 3.10
      *
      * @param  string $selectwhere
      * @param  array  $params
@@ -134,7 +134,7 @@ class store implements \tool_log\log\store, \core\log\sql_reader {
     /**
      * Fetch records using given criteria returning a Traversable object.
      * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
-     * @todo MDL-52805 This will be removed in Moodle 4.0
+     * @todo MDL-52805 This will be removed in Moodle 3.10
      *
      * Note that the traversable object contains a moodle_recordset, so
      * remember that is important that you call close() once you finish
@@ -168,7 +168,7 @@ class store implements \tool_log\log\store, \core\log\sql_reader {
     /**
      * Returns an event from the log data.
      * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
-     * @todo MDL-52805 This will be removed in Moodle 4.0
+     * @todo MDL-52805 This will be removed in Moodle 3.10
      *
      * @param stdClass $data Log data
      * @return \core\event\base
@@ -179,7 +179,7 @@ class store implements \tool_log\log\store, \core\log\sql_reader {
 
     /**
      * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
-     * @todo MDL-52805 This will be removed in Moodle 4.0
+     * @todo MDL-52805 This will be removed in Moodle 3.10
      *
      * @param  string $selectwhere
      * @param  array  $params
@@ -202,7 +202,7 @@ class store implements \tool_log\log\store, \core\log\sql_reader {
     /**
      * Are the new events appearing in the reader?
      * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
-     * @todo MDL-52805 This will be removed in Moodle 4.0
+     * @todo MDL-52805 This will be removed in Moodle 3.10
      *
      * @return bool true means new log events are being added, false means no new data will be added
      */
@@ -212,7 +212,7 @@ class store implements \tool_log\log\store, \core\log\sql_reader {
 
     /**
      * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
-     * @todo MDL-52805 This will be removed in Moodle 4.0
+     * @todo MDL-52805 This will be removed in Moodle 3.10
      */
     public function dispose() {
     }
index 07a51d9..006d917 100644 (file)
@@ -302,6 +302,7 @@ class api {
             $settings->tool_mobile_forcelogout = get_config('tool_mobile', 'forcelogout');
             $settings->tool_mobile_customlangstrings = get_config('tool_mobile', 'customlangstrings');
             $settings->tool_mobile_disabledfeatures = get_config('tool_mobile', 'disabledfeatures');
+            $settings->tool_mobile_filetypeexclusionlist = get_config('tool_mobile', 'filetypeexclusionlist');
             $settings->tool_mobile_custommenuitems = get_config('tool_mobile', 'custommenuitems');
             $settings->tool_mobile_apppolicy = get_config('tool_mobile', 'apppolicy');
         }
index 952e99b..678a1b5 100644 (file)
@@ -63,6 +63,9 @@ $string['downloadcourse'] = 'Download course';
 $string['downloadcourses'] = 'Download courses';
 $string['enablesmartappbanners'] = 'Enable App Banners';
 $string['enablesmartappbanners_desc'] = 'If enabled, a banner promoting the mobile app will be displayed when accessing the site using a mobile browser.';
+$string['filetypeexclusionlist'] = 'File type exclusion list';
+$string['filetypeexclusionlist_desc'] = 'List of file types that we don\'t want users to try and open in the app. These files will still be listed on the app\'s course screen, but attempting to open them on iOS or Android would display a warning to the user indicating that this file type is not intended for use on a mobile device. They can then either cancel the open, or ignore the warning and open anyway.';
+$string['filetypeexclusionlistplaceholder'] = 'Mobile file type exclusion list';
 $string['forcedurlscheme'] = 'If you want to allow only your custom branded app to be opened via a browser window, then specify its URL scheme here. If you want to allow only the official app, then set the default value. Leave the field empty if you want to allow any app.';
 $string['forcedurlscheme_key'] = 'URL scheme';
 $string['forcelogout'] = 'Force log out';
@@ -81,6 +84,7 @@ $string['loginintheapp'] = 'Via the app';
 $string['logininthebrowser'] = 'Via a browser window (for SSO plugins)';
 $string['loginintheembeddedbrowser'] = 'Via an embedded browser (for SSO plugins)';
 $string['mainmenu'] = 'Main menu';
+$string['managefiletypes'] = 'Manage file types';
 $string['minimumversion'] = 'If an app version is specified (3.8.0 or higher), any users using an older app version will be prompted to upgrade their app before being allowed access to the site.';
 $string['minimumversion_key'] = 'Minimum app version required';
 $string['mobileapp'] = 'Mobile app';
index 19a8798..029390c 100644 (file)
@@ -26,6 +26,8 @@
 
 defined('MOODLE_INTERNAL') || die();
 
+use core_admin\local\settings\autocomplete;
+
 if ($hassiteconfig) {
 
     $ADMIN->add('root', new admin_category('mobileapp', new lang_string('mobileapp', 'tool_mobile')), 'development');
@@ -188,6 +190,27 @@ if ($hassiteconfig) {
                 new lang_string('custommenuitems', 'tool_mobile'),
                 new lang_string('custommenuitems_desc', 'tool_mobile'), '', PARAM_RAW, '50', '10'));
 
+    // File type exclusionlist.
+    $choices = [];
+    foreach (core_filetypes::get_types() as $key => $info) {
+        $text = '.' . $key;
+        if (!empty($info['type'])) {
+            $text .= ' (' . $info['type'] . ')';
+        }
+        $choices[$key] = $text;
+    }
+
+    $attributes = [
+        'manageurl' => new \moodle_url('/admin/tool/filetypes/index.php'),
+        'managetext' => get_string('managefiletypes', 'tool_mobile'),
+        'multiple' => true,
+        'delimiter' => ',',
+        'placeholder' => get_string('filetypeexclusionlistplaceholder', 'tool_mobile')
+    ];
+    $temp->add(new autocomplete('tool_mobile/filetypeexclusionlist',
+        new lang_string('filetypeexclusionlist', 'tool_mobile'),
+        new lang_string('filetypeexclusionlist_desc', 'tool_mobile'), array(), $choices, $attributes));
+
     $temp->add(new admin_setting_heading('tool_mobile/language',
                 new lang_string('language'), ''));
 
index f39846e..1a16eb7 100644 (file)
@@ -213,6 +213,7 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
             array('name' => 'tool_mobile_forcelogout', 'value' => 0),
             array('name' => 'tool_mobile_customlangstrings', 'value' => ''),
             array('name' => 'tool_mobile_disabledfeatures', 'value' => ''),
+            array('name' => 'tool_mobile_filetypeexclusionlist', 'value' => ''),
             array('name' => 'tool_mobile_custommenuitems', 'value' => ''),
             array('name' => 'tool_mobile_apppolicy', 'value' => ''),
             array('name' => 'calendartype', 'value' => $CFG->calendartype),
index 33cdccd..8c4a6f8 100644 (file)
@@ -23,7 +23,7 @@
  */
 
 defined('MOODLE_INTERNAL') || die();
-$plugin->version   = 2020061500; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version   = 2020061501; // The current plugin version (Date: YYYYMMDDXX).
 $plugin->requires  = 2020060900; // Requires this Moodle version.
 $plugin->component = 'tool_mobile'; // Full name of the plugin (used for diagnostics).
 $plugin->dependencies = array(
index 5fbfd20..b228638 100644 (file)
@@ -52,7 +52,7 @@ class external extends external_api {
     public static function verify_webfinger_parameters() {
         return new external_function_parameters(
             array(
-                'profileurl' => new external_value(PARAM_RAW, 'The profile url that the user has given us', VALUE_REQUIRED),
+                'profileurl' => new external_value(PARAM_NOTAGS, 'The profile url that the user has given us', VALUE_REQUIRED),
                 'course' => new external_value(PARAM_INT, 'The course we are adding to', VALUE_REQUIRED),
                 'section' => new external_value(PARAM_INT, 'The section within the course we are adding to', VALUE_REQUIRED),
             )
index f1a922a..49027fd 100644 (file)
@@ -46,7 +46,7 @@ class profile_manager {
             $user = \core_user::get_user($userid, 'moodlenetprofile');
             try {
                 $userprofile = $user->moodlenetprofile ? $user->moodlenetprofile : '';
-                return (isset($user)) ? new moodlenet_user_profile($userprofile, $userid) : null;
+                return (isset($user)) ? new moodlenet_user_profile(s($userprofile), $userid) : null;
             } catch (\moodle_exception $e) {
                 // If an exception is thrown, means there isn't a valid profile set. No need to log exception.
                 return null;
@@ -59,7 +59,7 @@ class profile_manager {
             if ($field->get_category_name() == self::get_category_name()
                     && $field->inputname == 'profile_field_mnetprofile') {
                 try {
-                    return new moodlenet_user_profile($field->display_data(), $userid);
+                    return new moodlenet_user_profile(s($field->display_data()), $userid);
                 } catch (\moodle_exception $e) {
                     // If an exception is thrown, means there isn't a valid profile set. No need to log exception.
                     return null;
index 0442276..20d5118 100644 (file)
@@ -105,5 +105,23 @@ function xmldb_tool_moodlenet_upgrade(int $oldversion) {
     // Automatically generated Moodle v3.9.0 release upgrade line.
     // Put any upgrade step following this.
 
+    if ($oldversion < 2020090700) {
+
+        // Find out if there are users with MoodleNet profiles set.
+        $sql = "SELECT u.*
+                  FROM {user} u
+                 WHERE u.moodlenetprofile IS NOT NULL";
+
+        $records = $DB->get_records_sql($sql);
+
+        foreach ($records as $record) {
+            // Force clean user value just incase there is something malicious.
+            $record->moodlenetprofile = clean_text($record->moodlenetprofile, PARAM_NOTAGS);
+            $DB->update_record('user', $record);
+        }
+
+        upgrade_plugin_savepoint(true, 2020090700, 'tool', 'moodlenet');
+    }
+
     return true;
 }
index 415d400..00d3399 100644 (file)
@@ -25,6 +25,6 @@
 defined('MOODLE_INTERNAL') || die();
 
 $plugin->component  = 'tool_moodlenet';
-$plugin->version    = 2020061502;
+$plugin->version    = 2020090700;
 $plugin->requires   = 2020060900;
 $plugin->maturity   = MATURITY_ALPHA;
index 9b91a14..35a722e 100644 (file)
@@ -18,7 +18,7 @@
  * Form for scheduled tasks admin pages.
  *
  * @deprecated since Moodle 3.9 MDL-63580. Please use the \core\task\manager.
- * @todo final deprecation. To be removed in Moodle 4.3 MDL-63594.
+ * @todo final deprecation. To be removed in Moodle 4.1 MDL-63594.
  *
  * @package    tool_task
  * @copyright  2018 Toni Barbera <toni@moodle.com>
index 07cded8..0c1135b 100644 (file)
@@ -18,7 +18,7 @@
  * Task executor for adhoc tasks.
  *
  * @deprecated since Moodle 3.9 MDL-63580. Please use the admin/cli/adhoc_task.php.
- * @todo final deprecation. To be removed in Moodle 4.3 MDL-63594.
+ * @todo final deprecation. To be removed in Moodle 4.1 MDL-63594.
  *
  * @package    tool_task
  * @copyright  2018 Andrew Nicols <andrew@nicols.co.uk>
index 073075e..5b1038a 100644 (file)
@@ -18,7 +18,7 @@
  * CLI task execution.
  *
  * @deprecated since Moodle 3.9 MDL-63580. Please use the admin/cli/schedule_task.php.
- * @todo final deprecation. To be removed in Moodle 4.3 MDL-63594.
+ * @todo final deprecation. To be removed in Moodle 4.1 MDL-63594.
  *
  * @package    tool_task
  * @copyright  2014 Petr Skoda
index 99735bd..7a958f9 100644 (file)
@@ -111,14 +111,14 @@ abstract class base {
      * \core_analytics\local\analyser\by_course and \core_analytics\local\analyser\sitewide are implementing
      * this method returning site courses (by_course) and the whole system (sitewide) as analysables.
      *
-     * @todo MDL-65284 This will be removed in Moodle 4.1
+     * @todo MDL-65284 This will be removed in Moodle 3.11
      * @deprecated
      * @see get_analysables_iterator
      * @throws  \coding_exception
      * @return \core_analytics\analysable[] Array of analysable elements using the analysable id as array key.
      */
     public function get_analysables() {
-        // This function should only be called from get_analysables_iterator and we keep it here until Moodle 4.1
+        // This function should only be called from get_analysables_iterator and we keep it here until Moodle 3.11
         // for backwards compatibility.
         throw new \coding_exception('This method is deprecated in favour of get_analysables_iterator.');
     }
index faea917..054a07a 100644 (file)
@@ -303,7 +303,7 @@ class manager {
      * Returns the enabled time splitting methods.
      *
      * @deprecated since Moodle 3.7
-     * @todo MDL-65086 This will be deleted in Moodle 4.1
+     * @todo MDL-65086 This will be deleted in Moodle 3.11
      * @see \core_analytics\manager::get_time_splitting_methods_for_evaluation
      * @return \core_analytics\local\time_splitting\base[]
      */
@@ -604,7 +604,7 @@ class manager {
      * Used to be used to add models included with the Moodle core.
      *
      * @deprecated Deprecated since Moodle 3.7 (MDL-61667) - Use lib/db/analytics.php instead.
-     * @todo Remove this method in Moodle 4.1 (MDL-65186).
+     * @todo Remove this method in Moodle 3.11 (MDL-65186).
      * @return void
      */
     public static function add_builtin_models() {
index 34f5364..78bff15 100644 (file)
@@ -47,5 +47,13 @@ function xmldb_auth_cas_upgrade($oldversion) {
     // Automatically generated Moodle v3.9.0 release upgrade line.
     // Put any upgrade step following this.
 
+    if ($oldversion < 2020081700) {
+        // Normalize the memberattribute_isdn plugin config.
+        set_config('memberattribute_isdn',
+            !empty(get_config('auth_cas', 'memberattribute_isdn')), 'auth_cas');
+
+        upgrade_plugin_savepoint(true, 2020081700, 'auth', 'cas');
+    }
+
     return true;
 }
index 5434984..3e7fed2 100644 (file)
@@ -27,7 +27,9 @@ defined('MOODLE_INTERNAL') || die;
 if ($ADMIN->fulltree) {
 
     if (!function_exists('ldap_connect')) {
-        $settings->add(new admin_setting_heading('auth_casnotinstalled', '', get_string('auth_casnotinstalled', 'auth_cas')));
+        $notify = new \core\output\notification(get_string('auth_casnotinstalled', 'auth_cas'),
+            \core\output\notification::NOTIFY_WARNING);
+        $settings->add(new admin_setting_heading('auth_casnotinstalled', '', $OUTPUT->render($notify)));
     } else {
         // We use a couple of custom admin settings since we need to massage the data before it is inserted into the DB.
         require_once($CFG->dirroot.'/auth/ldap/classes/admin_setting_special_lowercase_configtext.php');
@@ -239,9 +241,9 @@ if ($ADMIN->fulltree) {
                 get_string('auth_ldap_memberattribute', 'auth_ldap'), '', PARAM_RAW));
 
         // Member attribute uses dn.
-        $settings->add(new admin_setting_configtext('auth_cas/memberattribute_isdn',
+        $settings->add(new admin_setting_configselect('auth_cas/memberattribute_isdn',
                 get_string('auth_ldap_memberattribute_isdn_key', 'auth_ldap'),
-                get_string('auth_ldap_memberattribute_isdn', 'auth_ldap'), '', PARAM_RAW));
+                get_string('auth_ldap_memberattribute_isdn', 'auth_ldap'), 0, $yesno));
 
         // Object class.
         $settings->add(new admin_setting_configtext('auth_cas/objectclass',
index 4699c55..e77e268 100644 (file)
@@ -26,7 +26,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2020061500;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2020081700;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2020060900;        // Requires this Moodle version
 $plugin->component = 'auth_cas';        // Full name of the plugin (used for diagnostics)
 
index aae5522..dfdd0b4 100644 (file)
@@ -709,7 +709,7 @@ class auth_plugin_ldap extends auth_plugin_base {
 
             do {
                 if ($ldappagedresults) {
-                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 4.1).
+                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 3.11).
                     if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                         // Before 7.3, use this function that was deprecated in PHP 7.4.
                         ldap_control_paged_result($ldapconnection, $this->config->pagesize, true, $ldapcookie);
@@ -722,7 +722,7 @@ class auth_plugin_ldap extends auth_plugin_base {
                 }
                 if ($this->config->search_sub) {
                     // Use ldap_search to find first user from subtree.
-                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 4.1).
+                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 3.11).
                     if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                         $ldapresult = ldap_search($ldapconnection, $context, $filter, array($this->config->user_attribute));
                     } else {
@@ -731,7 +731,7 @@ class auth_plugin_ldap extends auth_plugin_base {
                     }
                 } else {
                     // Search only in this context.
-                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 4.1).
+                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 3.11).
                     if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                         $ldapresult = ldap_list($ldapconnection, $context, $filter, array($this->config->user_attribute));
                     } else {
@@ -745,7 +745,7 @@ class auth_plugin_ldap extends auth_plugin_base {
                 if ($ldappagedresults) {
                     // Get next server cookie to know if we'll need to continue searching.
                     $ldapcookie = '';
-                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 4.1).
+                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 3.11).
                     if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                         // Before 7.3, use this function that was deprecated in PHP 7.4.
                         $pagedresp = ldap_control_paged_result_response($ldapconnection, $ldapresult, $ldapcookie);
@@ -1554,7 +1554,7 @@ class auth_plugin_ldap extends auth_plugin_base {
 
             do {
                 if ($ldap_pagedresults) {
-                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 4.1).
+                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 3.11).
                     if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                         // Before 7.3, use this function that was deprecated in PHP 7.4.
                         ldap_control_paged_result($ldapconnection, $this->config->pagesize, true, $ldap_cookie);
@@ -1567,7 +1567,7 @@ class auth_plugin_ldap extends auth_plugin_base {
                 }
                 if ($this->config->search_sub) {
                     // Use ldap_search to find first user from subtree.
-                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 4.1).
+                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 3.11).
                     if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                         $ldap_result = ldap_search($ldapconnection, $context, $filter, array($this->config->user_attribute));
                     } else {
@@ -1576,7 +1576,7 @@ class auth_plugin_ldap extends auth_plugin_base {
                     }
                 } else {
                     // Search only in this context.
-                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 4.1).
+                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 3.11).
                     if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                         $ldap_result = ldap_list($ldapconnection, $context, $filter, array($this->config->user_attribute));
                     } else {
@@ -1590,7 +1590,7 @@ class auth_plugin_ldap extends auth_plugin_base {
                 if ($ldap_pagedresults) {
                     // Get next server cookie to know if we'll need to continue searching.
                     $ldap_cookie = '';
-                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 4.1).
+                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 3.11).
                     if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                         // Before 7.3, use this function that was deprecated in PHP 7.4.
                         ldap_control_paged_result_response($ldapconnection, $ldap_result, $ldap_cookie);
index e40963a..bfe0e36 100644 (file)
@@ -47,5 +47,13 @@ function xmldb_auth_ldap_upgrade($oldversion) {
     // Automatically generated Moodle v3.9.0 release upgrade line.
     // Put any upgrade step following this.
 
+    if ($oldversion < 2020081700) {
+        // Normalize the memberattribute_isdn plugin config.
+        set_config('memberattribute_isdn',
+            !empty(get_config('auth_ldap', 'memberattribute_isdn')), 'auth_ldap');
+
+        upgrade_plugin_savepoint(true, 2020081700, 'auth', 'ldap');
+    }
+
     return true;
 }
index da0f7e9..fa074d2 100644 (file)
@@ -57,12 +57,12 @@ $string['auth_ldap_ldap_encoding'] = 'Encoding used by the LDAP server, most lik
 $string['auth_ldap_ldap_encoding_key'] = 'LDAP encoding';
 $string['auth_ldap_login_settings'] = 'Login settings';
 $string['auth_ldap_memberattribute'] = 'Optional: Overrides user member attribute, when users belongs to a group. Usually \'member\'';
-$string['auth_ldap_memberattribute_isdn'] = 'Optional: Overrides handling of member attribute values, either 0 or 1';
+$string['auth_ldap_memberattribute_isdn'] = 'Overrides handling of member attribute values';
 $string['auth_ldap_memberattribute_isdn_key'] = 'Member attribute uses dn';
 $string['auth_ldap_memberattribute_key'] = 'Member attribute';
 $string['auth_ldap_noconnect'] = 'LDAP-module cannot connect to server: {$a}';
 $string['auth_ldap_noconnect_all'] = 'LDAP-module cannot connect to any servers: {$a}';
-$string['auth_ldap_noextension'] = '<em>The PHP LDAP module does not seem to be present. Please ensure it is installed and enabled if you want to use this authentication plugin.</em>';
+$string['auth_ldap_noextension'] = 'The PHP LDAP module does not seem to be present. Please ensure it is installed and enabled if you want to use this authentication plugin.';
 $string['auth_ldap_no_mbstring'] = 'You need the mbstring extension to create users in Active Directory.';
 $string['auth_ldapnotinstalled'] = 'Cannot use LDAP authentication. The PHP LDAP module is not installed.';
 $string['auth_ldap_objectclass'] = 'Optional: Overrides objectClass used to name/search users on ldap_user_type. Usually you don\'t need to change this.';
index f9bc8d9..e9921f6 100644 (file)
@@ -27,7 +27,9 @@ defined('MOODLE_INTERNAL') || die;
 if ($ADMIN->fulltree) {
 
     if (!function_exists('ldap_connect')) {
-        $settings->add(new admin_setting_heading('auth_ldap_noextension', '', get_string('auth_ldap_noextension', 'auth_ldap')));
+        $notify = new \core\output\notification(get_string('auth_ldap_noextension', 'auth_ldap'),
+            \core\output\notification::NOTIFY_WARNING);
+        $settings->add(new admin_setting_heading('auth_ldap_noextension', '', $OUTPUT->render($notify)));
     } else {
 
         // We use a couple of custom admin settings since we need to massage the data before it is inserted into the DB.
@@ -142,9 +144,9 @@ if ($ADMIN->fulltree) {
                 get_string('auth_ldap_memberattribute', 'auth_ldap'), '', PARAM_RAW));
 
         // Member attribute uses dn.
-        $settings->add(new admin_setting_configtext('auth_ldap/memberattribute_isdn',
+        $settings->add(new admin_setting_configselect('auth_ldap/memberattribute_isdn',
                 get_string('auth_ldap_memberattribute_isdn_key', 'auth_ldap'),
-                get_string('auth_ldap_memberattribute_isdn', 'auth_ldap'), '', PARAM_RAW));
+                get_string('auth_ldap_memberattribute_isdn', 'auth_ldap'), 0, $yesno));
 
         // Object class.
         $settings->add(new admin_setting_configtext('auth_ldap/objectclass',
index 61e1f53..9ac96d2 100644 (file)
@@ -25,6 +25,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2020061500;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2020081700;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2020060900;        // Requires this Moodle version
 $plugin->component = 'auth_ldap';       // Full name of the plugin (used for diagnostics)
index 3e2d269..496cf02 100644 (file)
@@ -111,8 +111,8 @@ class copy  {
      *  Take the validated form data and extract the required information for copy operations.
      *
      * @param \stdClass $formdata Data from the validated course copy form.
-     * @throws \moodle_exception
      * @return \stdClass $copydata Data required for course copy operations.
+     * @throws \moodle_exception If one of the required copy fields is missing
      */
     private final function get_copy_data(\stdClass $formdata): \stdClass {
         $copydata = new \stdClass();
@@ -121,7 +121,7 @@ class copy  {
             if (isset($formdata->{$field})) {
                 $copydata->{$field} = $formdata->{$field};
             } else {
-                throw new \moodle_exception('copy_class_field_not_found');
+                throw new \moodle_exception('copyfieldnotfound', 'backup', '', null, $field);
             }
         }
 
index 34794a8..6856101 100644 (file)
@@ -162,15 +162,17 @@ class copy_form extends \moodleform {
         }
 
         // Keep source course user data.
+        $mform->addElement('select', 'userdata', get_string('userdata', 'backup'),
+            [0 => get_string('no'), 1 => get_string('yes')]);
+        $mform->setDefault('userdata', 0);
+        $mform->addHelpButton('userdata', 'userdata', 'backup');
+
         $requiredcapabilities = array(
             'moodle/restore:createuser', 'moodle/backup:userinfo', 'moodle/restore:userinfo'
         );
-        if (has_all_capabilities($requiredcapabilities, $coursecontext)) {
-            $dataarray = array();
-            $dataarray[] = $mform->createElement('advcheckbox', 'userdata',
-                get_string('enable'), '', array('group' => 1), array(0, 1));
-            $mform->addGroup($dataarray, 'dataarray', get_string('userdata', 'backup'), ' ', false);
-            $mform->addHelpButton('dataarray', 'userdata', 'backup');
+        if (!has_all_capabilities($requiredcapabilities, $coursecontext)) {
+            $mform->hardFreeze('userdata');
+            $mform->setConstants('userdata', 0);
         }
 
         // Keep manual enrolments.
index 9763156..491b555 100644 (file)
@@ -86,24 +86,6 @@ $string['hiddencourses'] = 'Removed from view';
 $string['show'] = 'Restore to view';
 $string['privacy:request:preference:set'] = 'The value of the setting \'{$a->name}\' was \'{$a->value}\'';
 
-// Deprecated since Moodle 3.6.
-$string['defaulttab'] = 'Default tab';
-$string['defaulttab_desc'] = 'The tab that will be displayed when a user first views their course overview. When returning to their course overview, the user\'s active tab is remembered.';
-$string['morecourses'] = 'More courses';
-$string['nocoursesinprogress'] = 'No in progress courses';
-$string['nocoursesfuture'] = 'No future courses';
-$string['nocoursespast'] = 'No past courses';
-$string['noevents'] = 'No upcoming activities due';
-$string['next30days'] = 'Next 30 days';
-$string['next7days'] = 'Next 7 days';
-$string['recentlyoverdue'] = 'Recently overdue';
-$string['sortbycourses'] = 'Sort by courses';
-$string['sortbydates'] = 'Sort by dates';
-$string['timeline'] = 'Timeline';
-$string['viewcoursename'] = 'View course {$a}';
-$string['privacy:metadata:overviewlasttab'] = 'This stores the last tab selected by the user on the overview block.';
-$string['viewcourse'] = 'View course';
-
 // Deprecated since Moodle 3.7.
 $string['complete'] = 'complete';
 $string['nocourses'] = 'No courses';
index fb6f86c..c70ee8b 100644 (file)
@@ -1,17 +1,2 @@
-defaulttab,block_myoverview
-defaulttab_desc,block_myoverview
-morecourses,block_myoverview
-nocoursesinprogress,block_myoverview
-nocoursesfuture,block_myoverview
-nocoursespast,block_myoverview
-noevents,block_myoverview
-next30days,block_myoverview
-next7days,block_myoverview
-recentlyoverdue,block_myoverview
-sortbycourses,block_myoverview
-sortbydates,block_myoverview
-timeline,block_myoverview
-viewcoursename,block_myoverview
-privacy:metadata:overviewlasttab,block_myoverview
 nocourses,block_myoverview
-complete,block_myoverview
\ No newline at end of file
+complete,block_myoverview
index 7d548f9..e943e9c 100644 (file)
@@ -369,7 +369,7 @@ class calendar_event_exporter extends event_exporter_base {
      * and the module's minimum timestamp limit.
      *
      * @deprecated since Moodle 3.6. Please use get_timestamp_min_limit().
-     * @todo final deprecation. To be removed in Moodle 4.0
+     * @todo final deprecation. To be removed in Moodle 3.10
      * @param DateTimeInterface $starttime The event start time
      * @param array $min The module's minimum limit for the event
      * @return array Returns an array with mindaytimestamp and mindayerror keys.
@@ -385,7 +385,7 @@ class calendar_event_exporter extends event_exporter_base {
      * and the module's maximum timestamp limit.
      *
      * @deprecated since Moodle 3.6. Please use get_timestamp_max_limit().
-     * @todo final deprecation. To be removed in Moodle 4.0
+     * @todo final deprecation. To be removed in Moodle 3.10
      * @param DateTimeInterface $starttime The event start time
      * @param array $max The module's maximum limit for the event
      * @return array Returns an array with maxdaytimestamp and maxdayerror keys.
index 541f1b2..9755765 100644 (file)
@@ -1281,6 +1281,12 @@ class calendar_information {
      * @param string|null $view preference view options (eg: day, month, upcoming)
      */
     public function add_sidecalendar_blocks(core_calendar_renderer $renderer, $showfilters=false, $view=null) {
+        global $PAGE;
+
+        if (!has_capability('moodle/block:view', $PAGE->context) ) {
+            return;
+        }
+
         if ($showfilters) {
             $filters = new block_contents();
             $filters->content = $renderer->event_filter();
index 3704b97..aa876f3 100644 (file)
@@ -298,7 +298,7 @@ class content_item_readonly_repository implements content_item_readonly_reposito
                 }
 
             } else if (component_callback_exists('mod_' . $mod->name, 'get_shortcuts')) {
-                // TODO: MDL-68011 this block needs to be removed in 4.3.
+                // TODO: MDL-68011 this block needs to be removed in 4.1.
                 debugging('The callback get_shortcuts has been deprecated. Please use get_course_content_items and
                     get_all_content_items instead. Some features of the activity chooser, such as favourites and recommendations
                     are not supported when providing content items via the deprecated callback.');
index 605f7c0..9a78691 100644 (file)
@@ -27,7 +27,7 @@
  *   - use "su" if "sudo" not available
  *
  * @deprecated since Moodle 3.7 MDL-59986 - please do not use this CLI script any more, use scheduled task instead.
- * @todo       MDL-63266 This will be deleted in Moodle 4.1.
+ * @todo       MDL-63266 This will be deleted in Moodle 3.11.
  * @package    enrol_database
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
index 3066945..83e88ec 100644 (file)
@@ -99,7 +99,7 @@ $string['objectclass_key'] = 'Object class';
 $string['ok'] = "OK!\n";
 $string['opt_deref'] = 'If the group membership contains distinguished names, specify how aliases are handled during a search. Select one of the following values: \'No\' (LDAP_DEREF_NEVER) or \'Yes\' (LDAP_DEREF_ALWAYS).';
 $string['opt_deref_key'] = 'Dereference aliases';
-$string['phpldap_noextension'] = '<em>The PHP LDAP module does not seem to be present. Please ensure it is installed and enabled if you want to use this enrolment plugin.</em>';
+$string['phpldap_noextension'] = 'The PHP LDAP module does not seem to be present. Please ensure it is installed and enabled if you want to use this enrolment plugin.';
 $string['pluginname'] = 'LDAP enrolments';
 $string['pluginname_desc'] = '<p>You can use an LDAP server to control your enrolments. It is assumed your LDAP tree contains groups that map to the courses, and that each of those groups/courses will have membership entries to map to students.</p><p>It is assumed that courses are defined as groups in LDAP, with each group having multiple membership fields (<em>member</em> or <em>memberUid</em>) that contain a uniqueidentification of the user.</p><p>To use LDAP enrolment, your users <strong>must</strong> to have a valid  idnumber field. The LDAP groups must have that idnumber in the member fields for a user to be enrolled in the course. This will usually work well if you are already using LDAP Authentication.</p><p>Enrolments will be updated when the user logs in. You can also run a script to keep enrolments in synch. Look in <em>enrol/ldap/cli/sync.php</em>.</p><p>This plugin can also be set to automatically create new courses when new groups appear in LDAP.</p>';
 $string['pluginnotenabled'] = 'Plugin not enabled!';
index 8c4672d..5b44a5a 100644 (file)
@@ -389,7 +389,7 @@ class enrol_ldap_plugin extends enrol_plugin {
                 $flat_records = array();
                 do {
                     if ($ldap_pagedresults) {
-                        // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 4.1).
+                        // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 3.11).
                         if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                             // Before 7.3, use this function that was deprecated in PHP 7.4.
                             ldap_control_paged_result($this->ldapconnection, $this->config->pagesize, true, $ldap_cookie);
@@ -403,7 +403,7 @@ class enrol_ldap_plugin extends enrol_plugin {
 
                     if ($this->config->course_search_sub) {
                         // Use ldap_search to find first user from subtree
-                        // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 4.1).
+                        // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 3.11).
                         if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                             $ldap_result = @ldap_search($this->ldapconnection, $ldap_context,
                                 $ldap_search_pattern, $ldap_fields_wanted);
@@ -414,7 +414,7 @@ class enrol_ldap_plugin extends enrol_plugin {
                         }
                     } else {
                         // Search only in this context
-                        // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 4.1).
+                        // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 3.11).
                         if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                             $ldap_result = @ldap_list($this->ldapconnection, $ldap_context,
                                 $ldap_search_pattern, $ldap_fields_wanted);
@@ -431,7 +431,7 @@ class enrol_ldap_plugin extends enrol_plugin {
                     if ($ldap_pagedresults) {
                         // Get next server cookie to know if we'll need to continue searching.
                         $ldap_cookie = '';
-                        // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 4.1).
+                        // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 3.11).
                         if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                             // Before 7.3, use this function that was deprecated in PHP 7.4.
                             ldap_control_paged_result_response($this->ldapconnection, $ldap_result, $ldap_cookie);
@@ -806,7 +806,7 @@ class enrol_ldap_plugin extends enrol_plugin {
             $flat_records = array();
             do {
                 if ($ldap_pagedresults) {
-                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 4.1).
+                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 3.11).
                     if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                         // Before 7.3, use this function that was deprecated in PHP 7.4.
                         ldap_control_paged_result($this->ldapconnection, $this->config->pagesize, true, $ldap_cookie);
@@ -820,7 +820,7 @@ class enrol_ldap_plugin extends enrol_plugin {
 
                 if ($this->get_config('course_search_sub')) {
                     // Use ldap_search to find first user from subtree
-                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 4.1).
+                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 3.11).
                     if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                         $ldap_result = @ldap_search($this->ldapconnection, $context,
                             $ldap_search_pattern, $ldap_fields_wanted);
@@ -831,7 +831,7 @@ class enrol_ldap_plugin extends enrol_plugin {
                     }
                 } else {
                     // Search only in this context
-                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 4.1).
+                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 3.11).
                     if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                         $ldap_result = @ldap_list($this->ldapconnection, $context,
                             $ldap_search_pattern, $ldap_fields_wanted);
@@ -849,7 +849,7 @@ class enrol_ldap_plugin extends enrol_plugin {
                 if ($ldap_pagedresults) {
                     // Get next server cookie to know if we'll need to continue searching.
                     $ldap_cookie = '';
-                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 4.1).
+                    // TODO: Remove the old branch of code once PHP 7.3.0 becomes required (Moodle 3.11).
                     if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                         // Before 7.3, use this function that was deprecated in PHP 7.4.
                         ldap_control_paged_result_response($this->ldapconnection, $ldap_result, $ldap_cookie);
index 2b668a5..c577859 100644 (file)
@@ -27,12 +27,15 @@ defined('MOODLE_INTERNAL') || die();
 
 if ($ADMIN->fulltree) {
 
-    //--- heading ---
-    $settings->add(new admin_setting_heading('enrol_ldap_settings', '', get_string('pluginname_desc', 'enrol_ldap')));
-
     if (!function_exists('ldap_connect')) {
-        $settings->add(new admin_setting_heading('enrol_phpldap_noextension', '', get_string('phpldap_noextension', 'enrol_ldap')));
+        $notify = new \core\output\notification(get_string('phpldap_noextension', 'enrol_ldap'),
+            \core\output\notification::NOTIFY_WARNING);
+        $settings->add(new admin_setting_heading('enrol_phpldap_noextension', '', $OUTPUT->render($notify)));
+        $settings->add(new admin_setting_heading('enrol_ldap_settings', '', get_string('pluginname_desc', 'enrol_ldap')));
     } else {
+
+        $settings->add(new admin_setting_heading('enrol_ldap_settings', '', get_string('pluginname_desc', 'enrol_ldap')));
+
         require_once($CFG->dirroot.'/enrol/ldap/settingslib.php');
         require_once($CFG->libdir.'/ldaplib.php');
 
index d9ce6f7..d6d3079 100644 (file)
@@ -31,83 +31,24 @@ defined('MOODLE_INTERNAL') || die();
  * Activity name filtering
  */
 class filter_activitynames extends moodle_text_filter {
-    // Trivial-cache - keyed on $cachedcourseid and $cacheduserid.
-    static $activitylist = null;
-    static $cachedcourseid;
-    static $cacheduserid;
 
     function filter($text, array $options = array()) {
-        global $USER; // Since 2.7 we can finally start using globals in filters.
-
         $coursectx = $this->context->get_course_context(false);
         if (!$coursectx) {
             return $text;
         }
         $courseid = $coursectx->instanceid;
 
-        // Initialise/invalidate our trivial cache if dealing with a different course.
-        if (!isset(self::$cachedcourseid) || self::$cachedcourseid !== (int)$courseid) {
-            self::$activitylist = null;
-        }
-        self::$cachedcourseid = (int)$courseid;
-        // And the same for user id.
-        if (!isset(self::$cacheduserid) || self::$cacheduserid !== (int)$USER->id) {
-            self::$activitylist = null;
-        }
-        self::$cacheduserid = (int)$USER->id;
-
-        /// It may be cached
-
-        if (is_null(self::$activitylist)) {
-            self::$activitylist = array();
-
-            $modinfo = get_fast_modinfo($courseid);
-            if (!empty($modinfo->cms)) {
-                self::$activitylist = array(); // We will store all the created filters here.
-
-                // Create array of visible activities sorted by the name length (we are only interested in properties name and url).
-                $sortedactivities = array();
-                foreach ($modinfo->cms as $cm) {
-                    // Use normal access control and visibility, but exclude labels and hidden activities.
-                    if ($cm->visible and $cm->has_view() and $cm->uservisible) {
-                        $sortedactivities[] = (object)array(
-                            'name' => $cm->name,
-                            'url' => $cm->url,
-                            'id' => $cm->id,
-                            'namelen' => -strlen($cm->name), // Negative value for reverse sorting.
-                        );
-                    }
-                }
-                // Sort activities by the length of the activity name in reverse order.
-                core_collator::asort_objects_by_property($sortedactivities, 'namelen', core_collator::SORT_NUMERIC);
-
-                foreach ($sortedactivities as $cm) {
-                    $title = s(trim(strip_tags($cm->name)));
-                    $currentname = trim($cm->name);
-                    $entitisedname  = s($currentname);
-                    // Avoid empty or unlinkable activity names.
-                    if (!empty($title)) {
-                        $href_tag_begin = html_writer::start_tag('a',
-                                array('class' => 'autolink', 'title' => $title,
-                                    'href' => $cm->url));
-                        self::$activitylist[$cm->id] = new filterobject($currentname, $href_tag_begin, '</a>', false, true);
-                        if ($currentname != $entitisedname) {
-                            // If name has some entity (&amp; &quot; &lt; &gt;) add that filter too. MDL-17545.
-                            self::$activitylist[$cm->id.'-e'] = new filterobject($entitisedname, $href_tag_begin, '</a>', false, true);
-                        }
-                    }
-                }
-            }
-        }
+        $activitylist = $this->get_cached_activity_list($courseid);
 
         $filterslist = array();
-        if (self::$activitylist) {
+        if (!empty($activitylist)) {
             $cmid = $this->context->instanceid;
-            if ($this->context->contextlevel == CONTEXT_MODULE && isset(self::$activitylist[$cmid])) {
+            if ($this->context->contextlevel == CONTEXT_MODULE && isset($activitylist[$cmid])) {
                 // remove filterobjects for the current module
-                $filterslist = array_values(array_diff_key(self::$activitylist, array($cmid => 1, $cmid.'-e' => 1)));
+                $filterslist = array_values(array_diff_key($activitylist, array($cmid => 1, $cmid.'-e' => 1)));
             } else {
-                $filterslist = array_values(self::$activitylist);
+                $filterslist = array_values($activitylist);
             }
         }
 
@@ -117,4 +58,76 @@ class filter_activitynames extends moodle_text_filter {
             return $text;
         }
     }
+
+    /**
+     * Get all the cached activity list for a course
+     *
+     * @param int $courseid id of the course
+     * @return filterobject[] the activities
+     */
+    protected function get_cached_activity_list($courseid) {
+        global $USER;
+        $cached = cache::make_from_params(cache_store::MODE_REQUEST, 'filter', 'activitynames');
+
+        // Return cached activity list.
+        if ($cached->get('cachecourseid') == $courseid && $cached->get('cacheuserid') == $USER->id) {
+            return $cached->get('activitylist');
+        }
+
+        // Not cached yet, get activity list and set cache.
+        $activitylist = $this->get_activity_list($courseid);
+        $cached->set('cacheuserid', $USER->id);
+        $cached->set('cachecourseid', $courseid);
+        $cached->set('activitylist', $activitylist);
+        return $activitylist;
+    }
+
+    /**
+     * Get all the activity list for a course
+     *
+     * @param int $courseid id of the course
+     * @return filterobject[] the activities
+     */
+    protected function get_activity_list($courseid) {
+        $activitylist = array();
+
+        $modinfo = get_fast_modinfo($courseid);
+        if (!empty($modinfo->cms)) {
+            $activitylist = array(); // We will store all the created filters here.
+
+            // Create array of visible activities sorted by the name length (we are only interested in properties name and url).
+            $sortedactivities = array();
+            foreach ($modinfo->cms as $cm) {
+                // Use normal access control and visibility, but exclude labels and hidden activities.
+                if ($cm->visible and $cm->has_view() and $cm->uservisible) {
+                    $sortedactivities[] = (object)array(
+                        'name' => $cm->name,
+                        'url' => $cm->url,
+                        'id' => $cm->id,
+                        'namelen' => -strlen($cm->name), // Negative value for reverse sorting.
+                    );
+                }
+            }
+            // Sort activities by the length of the activity name in reverse order.
+            core_collator::asort_objects_by_property($sortedactivities, 'namelen', core_collator::SORT_NUMERIC);
+
+            foreach ($sortedactivities as $cm) {
+                $title = s(trim(strip_tags($cm->name)));
+                $currentname = trim($cm->name);
+                $entitisedname  = s($currentname);
+                // Avoid empty or unlinkable activity names.
+                if (!empty($title)) {
+                    $hreftagbegin = html_writer::start_tag('a',
+                        array('class' => 'autolink', 'title' => $title,
+                            'href' => $cm->url));
+                    $activitylist[$cm->id] = new filterobject($currentname, $hreftagbegin, '</a>', false, true);
+                    if ($currentname != $entitisedname) {
+                        // If name has some entity (&amp; &quot; &lt; &gt;) add that filter too. MDL-17545.
+                        $activitylist[$cm->id.'-e'] = new filterobject($entitisedname, $hreftagbegin, '</a>', false, true);
+                    }
+                }
+            }
+        }
+        return $activitylist;
+    }
 }
index b21c924..98776b7 100644 (file)
@@ -102,4 +102,56 @@ class filter_activitynames_filter_testcase extends advanced_testcase {
         $this->assertEquals($page->cmid, $matches[2][0]);
         $this->assertEquals($page->name, $matches[3][0]);
     }
+
+    public function test_cache() {
+        $this->resetAfterTest(true);
+
+        // Create a test courses.
+        $course1 = $this->getDataGenerator()->create_course();
+        $course2 = $this->getDataGenerator()->create_course();
+        $context1 = context_course::instance($course1->id);
+        $context2 = context_course::instance($course2->id);
+
+        // Create page 1.
+        $page1 = $this->getDataGenerator()->create_module('page',
+            ['course' => $course1->id, 'name' => 'Test 1']);
+        // Format text with page 1 in HTML.
+        $html = '<p>Please read the two pages Test 1 and Test 2.</p>';
+        $filtered1 = format_text($html, FORMAT_HTML, array('context' => $context1));
+        // Find all the activity links in the result.
+        $matches = [];
+        preg_match_all('~<a class="autolink" title="([^"]*)" href="[^"]*/mod/page/view.php\?id=([0-9]+)">([^<]*)</a>~',
+            $filtered1, $matches);
+        // There should be 1 link.
+        $this->assertCount(1, $matches[1]);
+        $this->assertEquals($page1->name, $matches[1][0]);
+
+        // Create page 2.
+        $page2 = $this->getDataGenerator()->create_module('page',
+        ['course' => $course1->id, 'name' => 'Test 2']);
+        // Filter the text again.
+        $filtered2 = format_text($html, FORMAT_HTML, array('context' => $context1));
+        // The filter result does not change due to caching.
+        $this->assertEquals($filtered1, $filtered2);
+
+        // Change context, so that cache for course 1 is cleared.
+        $filtered3 = format_text($html, FORMAT_HTML, array('context' => $context2));
+        $this->assertNotEquals($filtered1, $filtered3);
+        $matches = [];
+        preg_match_all('~<a class="autolink" title="([^"]*)" href="[^"]*/mod/page/view.php\?id=([0-9]+)">([^<]*)</a>~',
+            $filtered3, $matches);
+        // There should be no links.
+        $this->assertCount(0, $matches[1]);
+
+        // Filter the text for course 1.
+        $filtered4 = format_text($html, FORMAT_HTML, array('context' => $context1));
+        // Find all the activity links in the result.
+        $matches = [];
+        preg_match_all('~<a class="autolink" title="([^"]*)" href="[^"]*/mod/page/view.php\?id=([0-9]+)">([^<]*)</a>~',
+            $filtered4, $matches);
+        // There should be 2 links.
+        $this->assertCount(2, $matches[1]);
+        $this->assertEquals($page1->name, $matches[1][0]);
+        $this->assertEquals($page2->name, $matches[1][1]);
+    }
 }
index 4bf99b4..f13cc48 100644 (file)
@@ -169,6 +169,7 @@ $string['copycoursetitle'] = 'Copy course: {$a}';
 $string['copydest'] = 'Destination';
 $string['copyingcourse'] = 'Course copying in progress';
 $string['copyingcourseshortname'] = 'copying';
+$string['copyfieldnotfound'] = 'A required field was not found';
 $string['copyformfail'] = 'AJAX submission of course copy form has failed.';
 $string['copyop'] = 'Current operation';
 $string['copyprogressheading'] = 'Course copies in progress';
index 62e4b4f..a98db31 100644 (file)
@@ -572,13 +572,6 @@ $string['version_help'] = 'The version field may be used to keep track of the ba
 $string['warnexpired'] = ' (This badge has expired!)';
 $string['year'] = 'Year(s)';
 
-// Deprecated since Moodle 3.6.
-$string['error:invalidbadgeurl'] = 'Invalid issuer URL format. The URL should have a prefix http:// or https://.';
-$string['backpackbadges'] = 'You have {$a->totalbadges} badge(s) displayed from {$a->totalcollections} collection(s). <a href="mybackpack.php">Change backpack settings</a>.';
-$string['error:nogroups'] = '<p>There are no public collections of badges available in your backpack. </p> <p>Only public collections are shown. <a href="https://backpack.openbadges.org">Visit your backpack</a> to create some public collections.</p>';
-$string['nobackpackbadges'] = 'There are no badges in the collections you have selected. <a href="mybackpack.php">Add more collections</a>.';
-$string['nobackpackcollections'] = 'No badge collections have been selected. <a href="mybackpack.php">Add collections</a>.';
-
 // Deprecated since Moodle 3.9.
 $string['editsettings'] = 'Edit settings';
 $string['sitebackpackverify'] = 'Backpack connection';
index 949af22..f5887d5 100644 (file)
@@ -5,28 +5,8 @@ myfilesmanage,core
 mypreferences,core_grades
 myprofile,core
 viewallmyentries,core_blog
-formattexttype,core
-currentlyselectedusers,core
-emailuserhasnone,core
-emaildisplayhidden,core
-sitemessage,core
-coursemessage,core
-addedrecip,core
-addedrecips,core
 messagecontactrequestsnotification,core_message
 messagecontactrequestsnotificationsubject,core_message
-messagingdisabled,core_message
-messagedselectedcountusersfailed,core
-backtoparticipants,core
-keepsearching,core
-allfieldsrequired,core
-previewhtml,core
-messagedselecteduserfailed,core
-eventmessagecontactblocked,core_message
-eventmessagecontactunblocked,core_message
-userisblockingyou,core_message
-userisblockingyounoncontact,core_message
-error:invalidbadgeurl,core_badges
 nomessages,core_message
 searchallavailablecourses_desc,core_admin
 search:mycourse,core_search
@@ -41,10 +21,6 @@ undockall,core_block
 undockblock,core_block
 undockitem,core_block
 canceledit,core_message
-backpackbadges,core_badges
-nobackpackbadges,core_badges
-nobackpackcollections,core_badges
-error:nogroups,core_badges
 purgedefinitionsuccess,core_cache
 purgestoresuccess,core_cache
 eventrolecapabilitiesupdated,core_role
index 61ed582..77a5b18 100644 (file)
@@ -262,8 +262,6 @@ $string['unreadnewgroupconversationmessage'] = 'New message from {$a->name} in {
 $string['unreadnewmessage'] = 'New message from {$a}';
 $string['useentertosend'] = 'Use enter to send';
 $string['usercantbemessaged'] = 'You can\'t message {$a} due to their message preferences. Try adding them as a contact.';
-$string['userisblockingyou'] = 'This user has blocked you from sending messages to them';
-$string['userisblockingyounoncontact'] = '{$a} only accepts messages from their contacts.';
 $string['userwouldliketocontactyou'] = '{$a} would like to contact you';
 $string['viewfullnotification'] = 'View full notification';
 $string['viewmessageswith'] = 'View messages with {$a}';
@@ -275,13 +273,6 @@ $string['you'] = 'You:';
 $string['youhaveblockeduser'] = 'You have blocked this user.';
 $string['yourcontactrequestpending'] = 'Your contact request is pending with {$a}';
 
-// Deprecated since Moodle 3.6.
-$string['eventmessagecontactblocked'] = 'Message contact blocked';
-$string['eventmessagecontactunblocked'] = 'Message contact unblocked';
-$string['messagingdisabled'] = 'Messaging is disabled on this site, emails will be sent instead';
-$string['userisblockingyou'] = 'This user has blocked you from sending messages to them.';
-$string['userisblockingyounoncontact'] = '{$a} only accepts messages from their contacts.';
-
 // Deprecated since Moodle 3.7.
 $string['nomessages'] = 'No messages';
 $string['outputdisabled'] = 'Output disabled';
index 9d7065e..2d8bd72 100644 (file)
@@ -2283,22 +2283,6 @@ $string['yourwordforx'] = 'Your word for \'{$a}\'';
 $string['zippingbackup'] = 'Zipping backup';
 $string['deprecatedeventname'] = '{$a} (no longer in use)';
 
-// Deprecated since Moodle 3.6.
-$string['addedrecip'] = 'Added {$a} new recipient';
-$string['addedrecips'] = 'Added {$a} new recipients';
-$string['allfieldsrequired'] = 'All fields are required';
-$string['backtoparticipants'] = 'Back to participants list';
-$string['currentlyselectedusers'] = 'Currently selected users';
-$string['coursemessage'] = 'Message course users';
-$string['emaildisplayhidden'] = 'Email hidden';
-$string['emailuserhasnone'] = 'There is no email address for the user.';
-$string['formattexttype'] = 'Formatting';
-$string['keepsearching'] = 'Keep searching';
-$string['messagedselectedcountusersfailed'] = 'A problem occurred and {$a} messages have not been sent.';
-$string['messagedselecteduserfailed'] = 'The message was not sent to user {$a->fullname}.';
-$string['previewhtml'] = 'HTML format preview';
-$string['sitemessage'] = 'Message users';
-
 // Deprecated since Moodle 3.9.
 $string['participantscount'] = 'Number of participants: {$a}';
 $string['userfilterplaceholder'] = 'Search keyword or select filter';
index 8452614..05791e5 100644 (file)
@@ -57,6 +57,8 @@ $string['cannotdelete'] = 'Cannot delete this file.';
 $string['cannotdownload'] = 'Cannot download this file';
 $string['cannotdownloaddir'] = 'Cannot download this folder';
 $string['cannotinitplugin'] = 'Call plugin_init failed';
+$string['cannotunzipcontentunreadable'] = 'Cannot unzip this file because the contents of the file cannot be read.';
+$string['cannotunzipquotaexceeded'] = 'Cannot unzip this file because the maximum size allowed in this draft area will be exceeded.';
 $string['cleancache'] = 'Clean my cache files';
 $string['close'] = 'Close';
 $string['commonrepositorysettings'] = 'Common repository settings';
index 9c28d5e..9da8476 100644 (file)
@@ -36,6 +36,7 @@ $string['filtersetmatchdescription'] = 'How multiple filters should be combined'
 $string['match'] = 'Match';
 $string['matchofthefollowing'] = 'of the following:';
 $string['moodlenetprofile'] = 'MoodleNet profile';
+$string['moodlenetprofile_help'] = 'This field is to link your MoodleNet profile to Moodle. It expects a WebFinger compliant URI';
 $string['placeholdertypeorselect'] = 'Type or select...';
 $string['placeholdertype'] = 'Type...';
 $string['privacy:courserequestpath'] = 'Requested courses';
index ab6f09e..46652ef 100644 (file)
@@ -521,6 +521,16 @@ function has_capability($capability, context $context, $user = null, $doanything
         }
     }
 
+    if (!empty($USER->loginascontext)) {
+        // The current user is logged in as another user and can assume their identity at or below the `loginascontext`
+        // defined in the USER session.
+        // The user may not assume their identity at any other location.
+        if (!$USER->loginascontext->is_parent_of($context, true)) {
+            // The context being checked is not the specified context, or one of its children.
+            return false;
+        }
+    }
+
     // Find out if user is admin - it is not possible to override the doanything in any way
     // and it is not possible to switch to admin role either.
     if ($doanything) {
@@ -5590,6 +5600,30 @@ abstract class context extends stdClass implements IteratorAggregate {
         return $result;
     }
 
+    /**
+     * Determine if the current context is a parent of the possible child.
+     *
+     * @param   context $possiblechild
+     * @param   bool $includeself Whether to check the current context
+     * @return  bool
+     */
+    public function is_parent_of(context $possiblechild, bool $includeself): bool {
+        // A simple substring check is used on the context path.
+        // The possible child's path is used as a haystack, with the current context as the needle.
+        // The path is prefixed with '+' to ensure that the parent always starts at the top.
+        // It is suffixed with '+' to ensure that parents are not included.
+        // The needle always suffixes with a '/' to ensure that the contextid uses a complete match (i.e. 142/ instead of 14).
+        // The haystack is suffixed with '/+' if $includeself is true to allow the current context to match.
+        // The haystack is suffixed with '+' if $includeself is false to prevent the current context from matching.
+        $haystacksuffix = $includeself ? '/+' : '+';
+
+        $strpos = strpos(
+            "+{$possiblechild->path}{$haystacksuffix}",
+            "+{$this->path}/"
+        );
+        return $strpos === 0;
+    }
+
     /**
      * Returns parent contexts of this context in reversed order, i.e. parent first,
      * then grand parent, etc.
@@ -5614,6 +5648,30 @@ abstract class context extends stdClass implements IteratorAggregate {
         return $result;
     }
 
+    /**
+     * Determine if the current context is a child of the possible parent.
+     *
+     * @param   context $possibleparent
+     * @param   bool $includeself Whether to check the current context
+     * @return  bool
+     */
+    public function is_child_of(context $possibleparent, bool $includeself): bool {
+        // A simple substring check is used on the context path.
+        // The current context is used as a haystack, with the possible parent as the needle.
+        // The path is prefixed with '+' to ensure that the parent always starts at the top.
+        // It is suffixed with '+' to ensure that children are not included.
+        // The needle always suffixes with a '/' to ensure that the contextid uses a complete match (i.e. 142/ instead of 14).
+        // The haystack is suffixed with '/+' if $includeself is true to allow the current context to match.
+        // The haystack is suffixed with '+' if $includeself is false to prevent the current context from matching.
+        $haystacksuffix = $includeself ? '/+' : '+';
+
+        $strpos = strpos(
+            "+{$this->path}{$haystacksuffix}",
+            "+{$possibleparent->path}/"
+        );
+        return $strpos === 0;
+    }
+
     /**
      * Returns parent context ids of this context in reversed order, i.e. parent first,
      * then grand parent, etc.
index b93d393..3a7045d 100644 (file)
@@ -3276,7 +3276,7 @@ class admin_setting_configselect extends admin_setting {
      * an empty string '' if the value is OK, or an error message if not.
      *
      * @param callable|null $validatefunction Validate function or null to clear
-     * @since Moodle 4.0
+     * @since Moodle 3.10
      */
     public function set_validate_function(?callable $validatefunction = null) {
         $this->validatefunction = $validatefunction;
@@ -3362,7 +3362,7 @@ class admin_setting_configselect extends admin_setting {
      *
      * @param string $data New value being set
      * @return string Empty string if valid, or error message text
-     * @since Moodle 4.0
+     * @since Moodle 3.10
      */
     protected function validate_setting(string $data): string {
         // If validation function is specified, call it now.
@@ -6538,7 +6538,7 @@ class admin_page_managemessageoutputs extends admin_externalpage {
  * Default message outputs configuration
  *
  * @deprecated since Moodle 3.7 MDL-64495. Please use admin_page_managemessageoutputs instead.
- * @todo       MDL-64866 This will be deleted in Moodle 4.1.
+ * @todo       MDL-64866 This will be deleted in Moodle 3.11.
  *
  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -6547,7 +6547,7 @@ class admin_page_defaultmessageoutputs extends admin_page_managemessageoutputs {
      * Calls parent::__construct with specific arguments
      *
      * @deprecated since Moodle 3.7 MDL-64495. Please use admin_page_managemessageoutputs instead.
-     * @todo       MDL-64866 This will be deleted in Moodle 4.1.
+     * @todo       MDL-64866 This will be deleted in Moodle 3.11.
      */
     public function __construct() {
         global $CFG;
@@ -7348,12 +7348,12 @@ class admin_setting_manageantiviruses extends admin_setting {
  *
  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead.
- * @todo MDL-45184 This class will be deleted in Moodle 4.3.
+ * @todo MDL-45184 This class will be deleted in Moodle 4.1.
  */
 class admin_setting_managelicenses extends admin_setting {
     /**
      * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead.
-     * @todo MDL-45184 This class will be deleted in Moodle 4.3
+     * @todo MDL-45184 This class will be deleted in Moodle 4.1
      */
     public function __construct() {
         global $ADMIN;
@@ -7375,7 +7375,7 @@ class admin_setting_managelicenses extends admin_setting {
      * Always returns true, does nothing
      *
      * @deprecated since Moodle 3.9 MDL-45184.
-     * @todo MDL-45184 This method will be deleted in Moodle 4.3
+     * @todo MDL-45184 This method will be deleted in Moodle 4.1
      *
      * @return true
      */
@@ -7390,7 +7390,7 @@ class admin_setting_managelicenses extends admin_setting {
      * Always returns true, does nothing
      *
      * @deprecated since Moodle 3.9 MDL-45184.
-     * @todo MDL-45184 This method will be deleted in Moodle 4.3
+     * @todo MDL-45184 This method will be deleted in Moodle 4.1
      *
      * @return true
      */
@@ -7405,7 +7405,7 @@ class admin_setting_managelicenses extends admin_setting {
      * Always returns '', does not write anything
      *
      * @deprecated since Moodle 3.9 MDL-45184.
-     * @todo MDL-45184 This method will be deleted in Moodle 4.3
+     * @todo MDL-45184 This method will be deleted in Moodle 4.1
      *
      * @return string Always returns ''
      */
@@ -7421,7 +7421,7 @@ class admin_setting_managelicenses extends admin_setting {
      * Builds the XHTML to display the control
      *
      * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead.
-     * @todo MDL-45184 This method will be deleted in Moodle 4.3
+     * @todo MDL-45184 This method will be deleted in Moodle 4.1
      *
      * @param string $data Unused
      * @param string $query
index 4fd1ed4..7029f5b 100644 (file)
Binary files a/lib/amd/build/checkbox-toggleall.min.js and b/lib/amd/build/checkbox-toggleall.min.js differ
index e7a5c40..f28f66b 100644 (file)
Binary files a/lib/amd/build/checkbox-toggleall.min.js.map and b/lib/amd/build/checkbox-toggleall.min.js.map differ
index 58ca652..f27fea3 100644 (file)
Binary files a/lib/amd/build/form-autocomplete.min.js and b/lib/amd/build/form-autocomplete.min.js differ
index 8ab6a51..7888e0d 100644 (file)
Binary files a/lib/amd/build/form-autocomplete.min.js.map and b/lib/amd/build/form-autocomplete.min.js.map differ
index 473a514..6237eff 100644 (file)
@@ -137,6 +137,45 @@ define(['jquery', 'core/pubsub'], function($, PubSub) {
         toggleSlavesToState(root, toggleGroupName, targetState);
     };
 
+    /**
+     * Toggles the master checkboxes and action elements in a given toggle group.
+     *
+     * @param {jQuery} root The root jQuery element.
+     * @param {String} toggleGroupName The name of the toggle group
+     */
+    var toggleMastersAndActionElements = function(root, toggleGroupName) {
+        var toggleGroupSlaves = getAllSlaveCheckboxes(root, toggleGroupName);
+        if (toggleGroupSlaves.length > 0) {
+            var toggleGroupCheckedSlaves = toggleGroupSlaves.filter(':checked');
+            var targetState = toggleGroupSlaves.length === toggleGroupCheckedSlaves.length;
+
+            // Make sure to toggle the exact master checkbox in the given toggle group.
+            setMasterStates(root, toggleGroupName, targetState, true);
+            // Enable the action elements if there's at least one checkbox checked in the given toggle group.
+            // Disable otherwise.
+            setActionElementStates(root, toggleGroupName, !toggleGroupCheckedSlaves.length);
+        }
+    };
+
+    /**
+     * Returns an array containing every toggle group level of a given toggle group.
+     *
+     * @param {String} toggleGroupName The name of the toggle group
+     * @return {Array} toggleGroupLevels Array that contains every toggle group level of a given toggle group
+     */
+    var getToggleGroupLevels = function(toggleGroupName) {
+        var toggleGroups = toggleGroupName.split(' ');
+        var toggleGroupLevels = [];
+        var toggleGroupLevel = '';
+
+        toggleGroups.forEach(function(toggleGroupName) {
+            toggleGroupLevel += ' ' + toggleGroupName;
+            toggleGroupLevels.push(toggleGroupLevel.trim());
+        });
+
+        return toggleGroupLevels;
+    };
+
     /**
      * Toggles the slave checkboxes to a specific state.
      *
@@ -146,14 +185,26 @@ define(['jquery', 'core/pubsub'], function($, PubSub) {
      */
     var toggleSlavesToState = function(root, toggleGroupName, targetState) {
         var slaves = getAllSlaveCheckboxes(root, toggleGroupName);
+        // Set the slave checkboxes from the masters and manually trigger the native 'change' event.
+        slaves.prop('checked', targetState).trigger('change');
+        // Get all checked slaves after the change of state.
         var checkedSlaves = slaves.filter(':checked');
 
+        // Toggle the master checkbox in the given toggle group.
         setMasterStates(root, toggleGroupName, targetState, false);
-
-        // Set the slave checkboxes from the masters.
-        slaves.prop('checked', targetState);
-        // Trigger 'change' event to toggle other master checkboxes (e.g. parent master checkboxes) and action elements.
-        slaves.trigger('change');
+        // Enable the action elements if there's at least one checkbox checked in the given toggle group. Disable otherwise.
+        setActionElementStates(root, toggleGroupName, !checkedSlaves.length);
+
+        // Get all toggle group levels and toggle accordingly all parent master checkboxes and action elements from each
+        // level. Exclude the given toggle group (toggleGroupName) as the master checkboxes and action elements from this
+        // level have been already toggled.
+        var toggleGroupLevels = getToggleGroupLevels(toggleGroupName)
+            .filter(toggleGroupLevel => toggleGroupLevel !== toggleGroupName);
+
+        toggleGroupLevels.forEach(function(toggleGroupLevel) {
+            // Toggle the master checkboxes action elements in the given toggle group level.
+            toggleMastersAndActionElements(root, toggleGroupLevel);
+        });
 
         PubSub.publish(events.checkboxToggled, {
             root: root,
@@ -190,33 +241,24 @@ define(['jquery', 'core/pubsub'], function($, PubSub) {
     var toggleMastersFromSlaves = function(e) {
         var root = e.data.root;
         var target = $(e.target);
+        var toggleGroupName = target.data('togglegroup');
+        var slaves = getAllSlaveCheckboxes(root, toggleGroupName);
+        var checkedSlaves = slaves.filter(':checked');
 
-        var toggleGroups = target.data('togglegroup').split(' ');
-        var toggleGroupLevels = [];
-        var toggleGroupLevel = '';
-        toggleGroups.forEach(function(toggleGroupName) {
-            toggleGroupLevel += ' ' + toggleGroupName;
-            toggleGroupLevels.push(toggleGroupLevel.trim());
+        // Get all toggle group levels for the given toggle group and toggle accordingly all master checkboxes
+        // and action elements from each level.
+        var toggleGroupLevels = getToggleGroupLevels(toggleGroupName);
+        toggleGroupLevels.forEach(function(toggleGroupLevel) {
+            // Toggle the master checkboxes action elements in the given toggle group level.
+            toggleMastersAndActionElements(root, toggleGroupLevel);
         });
 
-        toggleGroupLevels.forEach(function(toggleGroupName) {
-            var slaves = getAllSlaveCheckboxes(root, toggleGroupName);
-            var checkedSlaves = slaves.filter(':checked');
-            var targetState = (slaves.length === checkedSlaves.length);
-
-            // Make sure to toggle the exact master checkbox.
-            setMasterStates(root, toggleGroupName, targetState, true);
-
-            // Enable action elements when there's at least one checkbox checked. Disable otherwise.
-            setActionElementStates(root, toggleGroupName, !checkedSlaves.length);
-
-            PubSub.publish(events.checkboxToggled, {
-                root: root,
-                toggleGroupName: toggleGroupName,
-                slaves: slaves,
-                checkedSlaves: checkedSlaves,
-                anyChecked: !!checkedSlaves.length,
-            });
+        PubSub.publish(events.checkboxToggled, {
+            root: root,
+            toggleGroupName: toggleGroupName,
+            slaves: slaves,
+            checkedSlaves: checkedSlaves,
+            anyChecked: !!checkedSlaves.length,
         });
     };
 
@@ -281,7 +323,7 @@ define(['jquery', 'core/pubsub'], function($, PubSub) {
 
             var root = $(document.body);
             root.on('click', '[data-action="toggle"][data-toggle="master"]', {root: root}, toggleSlavesFromMasters);
-            root.on('change', '[data-action="toggle"][data-toggle="slave"]', {root: root}, toggleMastersFromSlaves);
+            root.on('click', '[data-action="toggle"][data-toggle="slave"]', {root: root}, toggleMastersFromSlaves);
         }
     };
 
index f90ff66..71b7e95 100644 (file)
@@ -36,7 +36,9 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
         SPACE: 32,
         ESCAPE: 27,
         COMMA: 44,
-        UP: 38
+        UP: 38,
+        LEFT: 37,
+        RIGHT: 39
     };
 
     var uniqueId = Date.now();
@@ -806,10 +808,24 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
             // Remove it from the selection.
             pendingPromise.resolve(deselectItem(options, state, $(e.currentTarget), originalSelect));
         });
+        // Remove the highlight of items when user tabs out the tag list.
+        selectionElement.on('blur', function(e) {
+            e.preventDefault();
+            $(this).children().attr('data-active-selection', false).attr('id', '');
+        });
+        // When tag list is focused, highlight the first item.
+        selectionElement.on('focus', function(e) {
+            e.preventDefault();
+            var element = $(this).children('[data-active-selection=true]');
+            if (element && element.length === 0) {
+                activateNextSelection(state);
+            }
+        });
         // Keyboard navigation for the selection list.
         selectionElement.on('keydown', function(e) {
             var pendingPromise = addPendingJSPromise('form-autocomplete-keydown-' + e.keyCode);
             switch (e.keyCode) {
+                case KEYS.RIGHT:
                 case KEYS.DOWN:
                     // We handled this event, so prevent it.
                     e.preventDefault();
@@ -817,6 +833,7 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
                     // Choose the next selection item.
                     pendingPromise.resolve(activateNextSelection(state));
                     return false;
+                case KEYS.LEFT:
                 case KEYS.UP:
                     // We handled this event, so prevent it.
                     e.preventDefault();
index ce2ae0b..db165c7 100644 (file)
@@ -66,7 +66,7 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
      * be enough.
      *
      * @deprecated since Moodle 3.7 MDL-64979 - please use get_reduced_timeout() instead
-     * @todo MDL-64982 This will be deleted in Moodle 4.1
+     * @todo MDL-64982 This will be deleted in Moodle 3.11
      * @see behat_base::get_reduced_timeout()
      */
     const REDUCED_TIMEOUT = 2;
@@ -75,7 +75,7 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
      * The timeout for each Behat step (load page, wait for an element to load...).
      *
      * @deprecated since Moodle 3.7 MDL-64979 - please use get_timeout() instead
-     * @todo MDL-64982 This will be deleted in Moodle 4.1
+     * @todo MDL-64982 This will be deleted in Moodle 3.11
      * @see behat_base::get_timeout()
      */
     const TIMEOUT = 6;
@@ -84,7 +84,7 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
      * And extended timeout for specific cases.
      *
      * @deprecated since Moodle 3.7 MDL-64979 - please use get_extended_timeout() instead
-     * @todo MDL-64982 This will be deleted in Moodle 4.1
+     * @todo MDL-64982 This will be deleted in Moodle 3.11
      * @see behat_base::get_extended_timeout()
      */
     const EXTENDED_TIMEOUT = 10;
index 60a3335..0ca4559 100644 (file)
@@ -1225,6 +1225,12 @@ class block_manager {
      */
     public function ensure_content_created($region, $output) {
         $this->ensure_instances_exist($region);
+
+        if (!has_capability('moodle/block:view', $this->page->context) ) {
+            $this->visibleblockcontent[$region] = [];
+            return;
+        }
+
         if (!array_key_exists($region, $this->visibleblockcontent)) {
             $contents = array();
             if (array_key_exists($region, $this->extracontent)) {
index 4dfec49..c703a2f 100644 (file)
@@ -92,7 +92,7 @@ class db_record_lock_factory implements lock_factory {
     /**
      * Multiple locks for the same resource can be held by a single process.
      *
-     * @deprecated since Moodle 4.0.
+     * @deprecated since Moodle 3.10.
      * @return boolean - False - not process specific.
      */
     public function supports_recursion() {
@@ -192,7 +192,7 @@ class db_record_lock_factory implements lock_factory {
     /**
      * Extend a lock that was previously obtained with @lock.
      *
-     * @deprecated since Moodle 4.0.
+     * @deprecated since Moodle 3.10.
      * @param lock $lock - a lock obtained from this factory.
      * @param int $maxlifetime - the new lifetime for the lock (in seconds).
      * @return boolean - true if the lock was extended.
index b771639..87b9380 100644 (file)
@@ -109,7 +109,7 @@ class file_lock_factory implements lock_factory {
     /**
      * Multiple locks for the same resource cannot be held from a single process.
      *
-     * @deprecated since Moodle 4.0.
+     * @deprecated since Moodle 3.10.
      * @return boolean - False
      */
     public function supports_recursion() {
@@ -193,7 +193,7 @@ class file_lock_factory implements lock_factory {
     /**
      * Extend a lock that was previously obtained with @lock.
      *
-     * @deprecated since Moodle 4.0.
+     * @deprecated since Moodle 3.10.
      * @param lock $lock - not used
      * @param int $maxlifetime - not used
      * @return boolean - true if the lock was extended.
index 0874ced..628a815 100644 (file)
@@ -76,7 +76,7 @@ class installation_lock_factory implements lock_factory {
     /**
      * Multiple locks for the same resource cannot be held from a single process.
      *
-     * @deprecated since Moodle 4.0.
+     * @deprecated since Moodle 3.10.
      * @return boolean - False
      */
     public function supports_recursion() {
@@ -118,7 +118,7 @@ class installation_lock_factory implements lock_factory {
     /**
      * Extend a lock that was previously obtained with @lock.
      *
-     * @deprecated since Moodle 4.0.
+     * @deprecated since Moodle 3.10.
      * @param lock $lock - not used
      * @param int $maxlifetime - not used
      * @return boolean - true if the lock was extended.
index f67083c..7012bcf 100644 (file)
@@ -81,7 +81,7 @@ class lock {
     /**
      * Extend the lifetime of this lock. Not supported by all factories.
      *
-     * @deprecated since Moodle 4.0.
+     * @deprecated since Moodle 3.10.
      * @param int $maxlifetime - the new lifetime for the lock (in seconds).
      * @return bool
      */
index c0d32b0..9ca9782 100644 (file)
@@ -63,7 +63,7 @@ interface lock_factory {
     /**
      * Supports recursion.
      *
-     * @deprecated since Moodle 4.0.
+     * @deprecated since Moodle 3.10.
      * @return boolean - True if attempting to get 2 locks on the same resource will "stack"
      */
     public function supports_recursion();
@@ -99,7 +99,7 @@ interface lock_factory {
     /**
      * Extend the timeout on a held lock.
      *
-     * @deprecated since Moodle 4.0.
+     * @deprecated since Moodle 3.10.
      * @param lock $lock - lock obtained from this factory
      * @param int $maxlifetime - new max time to hold the lock
      * @return boolean - True if the lock was extended.
index 753741b..29e3abc 100644 (file)
@@ -106,7 +106,7 @@ class mysql_lock_factory implements lock_factory {
      * Hard coded to false and workaround inconsistent support in different
      * versions of MySQL / MariaDB.
      *
-     * @deprecated since Moodle 4.0.
+     * @deprecated since Moodle 3.10.
      * @return boolean - false
      */
     public function supports_recursion() {
@@ -169,7 +169,7 @@ class mysql_lock_factory implements lock_factory {
     /**
      * Extend a lock that was previously obtained with @lock.
      *
-     * @deprecated since Moodle 4.0.
+     * @deprecated since Moodle 3.10.
      * @param lock $lock - a lock obtained from this factory.
      * @param int $maxlifetime - the new lifetime for the lock (in seconds).
      * @return boolean - true if the lock was extended.
index 1d18967..a4deb15 100644 (file)
@@ -120,7 +120,7 @@ class postgres_lock_factory implements lock_factory {
     /**
      * Multiple locks for the same resource can NOT be held by a single process.
      *
-     * @deprecated since Moodle 4.0.
+     * @deprecated since Moodle 3.10.
      * @return boolean - false.
      */
     public function supports_recursion() {
@@ -206,7 +206,7 @@ class postgres_lock_factory implements lock_factory {
     /**
      * Extend a lock that was previously obtained with @lock.
      *
-     * @deprecated since Moodle 4.0.
+     * @deprecated since Moodle 3.10.
      * @param lock $lock - a lock obtained from this factory.
      * @param int $maxlifetime - the new lifetime for the lock (in seconds).
      * @return boolean - true if the lock was extended.
index 6504d40..0f7bb41 100644 (file)
@@ -72,14 +72,16 @@ class asynchronous_copy_task extends adhoc_task {
         $keepuserdata = (bool)$copyinfo->userdata;
         $keptroles = $copyinfo->keptroles;
 
-        $backupplan->get_setting('users')->set_value('1');
         $bc->set_kept_roles($keptroles);
 
         // If we are not keeping user data don't include users or data in the backup.
         // In this case we'll add the user enrolments at the end.
         // Also if we have no roles to keep don't backup users.
         if (empty($keptroles) || !$keepuserdata) {
+            $backupplan->get_setting('users')->set_status(\backup_setting::NOT_LOCKED);
             $backupplan->get_setting('users')->set_value('0');
+        } else {
+            $backupplan->get_setting('users')->set_value('1');
         }
 
         // Do some preflight checks on the backup.
index 197dbd2..998a203 100644 (file)
@@ -28,7 +28,7 @@ namespace core\task;
  * Note - this is only for plugins using the legacy cron method,
  * plugins can also now just add their own scheduled tasks which is the preferred method.
  * @deprecated since Moodle 3.9 MDL-52846. Please use new task API.
- * @todo MDL-61165 This will be deleted in Moodle 4.3
+ * @todo MDL-61165 This will be deleted in Moodle 4.1
  */
 class legacy_plugin_cron_task extends scheduled_task {
 
index abc93f6..94f0c0d 100644 (file)
@@ -1104,26 +1104,6 @@ $functions = array(
         'ajax' => true,
         'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
     ),
-    'core_message_block_contacts' => array(
-        'classname' => 'core_message_external',
-        'methodname' => 'block_contacts',
-        'classpath' => 'message/externallib.php',
-        'description' => '** DEPRECATED ** Please do not call this function any more.
-                          Block contacts',
-        'type' => 'write',
-        'ajax' => true,
-        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
-    ),
-    'core_message_create_contacts' => array(
-        'classname' => 'core_message_external',
-        'methodname' => 'create_contacts',
-        'classpath' => 'message/externallib.php',
-        'description' => '** DEPRECATED ** Please do not call this function any more.
-                          Add contacts to the contact list',
-        'type' => 'write',
-        'ajax' => true,
-        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
-    ),
     'core_message_get_contact_requests' => array(
         'classname' => 'core_message_external',
         'methodname' => 'get_contact_requests',
@@ -1178,17 +1158,6 @@ $functions = array(
         'ajax' => true,
         'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
     ),
-    'core_message_delete_conversation' => array(
-        'classname' => 'core_message_external',
-        'methodname' => 'delete_conversation',
-        'classpath' => 'message/externallib.php',
-        'description' => '** DEPRECATED ** Please do not call this function any more.
-                          Deletes a conversation.',
-        'type' => 'write',
-        'capabilities' => 'moodle/site:deleteownmessage',
-        'ajax' => true,
-        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
-    ),
     'core_message_delete_conversations_by_id' => array(
         'classname' => 'core_message_external',
         'methodname' => 'delete_conversations_by_id',
@@ -1226,24 +1195,6 @@ $functions = array(
         'ajax' => true,
         'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
     ),
-    'core_message_data_for_messagearea_search_users' => array(
-        'classname' => 'core_message_external',
-        'methodname' => 'data_for_messagearea_search_users',
-        'classpath' => 'message/externallib.php',
-        'description' => '** DEPRECATED ** Please do not call this function any more.
-                          Retrieve the template data for searching for people',
-        'type' => 'read',
-        'ajax' => true,
-    ),
-    'core_message_data_for_messagearea_search_users_in_course' => array(
-        'classname' => 'core_message_external',
-        'methodname' => 'data_for_messagearea_search_users_in_course',
-        'classpath' => 'message/externallib.php',
-        'description' => '** DEPRECATED ** Please do not call this function any more.
-                          Retrieve the template data for searching for people in a course',
-        'type' => 'read',
-        'ajax' => true,
-    ),
     'core_message_message_search_users' => array(
         'classname' => 'core_message_external',
         'methodname' => 'message_search_users',
@@ -1253,63 +1204,6 @@ $functions = array(
         'ajax' => true,
         'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
     ),
-    'core_message_data_for_messagearea_conversations' => array(
-        'classname' => 'core_message_external',
-        'methodname' => 'data_for_messagearea_conversations',
-        'classpath' => 'message/externallib.php',
-        'description' => '** DEPRECATED ** Please do not call this function any more.
-                          Retrieve the template data for the conversation list',
-        'type' => 'read',
-        'ajax' => true,
-        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
-    ),
-    'core_message_data_for_messagearea_contacts' => array(
-        'classname' => 'core_message_external',
-        'methodname' => 'data_for_messagearea_contacts',
-        'classpath' => 'message/externallib.php',
-        'description' => '** DEPRECATED ** Please do not call this function any more.
-                          Retrieve the template data for the contact list',
-        'type' => 'read',
-        'ajax' => true,
-        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
-    ),
-    'core_message_data_for_messagearea_messages' => array(
-        'classname' => 'core_message_external',
-        'methodname' => 'data_for_messagearea_messages',
-        'classpath' => 'message/externallib.php',
-        'description' => '** DEPRECATED ** Please do not call this function any more.
-                          Retrieve the template data for the messages',
-        'type' => 'read',
-        'ajax' => true,
-        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
-    ),
-    'core_message_data_for_messagearea_get_most_recent_message' => array(
-        'classname' => 'core_message_external',
-        'methodname' => 'data_for_messagearea_get_most_recent_message',
-        'classpath' => 'message/externallib.php',
-        'description' => '** DEPRECATED ** Please do not call this function any more.
-                          Retrieve the template data for the most recent message',
-        'type' => 'read',
-        'ajax' => true,
-    ),
-    'core_message_data_for_messagearea_get_profile' => array(
-        'classname' => 'core_message_external',
-        'methodname' => 'data_for_messagearea_get_profile',
-        'classpath' => 'message/externallib.php',
-        'description' => '** DEPRECATED ** Please do not call this function any more.
-                          Retrieve the template data for the users\'s profile',
-        'type' => 'read',
-        'ajax' => true,
-    ),
-    'core_message_get_contacts' => array(
-        'classname' => 'core_message_external',
-        'methodname' => 'get_contacts',
-        'classpath' => 'message/externallib.php',
-        'description' => '** DEPRECATED ** Please do not call this function any more.
-                          Retrieve the contact list',
-        'type' => 'read',
-        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
-    ),
     'core_message_get_user_contacts' => array(
         'classname' => 'core_message_external',
         'methodname' => 'get_user_contacts',
@@ -1419,16 +1313,6 @@ $functions = array(
         'ajax' => true,
         'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
     ),
-    'core_message_mark_all_messages_as_read' => array(
-        'classname' => 'core_message_external',
-        'methodname' => 'mark_all_messages_as_read',
-        'classpath' => 'message/externallib.php',
-        'description' => '** DEPRECATED ** Please do not call this function any more.
-                          Mark all messages as read for a given user',
-        'type' => 'write',
-        'ajax' => true,
-        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
-    ),
     'core_message_mark_all_conversation_messages_as_read' => array(
         'classname' => 'core_message_external',
         'methodname' => 'mark_all_conversation_messages_as_read',
@@ -1519,16 +1403,6 @@ $functions = array(
         'ajax' => true,
         'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
     ),
-    'core_message_unblock_contacts' => array(
-        'classname' => 'core_message_external',
-        'methodname' => 'unblock_contacts',
-        'classpath' => 'message/externallib.php',
-        'description' => '** DEPRECATED ** Please do not call this function any more.
-                         Unblock contacts',
-        'type' => 'write',
-        'ajax' => true,
-        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
-    ),
     'core_message_get_user_notification_preferences' => array(
         'classname' => 'core_message_external',
         'methodname' => 'get_user_notification_preferences',
index 48661cb..e20ad23 100644 (file)
@@ -2761,7 +2761,7 @@ function message_get_contact() {
  *            in such a large SELECT
  *
  * @deprecated since Moodle 3.7
- * @todo The final deprecation of this function will take place in Moodle 41 - see MDL-65319.
+ * @todo The final deprecation of this function will take place in Moodle 3.11 - see MDL-65319.
  *
  * @param string|int $categoryid Either a category id or 'all' for everything
  * @param string $sort A field and direction to sort by
@@ -2823,7 +2823,7 @@ function get_courses_page($categoryid="all", $sort="c.sortorder ASC", $fields="c
  * Returns the models that generated insights in the provided context.
  *
  * @deprecated since Moodle 3.8 MDL-66091 - please do not use this function any more.
- * @todo MDL-65799 This will be deleted in Moodle 4.2
+ * @todo MDL-65799 This will be deleted in Moodle 4.0
  * @see \core_analytics\manager::cached_models_with_insights
  * @param  \context $context
  * @return int[]
@@ -2944,7 +2944,7 @@ function get_module_metadata($course, $modnames, $sectionreturn = null) {
  * with other processing (other than displaying the rest of the page) after using this function!
  *
  * @deprecated since Moodle 3.9 MDL-63580. Please use the \core\task\manager::run_from_cli($task).
- * @todo final deprecation. To be removed in Moodle 4.3 MDL-63594.
+ * @todo final deprecation. To be removed in Moodle 4.1 MDL-63594.
  * @param \core\task\scheduled_task $task Task to run
  * @return bool True if cron run successful
  */
@@ -2962,7 +2962,7 @@ function cron_run_single_task(\core\task\scheduled_task $task) {
  *   and 'Finished (whatever)' lines, otherwise does not display
  *
  * @deprecated since Moodle 3.9 MDL-52846. Please use new task API.
- * @todo MDL-61165 This will be deleted in Moodle 4.3.
+ * @todo MDL-61165 This will be deleted in Moodle 4.1.
  */
 function cron_execute_plugin_type($plugintype, $description = null) {
     global $DB;
@@ -3036,7 +3036,7 @@ function cron_execute_plugin_type($plugintype, $description = null) {
  *   looking in the older location
  *
  * @deprecated since Moodle 3.9 MDL-52846. Please use new task API.
- * @todo MDL-61165 This will be deleted in Moodle 4.3.
+ * @todo MDL-61165 This will be deleted in Moodle 4.1.
  */
 function cron_bc_hack_plugin_functions($plugintype, $plugins) {
     global $CFG; // Mandatory in case it is referenced by include()d PHP script.
@@ -3357,8 +3357,8 @@ function user_get_participants($courseid, $groupid = 0, $accesssince, $roleid, $
  *
  * Calls {@see core_course_category::make_categories_list()} to build the list.
  *
- * @deprecated since Moodle 4.0
- * @todo This will be finally removed for Moodle 4.4 as part of MDL-69124.
+ * @deprecated since Moodle 3.10
+ * @todo This will be finally removed for Moodle 4.2 as part of MDL-69124.
  * @return array array mapping course category id to the display name
  */
 function make_categories_options() {
index fc01b1e..d04b236 100644 (file)
@@ -496,6 +496,27 @@ class stored_file {
         return $this->filesystem->list_files($this, $packer);
     }
 
+    /**
+     * Returns the total size (in bytes) of the contents of an archive.
+     *
+     * @param file_packer $packer file packer instance
+     * @return int|null total size in bytes
+     */
+    public function get_total_content_size(file_packer $packer): ?int {
+        // Fetch the contents of the archive.
+        $files = $this->list_files($packer);
+
+        // Early return if the value of $files is not of type array.
+        // This can happen when the utility class is unable to open or read the contents of the archive.
+        if (!is_array($files)) {
+            return null;
+        }
+
+        return array_reduce($files, function ($contentsize, $file) {
+            return $contentsize + $file->size;
+        }, 0);
+    }
+
     /**
      * Extract file to given file path (real OS filesystem), existing files are overwritten.
      *
index e766d16..036233b 100644 (file)
@@ -493,6 +493,7 @@ M.form_filemanager.init = function(Y, options) {
             // XXX: magic here, to let filepicker use filemanager scope
             options.magicscope = this;
             options.savepath = this.currentpath;
+            options.previousActiveElement = e.target.ancestor('a', true);
             M.core_filepicker.show(Y, options);
         },
 
@@ -990,14 +991,21 @@ M.form_filemanager.init = function(Y, options) {
                 }
                 params['filepath'] = fileinfo.filepath;
                 params['filename'] = fileinfo.fullname;
+                // The unlimited value of areamaxbytes is -1, it is defined by FILE_AREA_MAX_BYTES_UNLIMITED.
+                params['areamaxbytes'] = this.areamaxbytes ? this.areamaxbytes : -1;
                 selectnode.addClass('loading');
                 this.request({
                     action: 'unzip',
                     scope: this,
                     params: params,
                     callback: function(id, obj, args) {
-                        args.scope.selectui.hide();
-                        args.scope.refresh(obj.filepath);
+                        if (obj.error) {
+                            selectnode.removeClass('loading');
+                            args.scope.print_msg(obj.error, 'error');
+                        } else {
+                            args.scope.selectui.hide();
+                            args.scope.refresh(obj.filepath);
+                        }
                     }
                 });
             }, this);
index 3010ef9..3d14013 100644 (file)
@@ -96,7 +96,7 @@ class license_manager {
      * Adding a new license type
      *
      * @deprecated Since Moodle 3.9, MDL-45184.
-     * @todo MDL-67344 This will be deleted in Moodle 4.3.
+     * @todo MDL-67344 This will be deleted in Moodle 4.1.
      * @see license_manager::save()
      *
      * @param object $license the license record to add.
index e08b3d3..b6277cb 100644 (file)
@@ -4682,6 +4682,12 @@ class action_menu_link extends action_link implements renderable {
      */
     public $actionmenu = null;
 
+    /**
+     * The number of instances of this action menu link (and its subclasses).
+     * @var int
+     */
+    protected static $instance = 1;
+
     /**
      * Constructs the object.
      *
@@ -4705,10 +4711,8 @@ class action_menu_link extends action_link implements renderable {
      * @return stdClass
      */
     public function export_for_template(renderer_base $output) {
-        static $instance = 1;
-
         $data = parent::export_for_template($output);
-        $data->instance = $instance++;
+        $data->instance = self::$instance++;
 
         // Ignore what the parent did with the attributes, except for ID and class.
         $data->attributes = [];
index b711ac9..7b5b1f4 100644 (file)
@@ -79,7 +79,7 @@ function plagiarism_get_file_results($cmid, $userid, $file) {
  * saves/updates plagiarism settings from a modules config page - called by course/modedit.php
  *
  * @deprecated Since Moodle 3.9. MDL-65835 Please use {plugin name}_coursemodule_edit_post_actions() instead.
- * @todo MDL-67526 This is to be moved from here to deprecatedlib.php in Moodle 4.3
+ * @todo MDL-67526 This is to be moved from here to deprecatedlib.php in Moodle 4.1
  * @param object $data - form data
  */
 function plagiarism_save_form_elements($data) {
@@ -108,7 +108,7 @@ function plagiarism_save_form_elements($data) {
  * adds the list of plagiarism settings to a form - called inside modules that have enabled plagiarism
  *
  * @deprecated Since Moodle 3.9. MDL-65835 Please use {plugin name}_coursemodule_standard_elements() instead.
- * @todo MDL-67526 This is to be moved from here to deprecatedlib.php in Moodle 4.3
+ * @todo MDL-67526 This is to be moved from here to deprecatedlib.php in Moodle 4.1
  * @param object $mform - Moodle form object
  * @param object $context - context object
  * @param string $modulename - Name of the module
@@ -181,7 +181,7 @@ function plagiarism_print_disclosure($cmid) {
 /**
  * Helper function - also loads lib file of plagiarism plugin
  *
- * @todo MDL-67872 the deprecated code in this function to be removed in Moodle 4.3
+ * @todo MDL-67872 the deprecated code in this function to be removed in Moodle 4.1
  * @return array of available plugins
  */
 function plagiarism_load_available_plugins() {
index 0184d49..a57beb2 100644 (file)
     }
 }}
 {{^disabled}}
-    <a href="{{url}}" class="aabtn {{classes}}" {{#attributes}}{{name}}={{#quote}}{{value}}{{/quote}} {{/attributes}}{{#showtext}}aria-labelledby="actionmenuaction-{{instance}}"{{/showtext}}>{{#icon}}{{#pix}}{{key}}, {{component}}, {{title}}{{/pix}}{{/icon}}{{#showtext}}<span class="menu-action-text" id="actionmenuaction-{{instance}}">{{{text}}}</span>{{/showtext}}</a>
+    <a href="{{url}}" class="{{$actionmenulinkclasses}}aabtn {{classes}}{{/actionmenulinkclasses}}" {{#attributes}}{{name}}={{#quote}}{{value}}{{/quote}} {{/attributes}}{{#showtext}}aria-labelledby="actionmenuaction-{{instance}}"{{/showtext}}>
+        {{#icon}}
+            {{#pix}}{{key}}, {{component}}, {{title}}{{/pix}}
+        {{/icon}}
+        {{#showtext}}
+            <span class="menu-action-text" id="actionmenuaction-{{instance}}">{{{text}}}</span>
+        {{/showtext}}
+    </a>
 {{/disabled}}
 {{#disabled}}
     <span class="currentlink" role="menuitem">{{#icon}}{{#pix}}{{key}},{{component}},{{title}}{{/pix}}{{/icon}}{{{text}}}</span>
index 8a172c9..9c009fc 100644 (file)
         <div class="dropdown-menu dropdown-menu-right {{classes}}"{{#attributes}} {{name}}="{{value}}"{{/attributes}}>
             {{#items}}
                 {{#actionmenulink}}
-                    <a href="{{url}}" class="dropdown-item {{classes}}" {{#attributes}}{{name}}={{#quote}}{{value}}{{/quote}} {{/attributes}}{{#showtext}}aria-labelledby="actionmenuaction-{{instance}}"{{/showtext}}>
-                        {{#icon}}
-                            {{#pix}}{{key}},{{component}},{{title}}{{/pix}}
-                        {{/icon}}
-                        {{#showtext}}
-                        <span class="menu-action-text" id="actionmenuaction-{{instance}}">
-                            {{{text}}}
-                        </span>
-                        {{/showtext}}
-                    </a>
+                    {{< core/action_menu_link}}
+                        {{$actionmenulinkclasses}}dropdown-item {{classes}}{{/actionmenulinkclasses}}
+                    {{/ core/action_menu_link}}
                 {{/actionmenulink}}
                 {{#actionmenufiller}}
                     <div class="dropdown-divider" role="presentation"><span class="filler">&nbsp;</span></div>
index 905932a..c889fa6 100644 (file)
@@ -442,4 +442,260 @@ class accesslib_has_capability_testcase extends \advanced_testcase {
             ],
         ];
     }
+
+    /**
+     * Data provider for for has_capability tests when logged in as a different user.
+     *
+     * @return  array
+     */
+    public function login_as_provider(): array {
+        return [
+            [
+                'system',
+                [
+                    'cat1course1block' => true,
+                    'cat1course1' => true,
+                    'cat1course2block' => true,
+                    'cat1course2' => true,
+                    'cat2course1block' => true,
+                    'cat2course1' => true,
+                    'cat2course2block' => true,
+                    'cat2course2' => true,
+                ],
+            ],
+            [
+                'cat1',
+                [
+                    'cat1course1block' => true,
+                    'cat1course1' => true,
+                    'cat1course2block' => true,
+                    'cat1course2' => true,
+
+                    'cat2course1block' => false,
+                    'cat2course1' => false,
+                    'cat2course2block' => false,
+                    'cat2course2' => false,
+                ],
+            ],
+            [
+                'cat1course1',
+                [
+                    'cat1course1block' => true,
+                    'cat1course1' => true,
+
+                    'cat1course2block' => false,
+                    'cat1course2' => false,
+                    'cat2course1block' => false,
+                    'cat2course1' => false,
+                    'cat2course2block' => false,
+                    'cat2course2' => false,
+                ],
+            ],
+            [
+                'cat1course1block',
+                [
+                    'cat1course1block' => true,
+
+                    'cat1course1' => false,
+                    'cat1course2block' => false,
+                    'cat1course2' => false,
+                    'cat2course1block' => false,
+                    'cat2course1' => false,
+                    'cat2course2block' => false,
+                    'cat2course2' => false,
+                ],
+            ],
+        ];
+    }
+
+    /**
+     * Test that the log in as functionality works as expected for an administrator.
+     *
+     * An administrator logged in as another user assumes all of their capabilities.
+     *
+     * @dataProvider    login_as_provider
+     * @param   string $loginascontext
+     * @param   string $testcontexts
+     */
+    public function test_login_as_admin(string $loginascontext, array $testcontexts): void {
+        $this->resetAfterTest();
+
+        $contexts = $this->get_test_data();
+
+        $this->setAdminUser();
+        $user = $this->getDataGenerator()->create_user();
+
+        $testcontext = $contexts->$loginascontext;
+        \core\session\manager::loginas($user->id, $testcontext);
+
+        $capability = 'moodle/block:view';
+        foreach ($testcontexts as $contextname => $hascapability) {
+            $this->assertEquals($hascapability, has_capability($capability, $contexts->$contextname));
+        }
+    }
+
+    /**
+     * Test that the log in as functionality works as expected for a regulr user.
+     *
+     * @dataProvider    login_as_provider
+     * @param   string $loginascontext
+     * @param   string $testcontexts
+     */
+    public function test_login_as_user(string $loginascontext, array $testcontexts): void {
+        $this->resetAfterTest();
+
+        $contexts = $this->get_test_data();
+
+        $initialuser = $this->getDataGenerator()->create_user();
+        $user = $this->getDataGenerator()->create_user();
+        $this->setUser($initialuser);
+
+        $testcontext = $contexts->$loginascontext;
+        \core\session\manager::loginas($user->id, $testcontext);
+
+        $capability = 'moodle/block:view';
+        foreach ($testcontexts as $contextname => $hascapability) {
+            $this->assertEquals($hascapability, has_capability($capability, $contexts->$contextname));
+        }
+    }
+
+    /**
+     * Get the test data contexts.
+     *
+     * @return  stdClass
+     */
+    protected function get_test_data(): stdclass {
+        $generator = $this->getDataGenerator();
+        $otheruser = $generator->create_user();
+
+        // / (system)
+        // /Cat1
+        // /Cat1/Block
+        // /Cat1/Course1
+        // /Cat1/Course1/Block
+        // /Cat1/Course2
+        // /Cat1/Course2/Block
+        // /Cat1/Cat1a
+        // /Cat1/Cat1a/Block
+        // /Cat1/Cat1a/Course1
+        // /Cat1/Cat1a/Course1/Block
+        // /Cat1/Cat1a/Course2
+        // /Cat1/Cat1a/Course2/Block
+        // /Cat1/Cat1b
+        // /Cat1/Cat1b/Block
+        // /Cat1/Cat1b/Course1
+        // /Cat1/Cat1b/Course1/Block
+        // /Cat1/Cat1b/Course2
+        // /Cat1/Cat1b/Course2/Block
+        // /Cat2
+        // /Cat2/Block
+        // /Cat2/Course1
+        // /Cat2/Course1/Block
+        // /Cat2/Course2
+        // /Cat2/Course2/Block
+        // /Cat2/Cat2a
+        // /Cat2/Cat2a/Block
+        // /Cat2/Cat2a/Course1
+        // /Cat2/Cat2a/Course1/Block
+        // /Cat2/Cat2a/Course2
+        // /Cat2/Cat2a/Course2/Block
+        // /Cat2/Cat2b
+        // /Cat2/Cat2b/Block
+        // /Cat2/Cat2b/Course1
+        // /Cat2/Cat2b/Course1/Block
+        // /Cat2/Cat2b/Course2
+        // /Cat2/Cat2b/Course2/Block
+
+        $adminuser = \core_user::get_user_by_username('admin');
+        $contexts = (object) [
+            'system' => \context_system::instance(),
+            'adminuser' => \context_user::instance($adminuser->id),
+        ];
+
+        $cat1 = $generator->create_category();
+        $cat1a = $generator->create_category(['parent' => $cat1->id]);
+        $cat1b = $generator->create_category(['parent' => $cat1->id]);
+
+        $contexts->cat1 = \context_coursecat::instance($cat1->id);
+        $contexts->cat1a = \context_coursecat::instance($cat1a->id);
+        $contexts->cat1b = \context_coursecat::instance($cat1b->id);
+
+        $cat1course1 = $generator->create_course(['category' => $cat1->id]);
+        $cat1course2 = $generator->create_course(['category' => $cat1->id]);
+        $cat1acourse1 = $generator->create_course(['category' => $cat1a->id]);
+        $cat1acourse2 = $generator->create_course(['category' => $cat1a->id]);
+        $cat1bcourse1 = $generator->create_course(['category' => $cat1b->id]);
+        $cat1bcourse2 = $generator->create_course(['category' => $cat1b->id]);
+
+        $contexts->cat1course1 = \context_course::instance($cat1course1->id);
+        $contexts->cat1acourse1 = \context_course::instance($cat1acourse1->id);
+        $contexts->cat1bcourse1 = \context_course::instance($cat1bcourse1->id);
+        $contexts->cat1course2 = \context_course::instance($cat1course2->id);
+        $contexts->cat1acourse2 = \context_course::instance($cat1acourse2->id);
+        $contexts->cat1bcourse2 = \context_course::instance($cat1bcourse2->id);
+
+        $cat1block = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat1->id]);
+        $cat1ablock = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat1a->id]);
+        $cat1bblock = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat1b->id]);
+        $cat1course1block = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat1course1->id]);
+        $cat1course2block = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat1course2->id]);
+        $cat1acourse1block = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat1acourse1->id]);
+        $cat1acourse2block = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat1acourse2->id]);
+        $cat1bcourse1block = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat1bcourse1->id]);
+        $cat1bcourse2block = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat1bcourse2->id]);
+
+        $contexts->cat1block = \context_block::instance($cat1block->id);
+        $contexts->cat1ablock = \context_block::instance($cat1ablock->id);
+        $contexts->cat1bblock = \context_block::instance($cat1bblock->id);
+        $contexts->cat1course1block = \context_block::instance($cat1course1block->id);
+        $contexts->cat1course2block = \context_block::instance($cat1course2block->id);
+        $contexts->cat1acourse1block = \context_block::instance($cat1acourse1block->id);
+        $contexts->cat1acourse2block = \context_block::instance($cat1acourse2block->id);
+        $contexts->cat1bcourse1block = \context_block::instance($cat1bcourse1block->id);
+        $contexts->cat1bcourse2block = \context_block::instance($cat1bcourse2block->id);
+
+        $cat2 = $generator->create_category();
+        $cat2a = $generator->create_category(['parent' => $cat2->id]);
+        $cat2b = $generator->create_category(['parent' => $cat2->id]);
+
+        $contexts->cat2 = \context_coursecat::instance($cat2->id);
+        $contexts->cat2a = \context_coursecat::instance($cat2a->id);
+        $contexts->cat2b = \context_coursecat::instance($cat2b->id);
+
+        $cat2course1 = $generator->create_course(['category' => $cat2->id]);
+        $cat2course2 = $generator->create_course(['category' => $cat2->id]);
+        $cat2acourse1 = $generator->create_course(['category' => $cat2a->id]);
+        $cat2acourse2 = $generator->create_course(['category' => $cat2a->id]);
+        $cat2bcourse1 = $generator->create_course(['category' => $cat2b->id]);
+        $cat2bcourse2 = $generator->create_course(['category' => $cat2b->id]);
+
+        $contexts->cat2course1 = \context_course::instance($cat2course1->id);
+        $contexts->cat2acourse1 = \context_course::instance($cat2acourse1->id);
+        $contexts->cat2bcourse1 = \context_course::instance($cat2bcourse1->id);
+        $contexts->cat2course2 = \context_course::instance($cat2course2->id);
+        $contexts->cat2acourse2 = \context_course::instance($cat2acourse2->id);
+        $contexts->cat2bcourse2 = \context_course::instance($cat2bcourse2->id);
+
+        $cat2block = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat2->id]);
+        $cat2ablock = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat2a->id]);
+        $cat2bblock = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat2b->id]);
+        $cat2course1block = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat2course1->id]);
+        $cat2course2block = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat2course2->id]);
+        $cat2acourse1block = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat2acourse1->id]);
+        $cat2acourse2block = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat2acourse2->id]);
+        $cat2bcourse1block = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat2bcourse1->id]);
+        $cat2bcourse2block = $generator->create_block('online_users', ['parentcontextid' => $contexts->cat2bcourse2->id]);
+
+        $contexts->cat2block = \context_block::instance($cat2block->id);
+        $contexts->cat2ablock = \context_block::instance($cat2ablock->id);
+        $contexts->cat2bblock = \context_block::instance($cat2bblock->id);
+        $contexts->cat2course1block = \context_block::instance($cat2course1block->id);
+        $contexts->cat2course2block = \context_block::instance($cat2course2block->id);
+        $contexts->cat2acourse1block = \context_block::instance($cat2acourse1block->id);
+        $contexts->cat2acourse2block = \context_block::instance($cat2acourse2block->id);
+        $contexts->cat2bcourse1block = \context_block::instance($cat2bcourse1block->id);
+        $contexts->cat2bcourse2block = \context_block::instance($cat2bcourse2block->id);
+
+        return $contexts;
+    }
 }
index 0382f72..f5abbd1 100644 (file)
@@ -4103,6 +4103,204 @@ class core_accesslib_testcase extends advanced_testcase {
         $this->assertEquals($expectedteacher, get_profile_roles($coursecontext));
     }
 
+    /**
+     * Data provider for is_parent_of context checks.
+     *
+     * @return  array
+     */
+    public function is_parent_of_provider(): array {
+        $provideboth = function(string $desc, string $contextpath, string $testpath, bool $expected): array {
+            return [
+                "includeself: true; {$desc}" => [
+                    $contextpath,
+                    $testpath,
+                    true,
+                    $expected,
+                ],
+                "includeself: false; {$desc}" => [
+                    $contextpath,
+                    $testpath,
+                    false,
+                    $expected,
+                ],
+            ];
+        };
+
+        return array_merge(
+            [
+                'includeself: true, testing self' => [
+                    '/1/4/17/291/1001/17105',
+                    '/1/4/17/291/1001/17105',
+                    true,
+                    true,
+                ],
+                'includeself: false, testing self' => [
+                    '/1/4/17/291/1001/17105',
+                    '/1/4/17/291/1001/17105',
+                    false,
+                    false,
+                ],
+            ],
+            $provideboth(
+                'testing parent',
+                '/1/4/17/291/1001/17105',
+                '/1/4/17/291/1001',
+                false
+            ),
+            $provideboth(
+                'testing child',
+                '/1/4/17/291/1001',
+                '/1/4/17/291/1001/17105',
+                true
+            ),
+            $provideboth(
+                'testing grandchild',
+                '/1',
+                '/1/4/17/291/1001/17105',
+                true
+            )
+        );
+    }
+
+    /**
+     * Ensure that the is_parent_of() function works as anticipated.
+     *
+     * @dataProvider is_parent_of_provider
+     * @param   string $contextpath The path of the context being compared with
+     * @param   string $testpath The path of the context being compared
+     * @param   bool $testself Whether to check the current context
+     * @param   bool $expected The expected result
+     */
+    public function test_is_parent_of(string $contextpath, string $testpath, bool $testself, bool $expected): void {
+        $context = $this->getMockBuilder(\context::class)
+            ->disableOriginalConstructor()
+            ->setMethods([
+                'get_url',
+                'get_capabilities',
+            ])
+            ->getMock();
+
+        $rcp = new ReflectionProperty($context, '_path');
+        $rcp->setAccessible(true);
+        $rcp->setValue($context, $contextpath);
+
+        $comparisoncontext = $this->getMockBuilder(\context::class)
+            ->disableOriginalConstructor()
+            ->setMethods([
+                'get_url',
+                'get_capabilities',
+            ])
+            ->getMock();
+
+        $rcp = new ReflectionProperty($comparisoncontext, '_path');
+        $rcp->setAccessible(true);
+        $rcp->setValue($comparisoncontext, $testpath);
+
+        $this->assertEquals($expected, $context->is_parent_of($comparisoncontext, $testself));
+    }
+
+    /**
+     * Data provider for is_child_of context checks.
+     *
+     * @return  array
+     */
+    public function is_child_of_provider(): array {
+        $provideboth = function(string $desc, string $contextpath, string $testpath, bool $expected): array {
+            return [
+                "includeself: true; {$desc}" => [
+                    $contextpath,
+                    $testpath,
+                    true,
+                    $expected,
+                ],
+                "includeself: false; {$desc}" => [
+                    $contextpath,
+                    $testpath,
+                    false,
+                    $expected,
+                ],
+            ];
+        };
+
+        return array_merge(
+            [
+                'includeself: true, testing self' => [
+                    '/1/4/17/291/1001/17105',
+                    '/1/4/17/291/1001/17105',
+                    true,
+                    true,
+                ],
+                'includeself: false, testing self' => [
+                    '/1/4/17/291/1001/17105',
+                    '/1/4/17/291/1001/17105',
+                    false,
+                    false,
+                ],
+            ],
+            $provideboth(
+                'testing child',
+                '/1/4/17/291/1001/17105',
+                '/1/4/17/291/1001',
+                true
+            ),
+            $provideboth(
+                'testing parent',
+                '/1/4/17/291/1001',
+                '/1/4/17/291/1001/17105',
+                false
+            ),
+            $provideboth(
+                'testing grandchild',
+                '/1/4/17/291/1001/17105',
+                '/1',
+                true
+            ),
+            $provideboth(
+                'testing grandparent',
+                '/1',
+                '/1/4/17/291/1001/17105',
+                false
+            )
+        );
+    }
+
+    /**
+     * Ensure that the is_child_of() function works as anticipated.
+     *
+     * @dataProvider is_child_of_provider
+     * @param   string $contextpath The path of the context being compared with
+     * @param   string $testpath The path of the context being compared
+     * @param   bool $testself Whether to check the current context
+     * @param   bool $expected The expected result
+     */
+    public function test_is_child_of(string $contextpath, string $testpath, bool $testself, bool $expected): void {
+        $context = $this->getMockBuilder(\context::class)
+            ->disableOriginalConstructor()
+            ->setMethods([
+                'get_url',
+                'get_capabilities',
+            ])
+            ->getMock();
+
+        $rcp = new ReflectionProperty($context, '_path');
+        $rcp->setAccessible(true);
+        $rcp->setValue($context, $contextpath);
+
+        $comparisoncontext = $this->getMockBuilder(\context::class)
+            ->disableOriginalConstructor()
+            ->setMethods([
+                'get_url',
+                'get_capabilities',
+            ])
+            ->getMock();
+
+        $rcp = new ReflectionProperty($comparisoncontext, '_path');
+        $rcp->setAccessible(true);
+        $rcp->setValue($comparisoncontext, $testpath);
+
+        $this->assertEquals($expected, $context->is_child_of($comparisoncontext, $testself));
+    }
+
     /**
      * Ensure that the get_parent_contexts() function limits the number of queries it performs.
      */
index 8906527..e7f23ef 100644 (file)
@@ -47,7 +47,7 @@ class behat_deprecated extends behat_base {
      * @param string $blockname
      * @return void
      * @deprecated since Moodle 3.7 MDL-64506 - please do not use this definition step any more.
-     * @todo MDL-65215 This will be deleted in Moodle 4.1.
+     * @todo MDL-65215 This will be deleted in Moodle 3.11.
      */
     public function i_dock_block($blockname) {
 
index a5f08be..23c54ee 100644 (file)
@@ -109,7 +109,7 @@ information provided here is intended especially for developers.
     - type: specifies the calendar type. Optional, defaults to Gregorian.
     - fixday: Whether to remove leading zero for day. Optional, defaults to 1.
     - fixhour: Whether to remove leading zero for hour. Optional, defaults to 1.
-* Legacy cron has been deprecated and will be removed in Moodle 4.3. This includes the functions:
+* Legacy cron has been deprecated and will be removed in Moodle 4.1. This includes the functions:
   - cron_execute_plugin_type()
   - cron_bc_hack_plugin_functions()
   Please, use the Task API instead: https://docs.moodle.org/dev/Task_API
index 2bf824f..613704c 100644 (file)
Binary files a/media/player/videojs/amd/build/loader.min.js and b/media/player/videojs/amd/build/loader.min.js differ
index f0d4d84..5b2c668 100644 (file)
Binary files a/media/player/videojs/amd/build/loader.min.js.map and b/media/player/videojs/amd/build/loader.min.js.map differ
index 70455f2..17357b2 100644 (file)
  *
  * This takes care of applying the filter on content which was dynamically loaded.
  *
+ * @module     media_videojs/loader
  * @package    media_videojs
  * @copyright  2016 Frédéric Massart - FMCorz.net
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-define(['jquery', 'core/event'], function($, Event) {
-
-    /**
-     * Stores the method we need to execute on the first load of videojs module.
-     */
-    var onload;
-
-    /**
-     * Set-up.
-     *
-     * Adds the listener for the event to then notify video.js.
-     * @param {Function} executeonload function to execute when media_videojs/video is loaded
-     */
-    var setUp = function(executeonload) {
-        onload = executeonload;
-        // Notify Video.js about the nodes already present on the page.
-        notifyVideoJS(null, $('body'));
-        // We need to call popover automatically if nodes are added to the page later.
-        Event.getLegacyEvents().done(function(events) {
-            $(document).on(events.FILTER_CONTENT_UPDATED, notifyVideoJS);
-        });
-    };
 
-    /**
-     * Notify video.js of new nodes.
-     *
-     * @param {Event} e The event.
-     * @param {NodeList} nodes List of new nodes.
-     */
-    var notifyVideoJS = function(e, nodes) {
-        var selector = '.mediaplugin_videojs';
-
-        // Find the descendants matching the expected parent of the audio and video
-        // tags. Then also addBack the nodes matching the same selector. Finally,
-        // we find the audio and video tags contained in those parents. Kind thanks
-        // to jQuery for the simplicity.
-        nodes.find(selector)
-            .addBack(selector)
-            .find('audio, video').each(function() {
-                var id = $(this).attr('id'),
-                    config = $(this).data('setup-lazy'),
-                    modules = ['media_videojs/video-lazy'];
-
-                if (config.techOrder && config.techOrder.indexOf('youtube') !== -1) {
-                    // Add YouTube to the list of modules we require.
-                    modules.push('media_videojs/Youtube-lazy');
-                }
-                if (config.techOrder && config.techOrder.indexOf('flash') !== -1) {
-                    // Add Flash to the list of modules we require.
-                    modules.push('media_videojs/videojs-flash-lazy');
+import Config from 'core/config';
+import Event from 'core/event';
+import jQuery from 'jquery';
+import Ajax from 'core/ajax';
+import LocalStorage from 'core/localstorage';
+import Notification from 'core/notification';
+
+/** @var {bool} Whether this is the first load of videojs module */
+let firstLoad;
+
+/** @var {string} The language that is used in the player */
+let language;
+
+/** @var {object} List of languages and translations for the current page */
+let langStringCache;
+
+/**
+ * Set-up.
+ *
+ * Adds the listener for the event to then notify video.js.
+ * @param {string} lang Language to be used in the player
+ */
+export const setUp = (lang) => {
+    language = lang;
+    firstLoad = true;
+    // Notify Video.js about the nodes already present on the page.
+    notifyVideoJS(null, jQuery('body'));
+    // We need to call popover automatically if nodes are added to the page later.
+    Event.getLegacyEvents().done((events) => {
+        jQuery(document).on(events.FILTER_CONTENT_UPDATED, notifyVideoJS);
+    });
+};
+
+/**
+ * Notify video.js of new nodes.
+ *
+ * @param {Event} e The event.
+ * @param {NodeList} nodes List of new nodes.
+ */
+const notifyVideoJS = (e, nodes) => {
+    const selector = '.mediaplugin_videojs';
+    const langStrings = getLanguageJson();
+
+    // Find the descendants matching the expected parent of the audio and video
+    // tags. Then also addBack the nodes matching the same selector. Finally,
+    // we find the audio and video tags contained in those parents. Kind thanks
+    // to jQuery for the simplicity.
+    nodes.find(selector)
+        .addBack(selector)
+        .find('audio, video').each((index, element) => {
+            const id = jQuery(element).attr('id');
+            const config = jQuery(element).data('setup-lazy');
+            const modulePromises = [import('media_videojs/video-lazy')];
+
+            if (config.techOrder && config.techOrder.indexOf('youtube') !== -1) {
+                // Add YouTube to the list of modules we require.
+                modulePromises.push(import('media_videojs/Youtube-lazy'));
+            }
+            if (config.techOrder && config.techOrder.indexOf('flash') !== -1) {
+                // Add Flash to the list of modules we require.
+                modulePromises.push(import('media_videojs/videojs-flash-lazy'));
+            }
+            Promise.all([langStrings, ...modulePromises])
+            .then(([langJson, videojs]) => {
+                if (firstLoad) {
+                    videojs.options.flash.swf = `${Config.wwwroot}/media/player/videojs/videojs/video-js.swf`;
+                    videojs.addLanguage(language, langJson);
+
+                    firstLoad = false;
                 }
-                require(modules, function(videojs) {
-                    if (onload) {
-                        onload(videojs);
-                        onload = null;
-                    }
-                    videojs(id, config);
-                });
-            });
-    };
+                videojs(id, config);
+                return;
+            })
+            .catch(Notification.exception);
+        });
+};
+
+/**
+ * Returns the json object of the language strings to be used in the player.
+ *
+ * @returns {Promise}
+ */
+const getLanguageJson = () => {
+    if (langStringCache) {
+        return Promise.resolve(langStringCache);
+    }
 
-    return {
-        setUp: setUp
+    const cacheKey = `media_videojs/${language}`;
+
+    const rawCacheContent = LocalStorage.get(cacheKey);
+    if (rawCacheContent) {
+        const cacheContent = JSON.parse(rawCacheContent);
+
+        langStringCache = cacheContent;
+
+        return Promise.resolve(langStringCache);
+    }
+
+    const request = {
+        methodname: 'media_videojs_get_language',
+        args: {
+            lang: language,
+        },
     };
 
-});
+    return Ajax.call([request])[0]
+        .then(langStringData => {
+            LocalStorage.set(cacheKey, langStringData);
+
+            return langStringData;
+        })
+        .then(result => JSON.parse(result))
+        .then(langStrings => {
+            langStringCache = langStrings;
+
+            return langStrings;
+        });
+};
diff --git a/media/player/videojs/classes/external/get_language.php b/media/player/videojs/classes/external/get_language.php
new file mode 100644 (file)
index 0000000..9e32bab
--- /dev/null
@@ -0,0 +1,73 @@
+<?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/>.
+
+/**
+ * External API to get language strings for the videojs.
+ *
+ * @package    media_videojs
+ * @copyright  2020 Shamim Rezaie <shamim@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace media_videojs\external;
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once("$CFG->libdir/externallib.php");
+
+use external_api;
+use external_function_parameters;
+use external_value;
+
+/**
+ * The API to get language strings for the videojs.
+ *
+ * @copyright  2020 Shamim Rezaie <shamim@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class get_language extends external_api {
+    /**
+     * Returns description of parameters.
+     *
+     * @return external_function_parameters
+     */
+    public static function execute_parameters() {
+        return new external_function_parameters([
+            'lang' => new external_value(PARAM_ALPHAEXT, 'language')
+        ]);
+    }
+
+    /**
+     * Returns language strings in the JSON format
+     *
+     * @param string $lang The language code
+     * @return string
+     */
+    public static function execute(string $lang) {
+        external_api::validate_parameters(self::execute_parameters(), ['lang' => $lang]);
+
+        return \media_videojs_plugin::get_language_content($lang);
+    }
+
+    /**
+     * Returns description of method result value
+     *
+     * @return external_value
+     */
+    public static function execute_returns() {
+        return new external_value(PARAM_RAW, 'language pack json');
+    }
+}
index 2e5b170..7ca33e3 100644 (file)
@@ -340,33 +340,40 @@ class media_videojs_plugin extends core_media_player_native {
         $langfiles = get_directory_list($basedir);
         $candidates = [];
         foreach ($langfiles as $langfile) {
-            if (strtolower(pathinfo($langfile, PATHINFO_EXTENSION)) !== 'js') {
+            if (strtolower(pathinfo($langfile, PATHINFO_EXTENSION)) !== 'json') {
                 continue;
             }
-            $lang = basename($langfile, '.js');
-            if (strtolower($langfile) === $this->language . '.js') {
-                // Found an exact match for the language.
-                $js = file_get_contents($basedir . $langfile);
-                break;
+            $lang = basename($langfile, '.json');
+            if (strtolower($langfile) === $this->language . '.json') {
+                // Found an exact match for the language. It is stored in $this->language.
+                return;
             }
             if (substr($this->language, 0, 2) === strtolower(substr($langfile, 0, 2))) {
                 // Not an exact match but similar, for example "pt_br" is similar to "pt".
                 $candidates[$lang] = $langfile;
             }
         }
-        if (empty($js) && $candidates) {
+
+        if ($candidates) {
             // Exact match was not found, take the first candidate.
             $this->language = key($candidates);
-            $js = file_get_contents($basedir . $candidates[$this->language]);
-        }
-        // Add it as a language for Video.JS.
-        if (!empty($js)) {
-            return "$js\n";
+        } else {
+            // Could not match, use default language of video player (English).
+            $this->language = null;
         }
+    }
+
+    /**
+     * Returns the requested language pack in the json format.
+     *
+     * @param string $lang The language code
+     * @return false|string The read data or false on failure
+     */
+    public static function get_language_content(string $lang) {
+        global $CFG;
+        $langfile = "{$CFG->dirroot}/media/player/videojs/videojs/lang/{$lang}.json";
 
-        // Could not match, use default language of video player (English).
-        $this->language = null;
-        return "";
+        return file_exists($langfile) ? file_get_contents($langfile) : '';
     }
 
     public function supports($usedextensions = []) {
@@ -421,14 +428,10 @@ class media_videojs_plugin extends core_media_player_native {
         // Load dynamic loader. It will scan page for videojs media and load necessary modules.
         // Loader will be loaded on absolutely every page, however the videojs will only be loaded
         // when video is present on the page or added later to it in AJAX.
-        $path = new moodle_url('/media/player/videojs/videojs/video-js.swf');
-        $contents = 'videojs.options.flash.swf = "' . $path . '";' . "\n";
-        $contents .= $this->find_language(current_language());
+        $this->find_language();
         $page->requires->js_amd_inline(<<<EOT
 require(["media_videojs/loader"], function(loader) {
-    loader.setUp(function(videojs) {
-        $contents
-    });
+    loader.setUp('$this->language');
 });
 EOT
         );
diff --git a/media/player/videojs/db/services.php b/media/player/videojs/db/services.php
new file mode 100644 (file)
index 0000000..e96d97f
--- /dev/null
@@ -0,0 +1,38 @@
+<?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/>.
+
+/**
+ * VideoJS player external functions and service definitions.
+ *
+ * @package    media_videojs
+ * @copyright  2020 Shamim Rezaie <shamim@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+$functions = [
+    'media_videojs_get_language' => [
+        'classname'     => 'media_videojs\external\get_language',
+        'methodname'    => 'execute',
+        'classpath'     => '',
+        'description'   => 'get language.',
+        'type'          => 'read',
+        'ajax'          => 'true',
+        'capabilities'  => '',
+        'loginrequired' => false,
+    ]
+];
index 908d0e9..5e6ebaa 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2020061500;         // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2020082701;         // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2020060900;         // Requires this Moodle version
 $plugin->component = 'media_videojs'; // Full name of the plugin (used for diagnostics).
index 12a1412..62651a7 100644 (file)
Binary files a/message/amd/build/message_repository.min.js and b/message/amd/build/message_repository.min.js differ
index f2f9710..053d670 100644 (file)
Binary files a/message/amd/build/message_repository.min.js.map and b/message/amd/build/message_repository.min.js.map differ
index ebfd3cd..1294576 100644 (file)
Binary files a/message/amd/build/toggle_contact_button.min.js and b/message/amd/build/toggle_contact_button.min.js differ
index 0466155..6f2d7ef 100644 (file)
Binary files a/message/amd/build/toggle_contact_button.min.js.map and b/message/amd/build/toggle_contact_button.min.js.map differ
index 56b5925..d034a70 100644 (file)
@@ -36,48 +36,6 @@ define(
 
     var CONVERSATION_TYPES = Constants.CONVERSATION_TYPES;
 
-    /**
-     * Retrieve a list of messages from the server.
-     *
-     * @param {object} args The request arguments:
-     * @return {object} jQuery promise
-     */
-    var query = function(args) {
-        // Normalise the arguments to use limit/offset rather than limitnum/limitfrom.
-        if (typeof args.limit === 'undefined') {
-            args.limit = 0;
-        }
-
-        if (typeof args.offset === 'undefined') {
-            args.offset = 0;
-        }
-
-        if (typeof args.type === 'undefined') {
-            args.type = null;
-        }
-
-        if (typeof args.favouritesonly === 'undefined') {
-            args.favouritesonly = false;
-        }
-
-        args.limitfrom = args.offset;
-        args.limitnum = args.limit;
-
-        delete args.limit;
-        delete args.offset;
-
-        var request = {
-            methodname: 'core_message_data_for_messagearea_conversations',
-            args: args
-        };
-
-        var promise = Ajax.call([request])[0];
-
-        promise.fail(Notification.exception);
-
-        return promise;
-    };
-
     /**
      * Count the number of unread conversations (one or more messages from a user)
      * for a given user.
@@ -98,25 +56,6 @@ define(
         return promise;
     };
 
-    /**
-     * Mark all of unread messages for a user as read.
-     *
-     * @param {object} args The request arguments:
-     * @return {object} jQuery promise
-     */
-    var markAllAsRead = function(args) {
-        var request = {
-            methodname: 'core_message_mark_all_messages_as_read',
-            args: args
-        };
-
-        var promise = Ajax.call([request])[0];
-
-        promise.fail(Notification.exception);
-
-        return promise;
-    };
-
     /**
      * Get contacts for given user.
      *
@@ -146,25 +85,6 @@ define(
         return Ajax.call([request])[0];
     };
 
-    /**
-     * Request profile information as a user for a given user.
-     *
-     * @param {int} userId The requesting user
-     * @param {int} profileUserId The id of the user who's profile is being requested
-     * @return {object} jQuery promise
-     */
-    var getProfile = function(userId, profileUserId) {
-        var request = {
-            methodname: 'core_message_data_for_messagearea_get_profile',
-            args: {
-                currentuserid: userId,
-                otheruserid: profileUserId
-            }
-        };
-
-        return Ajax.call([request])[0];
-    };
-
     /**
      * Block a user.
      *
@@ -1139,11 +1059,8 @@ define(
     };
 
     return {
-        query: query,
         countUnreadConversations: countUnreadConversations,
-        markAllAsRead: markAllAsRead,
         getContacts: getContacts,
-        getProfile: getProfile,
         blockUser: blockUser,
         unblockUser: unblockUser,
         createContactRequest: createContactRequest,
index aa22a42..bf5349f 100644 (file)
@@ -67,6 +67,17 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/cust
         return element.attr('data-userid');
     };
 
+    /**
+     * Get the id for the logged in user.
+     *
+     * @method getUserId
+     * @param {object} element jQuery object for the button
+     * @return {int}
+     */
+    var getCurrentUserId = function(element) {
+        return element.attr('data-currentuserid');
+    };
+
     /**
      * Check if this element is currently loading.
      *
@@ -117,9 +128,10 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/cust
         }
 
         var request = {
-            methodname: 'core_message_create_contacts',
+            methodname: 'core_message_create_contact_request',
             args: {
-                userids: [getUserId(element)],
+                userid: getCurrentUserId(element),
+                requesteduserid: getUserId(element),
             }
         };
         sendRequest(element, request).done(function() {
index 86708bc..4d758b1 100644 (file)
@@ -167,138 +167,17 @@ class api {
     }
 
     /**
-     * Handles searching for user in a particular course in the message area.
-     *
-     * TODO: This function should be removed once the related web service goes through final deprecation.
-     * The related web service is data_for_messagearea_search_users_in_course.
-     * Followup: MDL-63261
-     *
-     * @param int $userid The user id doing the searching
-     * @param int $courseid The id of the course we are searching in
-     * @param string $search The string the user is searching
-     * @param int $limitfrom
-     * @param int $limitnum
-     * @return array
+     * @deprecated since 3.6
      */
-    public static function search_users_in_course($userid, $courseid, $search, $limitfrom = 0, $limitnum = 0) {
-        global $DB;
-
-        // Get all the users in the course.
-        list($esql, $params) = get_enrolled_sql(\context_course::instance($courseid), '', 0, true);
-        $sql = "SELECT u.*, mub.id as isblocked
-                  FROM {user} u
-                  JOIN ($esql) je
-                    ON je.id = u.id
-             LEFT JOIN {message_users_blocked} mub
-                    ON (mub.blockeduserid = u.id AND mub.userid = :userid)
-                 WHERE u.deleted = 0";
-        // Add more conditions.
-        $fullname = $DB->sql_fullname();
-        $sql .= " AND u.id != :userid2
-                  AND " . $DB->sql_like($fullname, ':search', false) . "
-             ORDER BY " . $DB->sql_fullname();
-        $params = array_merge(array('userid' => $userid, 'userid2' => $userid, 'search' => '%' . $search . '%'), $params);
-
-        // Convert all the user records into contacts.
-        $contacts = array();
-        if ($users = $DB->get_records_sql($sql, $params, $limitfrom, $limitnum)) {
-            foreach ($users as $user) {
-                $user->blocked = $user->isblocked ? 1 : 0;
-                $contacts[] = helper::create_contact($user);
-            }
-        }
-
-        return $contacts;
+    public static function search_users_in_course() {
+        throw new \coding_exception('\core_message\api::search_users_in_course has been removed.');
     }
 
     /**
-     * Handles searching for user in the message area.
-     *
-     * TODO: This function should be removed once the related web service goes through final deprecation.
-     * The related web service is data_for_messagearea_search_users.
-     * Followup: MDL-63261
-     *
-     * @param int $userid The user id doing the searching
-     * @param string $search The string the user is searching
-     * @param int $limitnum
-     * @return array
+     * @deprecated since 3.6
      */
-    public static function search_users($userid, $search, $limitnum = 0) {
-        global $CFG, $DB;
-
-        // Used to search for contacts.
-        $fullname = $DB->sql_fullname();
-        $ufields = \user_picture::fields('u', array('lastaccess'));
-
-        // Users not to include.
-        $excludeusers = array($userid, $CFG->siteguest);
-        list($exclude, $excludeparams) = $DB->get_in_or_equal($excludeusers, SQL_PARAMS_NAMED, 'param', false);
-
-        // Ok, let's search for contacts first.
-        $contacts = array();
-        $sql = "SELECT $ufields, mub.id as isuserblocked
-                  FROM {user} u
-                  JOIN {message_contacts} mc
-                    ON u.id = mc.contactid
-             LEFT JOIN {message_users_blocked} mub
-                    ON (mub.userid = :userid2 AND mub.blockeduserid = u.id)
-                 WHERE mc.userid = :userid
-                   AND u.deleted = 0
-                   AND u.confirmed = 1
-                   AND " . $DB->sql_like($fullname, ':search', false) . "
-                   AND u.id $exclude
-              ORDER BY " . $DB->sql_fullname();
-        if ($users = $DB->get_records_sql($sql, array('userid' => $userid, 'userid2' => $userid,
-                'search' => '%' . $search . '%') + $excludeparams, 0, $limitnum)) {
-            foreach ($users as $user) {
-                $user->blocked = $user->isuserblocked ? 1 : 0;
-                $contacts[] = helper::create_contact($user);
-            }
-        }
-
-        // Now, let's get the courses.
-        // Make sure to limit searches to enrolled courses.
-        $enrolledcourses = enrol_get_my_courses(array('id', 'cacherev'));
-        $courses = array();
-        // Really we want the user to be able to view the participants if they have the capability
-        // 'moodle/course:viewparticipants' or 'moodle/course:enrolreview', but since the search_courses function
-        // only takes required parameters we can't. However, the chance of a user having 'moodle/course:enrolreview' but
-        // *not* 'moodle/course:viewparticipants' are pretty much zero, so it is not worth addressing.
-        if ($arrcourses = \core_course_category::search_courses(array('search' => $search), array('limit' => $limitnum),
-                array('moodle/course:viewparticipants'))) {
-            foreach ($arrcourses as $course) {
-                if (isset($enrolledcourses[$course->id])) {
-                    $data = new \stdClass();
-                    $data->id = $course->id;
-                    $data->shortname = $course->shortname;
-                    $data->fullname = $course->fullname;
-                    $courses[] = $data;
-                }
-            }
-        }
-
-        // Let's get those non-contacts. Toast them gears boi.
-        // Note - you can only block contacts, so these users will not be blocked, so no need to get that
-        // extra detail from the database.
-        $noncontacts = array();
-        $sql = "SELECT $ufields
-                  FROM {user} u
-                 WHERE u.deleted = 0
-                   AND u.confirmed = 1
-                   AND " . $DB->sql_like($fullname, ':search', false) . "
-                   AND u.id $exclude
-                   AND u.id NOT IN (SELECT contactid
-                                      FROM {message_contacts}
-                                     WHERE userid = :userid)
-              ORDER BY " . $DB->sql_fullname();
-        if ($users = $DB->get_records_sql($sql,  array('userid' => $userid, 'search' => '%' . $search . '%') + $excludeparams,
-                0, $limitnum)) {
-            foreach ($users as $user) {
-                $noncontacts[] = helper::create_contact($user);
-            }
-        }
-
-        return array($contacts, $courses, $noncontacts);
+    public static function search_users() {
+        throw new \coding_exception('\core_message\api::search_users has been removed.');
     }
 
     /**
@@ -1070,55 +949,10 @@ class api {
     }
 
     /**
-     * Returns the contacts to display in the contacts area.
-     *
-     * TODO: This function should be removed once the related web service goes through final deprecation.
-     * The related web service is data_for_messagearea_contacts.
-     * Followup: MDL-63261
-     *
-     * @param int $userid The user id
-     * @param int $limitfrom
-     * @param int $limitnum
-     * @return array
+     * @deprecated since 3.6
      */
-    public static function get_contacts($userid, $limitfrom = 0, $limitnum = 0) {
-        global $DB;
-
-        $contactids = [];
-        $sql = "SELECT mc.*
-                  FROM {message_contacts} mc
-                 WHERE mc.userid = ? OR mc.contactid = ?
-              ORDER BY timecreated DESC";
-        if ($contacts = $DB->get_records_sql($sql, [$userid, $userid], $limitfrom, $limitnum)) {
-            foreach ($contacts as $contact) {
-                if ($userid == $contact->userid) {
-                    $contactids[] = $contact->contactid;
-                } else {
-                    $contactids[] = $contact->userid;
-                }
-            }
-        }
-
-        if (!empty($contactids)) {
-            list($insql, $inparams) = $DB->get_in_or_equal($contactids);
-
-            $sql = "SELECT u.*, mub.id as isblocked
-                      FROM {user} u
-                 LEFT JOIN {message_users_blocked} mub
-                        ON u.id = mub.blockeduserid
-                     WHERE u.id $insql";
-            if ($contacts = $DB->get_records_sql($sql, $inparams)) {
-                $arrcontacts = [];
-                foreach ($contacts as $contact) {
-                    $contact->blocked = $contact->isblocked ? 1 : 0;
-                    $arrcontacts[] = helper::create_contact($contact);
-                }
-
-                return $arrcontacts;
-            }
-        }
-
-        return [];
+    public static function get_contacts() {
+        throw new \coding_exception('\core_message\api::get_contacts has been removed.');
     }
 
     /**
@@ -1170,6 +1004,8 @@ class api {
      * Returns the an array of the users the given user is in a conversation
      * with who are a contact and the number of unread messages.
      *
+     * @deprecated since 3.10
+     * TODO: MDL-69643
      * @param int $userid The user id
      * @param int $limitfrom
      * @param int $limitnum
@@ -1178,6 +1014,9 @@ class api {
     public static function get_contacts_with_unread_message_count($userid, $limitfrom = 0, $limitnum = 0) {
         global $DB;
 
+        debugging('\core_message\api::get_contacts_with_unread_message_count is deprecated and no longer used',
+            DEBUG_DEVELOPER);
+
         $userfields = \user_picture::fields('u', array('lastaccess'));
         $unreadcountssql = "SELECT $userfields, count(m.id) as messagecount
                               FROM {message_contacts} mc
@@ -1206,6 +1045,8 @@ class api {
      * Returns the an array of the users the given user is in a conversation
      * with who are not a contact and the number of unread messages.
      *
+     * @deprecated since 3.10
+     * TODO: MDL-69643
      * @param int $userid The user id
      * @param int $limitfrom
      * @param int $limitnum
@@ -1214,6 +1055,9 @@ class api {
     public static function get_non_contacts_with_unread_message_count($userid, $limitfrom = 0, $limitnum = 0) {
         global $DB;
 
+        debugging('\core_message\api::get_non_contacts_with_unread_message_count is deprecated and no longer used',
+            DEBUG_DEVELOPER);
+
         $userfields = \user_picture::fields('u', array('lastaccess'));
         $unreadcountssql = "SELECT $userfields, count(m.id) as messagecount
                               FROM {user} u
@@ -1240,52 +1084,10 @@ class api {
     }
 
     /**
-     * Returns the messages to display in the message area.
-     *
-     * TODO: This function should be removed once the related web service goes through final deprecation.
-     * The related web service is data_for_messagearea_messages.
-     * Followup: MDL-63261
-     *
-     * @param int $userid the current user
-     * @param int $otheruserid the other user
-     * @param int $limitfrom
-     * @param int $limitnum
-     * @param string $sort
-     * @param int $timefrom the time from the message being sent
-     * @param int $timeto the time up until the message being sent
-     * @return array
+     * @deprecated since 3.6
      */
-    public static function get_messages($userid, $otheruserid, $limitfrom = 0, $limitnum = 0,
-            $sort = 'timecreated ASC', $timefrom = 0, $timeto = 0) {
-
-        if (!empty($timefrom)) {
-            // Get the conversation between userid and otheruserid.
-            $userids = [$userid, $otheruserid];
-            if (!$conversationid = self::get_conversation_between_users($userids)) {
-                // This method was always used for individual conversations.
-                $conversation = self::create_conversation(self::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $userids);
-                $conversationid = $conversation->id;
-            }
-
-            // Check the cache to see if we even need to do a DB query.
-            $cache = \cache::make('core', 'message_time_last_message_between_users');
-            $key = helper::get_last_message_time_created_cache_key($conversationid);
-            $lastcreated = $cache->get($key);
-
-            // The last known message time is earlier than the one being requested so we can
-            // just return an empty result set rather than having to query the DB.
-            if ($lastcreated && $lastcreated < $timefrom) {
-                return [];
-            }
-        }
-
-        $arrmessages = array();
-        if ($messages = helper::get_messages($userid, $otheruserid, 0, $limitfrom, $limitnum,
-                                             $sort, $timefrom, $timeto)) {
-            $arrmessages = helper::create_messages($userid, $messages);
-        }
-
-        return $arrmessages;
+    public static function get_messages() {
+        throw new \coding_exception('\core_message\api::get_messages has been removed.');
     }
 
     /**
@@ -1321,26 +1123,10 @@ class api {
     }
 
     /**
-     * Returns the most recent message between two users.
-     *
-     * TODO: This function should be removed once the related web service goes through final deprecation.
-     * The related web service is data_for_messagearea_get_most_recent_message.
-     * Followup: MDL-63261
-     *
-     * @param int $userid the current user
-     * @param int $otheruserid the other user
-     * @return \stdClass|null
+     * @deprecated since 3.6
      */
-    public static function get_most_recent_message($userid, $otheruserid) {
-        // We want two messages here so we get an accurate 'blocktime' value.
-        if ($messages = helper::get_messages($userid, $otheruserid, 0, 0, 2, 'timecreated DESC')) {
-            // Swap the order so we now have them in historical order.
-            $messages = array_reverse($messages);
-            $arrmessages = helper::create_messages($userid, $messages);
-            return array_pop($arrmessages);
-        }
-
-        return null;
+    public static function get_most_recent_message() {
+        throw new \coding_exception('\core_message\api::get_most_recent_message has been removed.');
     }
 
     /**
@@ -1366,58 +1152,10 @@ class api {
     }
 
     /**
-     * Returns the profile information for a contact for a user.
-     *
-     * TODO: This function should be removed once the related web service goes through final deprecation.
-     * The related web service is data_for_messagearea_get_profile.
-     * Followup: MDL-63261
-     *
-     * @param int $userid The user id
-     * @param int $otheruserid The id of the user whose profile we want to view.
-     * @return \stdClass
+     * @deprecated since 3.6
      */
-    public static function get_profile($userid, $otheruserid) {
-        global $CFG, $PAGE;
-
-        require_once($CFG->dirroot . '/user/lib.php');
-
-        $user = \core_user::get_user($otheruserid, '*', MUST_EXIST);
-
-        // Create the data we are going to pass to the renderable.
-        $data = new \stdClass();
-        $data->userid = $otheruserid;
-        $data->fullname = fullname($user);
-        $data->city = '';
-        $data->country = '';
-        $data->email = '';
-        $data->isonline = null;
-        // Get the user picture data - messaging has always shown these to the user.
-        $userpicture = new \user_picture($user);
-        $userpicture->size = 1; // Size f1.
-        $data->profileimageurl = $userpicture->get_url($PAGE)->out(false);
-        $userpicture->size = 0; // Size f2.
-        $data->profileimageurlsmall = $userpicture->get_url($PAGE)->out(false);
-
-        $userfields = user_get_user_details($user, null, array('city', 'country', 'email', 'lastaccess'));
-        if ($userfields) {
-            if (isset($userfields['city'])) {
-                $data->city = $userfields['city'];
-            }
-            if (isset($userfields['country'])) {
-                $data->country = $userfields['country'];
-            }
-            if (isset($userfields['email'])) {
-                $data->email = $userfields['email'];
-            }
-            if (isset($userfields['lastaccess'])) {
-                $data->isonline = helper::is_online($userfields['lastaccess']);
-            }
-        }
-
-        $data->isblocked = self::is_blocked($userid, $otheruserid);
-        $data->iscontact = self::is_contact($userid, $otheruserid);
-
-        return $data;
+    public static function get_profile() {
+        throw new \coding_exception('\core_message\api::get_profile has been removed.');
     }
 
     /**
@@ -1456,30 +1194,11 @@ class api {
     }
 
     /**
-     * Deletes a conversation.
-     *
-     * This function does not verify any permissions.
-     *
      * @deprecated since 3.6
-     * @param int $userid The user id of who we want to delete the messages for (this may be done by the admin
-     *  but will still seem as if it was by the user)
-     * @param int $otheruserid The id of the other user in the conversation
-     * @return bool
      */
-    public static function delete_conversation($userid, $otheruserid) {
-        debugging('\core_message\api::delete_conversation() is deprecated, please use ' .
-            '\core_message\api::delete_conversation_by_id() instead.', DEBUG_DEVELOPER);
-
-        $conversationid = self::get_conversation_between_users([$userid, $otheruserid]);
-
-        // If there is no conversation, there is nothing to do.
-        if (!$conversationid) {
-            return true;
-        }
-
-        self::delete_conversation_by_id($userid, $conversationid);
-
-        return true;
+    public static function delete_conversation() {
+        throw new \coding_exception('\core_message\api::delete_conversation() is deprecated, please use ' .
+            '\core_message\api::delete_conversation_by_id() instead.');
     }
 
     /**
@@ -2028,80 +1747,17 @@ class api {
     }
 
     /**
-     * Checks if the recipient is allowing messages from users that aren't a
-     * contact. If not then it checks to make sure the sender is in the
-     * recipient's contacts.
-     *
      * @deprecated since 3.6
-     * @param \stdClass $recipient The user object.
-     * @param \stdClass|null $sender The user object.
-     * @return bool true if $sender is blocked, false otherwise.
      */
-    public static function is_user_non_contact_blocked($recipient, $sender = null) {
-        debugging('\core_message\api::is_user_non_contact_blocked() is deprecated', DEBUG_DEVELOPER);
-
-        global $USER, $CFG;
-
-        if (is_null($sender)) {
-            // The message is from the logged in user, unless otherwise specified.
-            $sender = $USER;
-        }
-
-        $privacypreference = self::get_user_privacy_messaging_preference($recipient->id);
-        switch ($privacypreference) {
-            case self::MESSAGE_PRIVACY_SITE:
-                if (!empty($CFG->messagingallusers)) {
-                    // Users can be messaged without being contacts or members of the same course.
-                    break;
-                }
-                // When the $CFG->messagingallusers privacy setting is disabled, continue with the next
-                // case, because MESSAGE_PRIVACY_SITE is replaced to MESSAGE_PRIVACY_COURSEMEMBER.
-            case self::MESSAGE_PRIVACY_COURSEMEMBER:
-                // Confirm the sender and the recipient are both members of the same course.
-                if (enrol_sharing_course($recipient, $sender)) {
-                    // All good, the recipient and the sender are members of the same course.
-                    return false;
-                }
-            case self::MESSAGE_PRIVACY_ONLYCONTACTS:
-                // True if they aren't contacts (they can't send a message because of the privacy settings), false otherwise.
-                return !self::is_contact($sender->id, $recipient->id);
-        }
-
-        return false;
+    public static function is_user_non_contact_blocked() {
+        throw new \coding_exception('\core_message\api::is_user_non_contact_blocked() is deprecated');
     }
 
     /**
-     * Checks if the recipient has specifically blocked the sending user.
-     *
-     * Note: This function will always return false if the sender has the
-     * readallmessages capability at the system context level.
-     *
      * @deprecated since 3.6
-     * @param int $recipientid User ID of the recipient.
-     * @param int $senderid User ID of the sender.
-     * @return bool true if $sender is blocked, false otherwise.
      */
-    public static function is_user_blocked($recipientid, $senderid = null) {
-        debugging('\core_message\api::is_user_blocked is deprecated and should not be used.',
-            DEBUG_DEVELOPER);
-
-        global $USER;
-
-        if (is_null($senderid)) {
-            // The message is from the logged in user, unless otherwise specified.
-            $senderid = $USER->id;
-        }
-
-        $systemcontext = \context_system::instance();
-        if (has_capability('moodle/site:readallmessages', $systemcontext, $senderid)) {
-            return false;
-        }
-
-        if (self::is_blocked($recipientid, $senderid)) {
-            return true;
-        }
-
-        return false;
+    public static function is_user_blocked() {
+        throw new \coding_exception('\core_message\api::is_user_blocked is deprecated and should not be used.');
     }
 
     /**
@@ -2465,20 +2121,11 @@ class api {
     }
 
     /**
-     * Creates a conversation between two users.
-     *
      * @deprecated since 3.6
-     * @param array $userids
-     * @return int The id of the conversation
      */
-    public static function create_conversation_between_users(array $userids) {
-        debugging('\core_message\api::create_conversation_between_users is deprecated, please use ' .
-            '\core_message\api::create_conversation instead.', DEBUG_DEVELOPER);
-
-        // This method was always used for individual conversations.
-        $conversation = self::create_conversation(self::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $userids);
-
-        return $conversation->id;
+    public static function create_conversation_between_users() {
+        throw new \coding_exception('\core_message\api::create_conversation_between_users is deprecated, please use ' .
+            '\core_message\api::create_conversation instead.');
     }
 
     /**
index c6472a4..286b36c 100644 (file)
@@ -37,82 +37,10 @@ require_once($CFG->dirroot . '/message/lib.php');
 class helper {
 
     /**
-     * Helper function to retrieve the messages between two users
-     *
-     * TODO: This function should be removed once the related web services go through final deprecation.
-     * The related web services are data_for_messagearea_messages AND data_for_messagearea_get_most_recent_message.
-     * Followup: MDL-63261
-     *
-     * @param int $userid the current user
-     * @param int $otheruserid the other user
-     * @param int $timedeleted the time the message was deleted
-     * @param int $limitfrom
-     * @param int $limitnum
-     * @param string $sort
-     * @param int $timefrom the time from the message being sent
-     * @param int $timeto the time up until the message being sent
-     * @return array of messages
+     * @deprecated since 3.6
      */
-    public static function get_messages($userid, $otheruserid, $timedeleted = 0, $limitfrom = 0, $limitnum = 0,
-                                        $sort = 'timecreated ASC', $timefrom = 0, $timeto = 0) {
-        global $DB;
-
-        $hash = self::get_conversation_hash([$userid, $otheruserid]);
-
-        $sql = "SELECT m.id, m.useridfrom, m.subject, m.fullmessage, m.fullmessagehtml,
-                       m.fullmessageformat, m.fullmessagetrust, m.smallmessage, m.timecreated,
-                       mc.contextid, muaread.timecreated AS timeread
-                  FROM {message_conversations} mc
-            INNER JOIN {messages} m
-                    ON m.conversationid = mc.id
-             LEFT JOIN {message_user_actions} muaread
-                    ON (muaread.messageid = m.id
-                   AND muaread.userid = :userid1
-                   AND muaread.action = :readaction)";
-        $params = ['userid1' => $userid, 'readaction' => api::MESSAGE_ACTION_READ, 'convhash' => $hash];
-
-        if (empty($timedeleted)) {
-            $sql .= " LEFT JOIN {message_user_actions} mua
-                             ON (mua.messageid = m.id
-                            AND mua.userid = :userid2
-                            AND mua.action = :deleteaction
-                            AND mua.timecreated is NOT NULL)";
-        } else {
-            $sql .= " INNER JOIN {message_user_actions} mua
-                              ON (mua.messageid = m.id
-                             AND mua.userid = :userid2
-                             AND mua.action = :deleteaction
-                             AND mua.timecreated = :timedeleted)";
-            $params['timedeleted'] = $timedeleted;
-        }
-
-        $params['userid2'] = $userid;
-        $params['deleteaction'] = api::MESSAGE_ACTION_DELETED;
-
-        $sql .= " WHERE mc.convhash = :convhash";
-
-        if (!empty($timefrom)) {
-            $sql .= " AND m.timecreated >= :timefrom";
-            $params['timefrom'] = $timefrom;
-        }
-
-        if (!empty($timeto)) {
-            $sql .= " AND m.timecreated <= :timeto";
-            $params['timeto'] = $timeto;
-        }
-
-        if (empty($timedeleted)) {
-            $sql .= " AND mua.id is NULL";
-        }
-
-        $sql .= " ORDER BY m.$sort";
-
-        $messages = $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
-        foreach ($messages as &$message) {
-            $message->useridto = ($message->useridfrom == $userid) ? $otheruserid : $userid;
-        }
-
-        return $messages;
+    public static function get_messages() {
+        throw new \coding_exception('\core_message\helper::get_messages has been removed.');
     }
 
     /**
@@ -234,55 +162,10 @@ class helper {
     }
 
     /**
-     * Helper function to return an array of messages.
-     *
-     * TODO: This function should be removed once the related web services go through final deprecation.
-     * The related web services are data_for_messagearea_messages AND data_for_messagearea_get_most_recent_message.
-     * Followup: MDL-63261
-     *
-     * @param int $userid
-     * @param array $messages
-     * @return array
+     * @deprecated since 3.6
      */
-    public static function create_messages($userid, $messages) {
-        // Store the messages.
-        $arrmessages = array();
-
-        // We always view messages from oldest to newest, ensure we have it in that order.
-        $lastmessage = end($messages);
-        $firstmessage = reset($messages);
-        if ($lastmessage->timecreated < $firstmessage->timecreated) {
-            $messages = array_reverse($messages);
-        }
-
-        // Keeps track of the last day, month and year combo we were viewing.
-        $day = '';
-        $month = '';
-        $year = '';
-        foreach ($messages as $message) {
-            // Check if we are now viewing a different block period.
-            $displayblocktime = false;
-            $date = usergetdate($message->timecreated);
-            if ($day != $date['mday'] || $month != $date['month'] || $year != $date['year']) {
-                $day = $date['mday'];
-                $month = $date['month'];
-                $year = $date['year'];
-                $displayblocktime = true;
-            }
-            // Store the message to pass to the renderable.
-            $msg = new \stdClass();
-            $msg->id = $message->id;
-            $msg->text = message_format_message_text($message);
-            $msg->currentuserid = $userid;
-            $msg->useridfrom = $message->useridfrom;
-            $msg->useridto = $message->useridto;
-            $msg->displayblocktime = $displayblocktime;
-            $msg->timecreated = $message->timecreated;
-            $msg->timeread = $message->timeread;
-            $arrmessages[] = $msg;
-        }
-
-        return $arrmessages;
+    public static function create_messages() {
+        throw new \coding_exception('\core_message\helper::create_messages has been removed.');
     }
 
     /**
@@ -428,7 +311,9 @@ class helper {
      * @return array
      */
     public static function togglecontact_link_params($user, $iscontact = false) {
+        global $USER;
         $params = array(
+            'data-currentuserid' => $USER->id,
             'data-userid' => $user->id,
             'data-is-contact' => $iscontact,
             'id' => 'toggle-contact-button',
@@ -639,47 +524,11 @@ class helper {
 
         return $members;
     }
-
     /**
-     * Backwards compatibility formatter, transforming the new output of get_conversations() into the old format.
-     *
-     * TODO: This function should be removed once the related web services go through final deprecation.
-     * The related web services are data_for_messagearea_conversations.
-     * Followup: MDL-63261
-     *
-     * @param array $conversations the array of conversations, which must come from get_conversations().
-     * @return array the array of conversations, formatted in the legacy style.
+     * @deprecated since 3.6
      */
-    public static function get_conversations_legacy_formatter(array $conversations) : array {
-        // Transform new data format back into the old format, just for BC during the deprecation life cycle.
-        $tmp = [];
-        foreach ($conversations as $id => $conv) {
-            // Only individual conversations were supported in legacy messaging.
-            if ($conv->type != \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL) {
-                continue;
-            }
-            $data = new \stdClass();
-            // The logic for the 'other user' is as follows:
-            // If a conversation is of type 'individual', the other user is always the member who is not the current user.
-            // If the conversation is of type 'group', the other user is always the sender of the most recent message.
-            // The get_conversations method already follows this logic, so we just need the first member.
-            $otheruser = reset($conv->members);
-            $data->userid = $otheruser->id;
-            $data->useridfrom = $conv->messages[0]->useridfrom ?? null;
-            $data->fullname = $conv->members[$otheruser->id]->fullname;
-            $data->profileimageurl = $conv->members[$otheruser->id]->profileimageurl;
-            $data->profileimageurlsmall = $conv->members[$otheruser->id]->profileimageurlsmall;
-            $data->ismessaging = isset($conv->messages[0]->text) ? true : false;
-            $data->lastmessage = $conv->messages[0]->text ? clean_param($conv->messages[0]->text, PARAM_NOTAGS) : null;
-            $data->lastmessagedate = $conv->messages[0]->timecreated ?? null;
-            $data->messageid = $conv->messages[0]->id ?? null;
-            $data->isonline = $conv->members[$otheruser->id]->isonline ?? null;
-            $data->isblocked = $conv->members[$otheruser->id]->isblocked ?? null;
-            $data->isread = $conv->isread;
-            $data->unreadcount = $conv->unreadcount;
-            $tmp[$data->userid] = $data;
-        }
-        return $tmp;
+    public static function get_conversations_legacy_formatter() {
+        throw new \coding_exception('\core_message\helper::get_conversations_legacy_formatter has been removed.');
     }
 
     /**
diff --git a/message/classes/output/messagearea/contact.php b/message/classes/output/messagearea/contact.php
deleted file mode 100644 (file)
index ab01237..0000000
+++ /dev/null
@@ -1,168 +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/>.
-
-/**
- * Contains class used to prepare a contact for display.
- *
- * TODO: This file should be removed once the related web services go through final deprecation.
- * Followup: MDL-63261
- *
- * @package   core_message
- * @copyright 2016 Mark Nelson <markn@moodle.com>
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_message\output\messagearea;
-
-defined('MOODLE_INTERNAL') || die();
-
-use renderable;
-use templatable;
-
-/**
- * Class to prepare a contact for display.
- *
- * @package   core_message
- * @copyright 2016 Mark Nelson <markn@moodle.com>
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class contact implements templatable, renderable {
-
-    /**
-     * @var int Maximum length of message to show in panel.
-     */
-    const MAX_MSG_LENGTH = 60;
-
-    /**
-     * @var int The userid.
-     */
-    public $userid;
-
-    /**
-     * @var int The id of the user who sent the last message.
-     */
-    public $useridfrom;
-
-    /**
-     * @var string The fullname.
-     */
-    public $fullname;
-
-    /**
-     * @var string The profile image url.
-     */
-    public $profileimageurl;
-
-    /**
-     * @var string The small profile image url.
-     */
-    public $profileimageurlsmall;
-
-    /**
-     * @var int The message id.
-     */
-    public $messageid;
-
-    /**
-     * @var bool Are we messaging the user?
-     */
-    public $ismessaging;
-
-    /**
-     * @var string The last message sent.
-     */
-    public $lastmessage;
-
-    /**
-     * @var int The last message sent timestamp.
-     */
-    public $lastmessagedate;
-
-    /**
-     * @var bool Is the user online?
-     */
-    public $isonline;
-
-    /**
-     * @var bool Is the user blocked?
-     */
-    public $isblocked;
-
-    /**
-     * @var bool Is the message read?
-     */
-    public $isread;
-
-    /**
-     * @var int The number of unread messages.
-     */
-    public $unreadcount;
-
-    /**
-     * @var int The id of the conversation to which to message belongs.
-     */
-    public $conversationid;
-
-    /**
-     * Constructor.
-     *
-     * @param \stdClass $contact
-     */
-    public function __construct($contact) {
-        $this->userid = $contact->userid;
-        $this->useridfrom = $contact->useridfrom;
-        $this->fullname = $contact->fullname;
-        $this->profileimageurl = $contact->profileimageurl;
-        $this->profileimageurlsmall = $contact->profileimageurlsmall;
-        $this->messageid = $contact->messageid;
-        $this->ismessaging = $contact->ismessaging;
-        $this->lastmessage = $contact->lastmessage;
-        $this->lastmessagedate = $contact->lastmessagedate;
-        $this->isonline = $contact->isonline;
-        $this->isblocked = $contact->isblocked;
-        $this->isread = $contact->isread;
-        $this->unreadcount = $contact->unreadcount;
-        $this->conversationid = $contact->conversationid ?? null;
-    }
-
-    public function export_for_template(\renderer_base $output) {
-        $contact = new \stdClass();
-        $contact->userid = $this->userid;
-        $contact->fullname = $this->fullname;
-        $contact->profileimageurl = $this->profileimageurl;
-        $contact->profileimageurlsmall = $this->profileimageurlsmall;
-        $contact->messageid = $this->messageid;
-        $contact->ismessaging = $this->ismessaging;
-        $contact->sentfromcurrentuser = false;
-        if ($this->lastmessage) {
-            if ($this->userid !== $this->useridfrom) {
-                $contact->sentfromcurrentuser = true;
-            }
-            $contact->lastmessage = shorten_text($this->lastmessage, self::MAX_MSG_LENGTH);
-        } else {
-            $contact->lastmessage = null;
-        }
-        $contact->lastmessagedate = $this->lastmessagedate;
-        $contact->showonlinestatus = is_null($this->isonline) ? false : true;
-        $contact->isonline = $this->isonline;
-        $contact->isblocked = $this->isblocked;
-        $contact->isread = $this->isread;
-        $contact->unreadcount = $this->unreadcount;
-        $contact->conversationid = $this->conversationid;
-
-        return $contact;
-    }
-}
diff --git a/message/classes/output/messagearea/contacts.php b/message/classes/output/messagearea/contacts.php
deleted file mode 100644 (file)
index 785d9a9..0000000
+++ /dev/null
@@ -1,96 +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/>.
-
-/**
- * Contains class used to prepare the contacts for display.
- *
- * TODO: This file should be removed once the related web services go through final deprecation.
- * Followup: MDL-63261
- *
- * @package   core_message
- * @copyright 2016 Mark Nelson <markn@moodle.com>
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_message\output\messagearea;
-
-defined('MOODLE_INTERNAL') || die();
-
-use renderable;
-use templatable;
-
-/**
- * Class to prepare the contacts for display.
- *
- * @package   core_message
- * @copyright 2016 Mark Nelson <markn@moodle.com>
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class contacts implements templatable, renderable {
-
-    /**
-     * @var int The id of the user that has been selected.
-     */
-    public $contactuserid;
-
-    /**
-     * @var array The contacts.
-     */
-    public $contacts;
-
-    /**
-     * Constructor.
-     *
-     * @param int|null $contactuserid The id of the user that has been selected
-     * @param array $contacts
-     */
-    public function __construct($contactuserid, $contacts) {
-        $this->contactuserid = $contactuserid;
-        $this->contacts = $contacts;
-    }
-
-    public function export_for_template(\renderer_base $output) {
-        $data = new \stdClass();
-        $data->contacts = array();
-        $userids = array();
-        foreach ($this->contacts as $contact) {
-            $contact = new contact($contact);
-            $contactdata = $contact->export_for_template($output);
-            $userids[$contactdata->userid] = $contactdata->userid;
-            // Check if the contact was selected.
-            if ($this->contactuserid == $contactdata->userid) {
-                $contactdata->selected = true;
-            }
-            $data->contacts[] = $contactdata;
-        }
-        // Check if the other user is not part of the contacts. We may be sending a message to someone
-        // we have not had a conversation with, so we want to add a new item to the contacts array.
-        if ($this->contactuserid && !isset($userids[$this->contactuserid])) {
-            $user = \core_user::get_user($this->contactuserid);
-            // Set an empty message so that we know we are messaging the user, and not viewing their profile.
-            $user->smallmessage = '';
-            $user->useridfrom = $user->id;
-            $contact = \core_message\helper::create_contact($user);
-            $contact = new contact($contact);
-            $contactdata = $contact->export_for_template($output);
-            $contactdata->selected = true;
-            // Put the contact at the front.
-            array_unshift($data->contacts, $contactdata);
-        }
-
-        return $data;
-    }
-}
diff --git a/message/classes/output/messagearea/message.php b/message/classes/output/messagearea/message.php
deleted file mode 100644 (file)
index 93177e0..0000000