Compare commits

...

90 Commits

Author SHA1 Message Date
Jun Pataleta ac9e395ed6 Moodle release 4.0.1 2022-05-07 19:34:54 +08:00
Eloy Lafuente (stronk7) 99fc164199 weekly release 4.0+ 2022-05-06 15:58:28 +02:00
Jake Dallimore 9ec2c12f3d MDL-74602 mod_assign: fix js error with assign grader on classic 2022-05-06 16:48:29 +08:00
Jun Pataleta 6ec89f6374 Merge branch 'MDL-74584-400-v1' of https://github.com/mihailges/moodle into MOODLE_400_STABLE 2022-05-06 14:17:37 +08:00
Mihail Geshoski 834c80849c MDL-74584 mod_bigbluebuttonbn: Update behat tests 2022-05-06 13:32:14 +08:00
Mihail Geshoski 674198a799 MDL-74584 mod_bigbluebuttonbn: Update unit tests 2022-05-06 13:32:14 +08:00
Mihail Geshoski 66c32a3687 MDL-74584 course: New optional parameter in get_module_types_names()
Adds new parameter in get_module_types_names() to optionally reset
the $modnames static cache.
2022-05-06 13:32:13 +08:00
Mihail Geshoski 0118227509 MDL-74584 mod_bigbluebuttonbn: Add install script and new upgrade step
Adds a new install script for the BBB module and a new upgrade step.
The BBB module now will be disabled by default for new installations
and also it will be disabled for the existing sites that use the
default BBB server configuration. Admistrators will be required to
confirm the acceptance of the related DPA prior to (re)enabling the
plugin once again.
2022-05-06 12:19:56 +08:00
Mihail Geshoski 2e1478b094 MDL-74584 mod_bigbluebuttonbn: Add DPA info in the general setting page
Adds a note in the BBB general settings page to raise awareness about
the legal obligations related to the data processing agreement with the
service provider which is currently configured.
2022-05-06 12:19:56 +08:00
Mihail Geshoski 6961258bc2 MDL-74584 mod_bigbluebuttonbn: Implement 'pre_enable_plugin_actions'
Implements a function for the 'pre_enable_plugin_actions' callback.
When the default BBB server settings are used while the administrator
attempts to enable the BBB plugin, the function will prevent this
action and trigger a dynamic form requesting the admin to conifrm
their acceptance of the related data processing agreement. The process
of enabling the plugin will continue as usual in cases where the
admin has already confirmed the acceptance of the dpa or a different
BBB server configuration is used other than the default.
2022-05-06 12:19:56 +08:00
Mihail Geshoski b4f6816952 MDL-74584 admin: Add callback executed prior to enabling a module
Adds a callback xxx_pre_enable_plugin_actions in admin/modules.php
which plugins can use to force additional actions before enabling the
plugin. The return value (bool) from the plugin callback method
specifies whether the process of enabling the plugin should continue
after the added actions or not.
2022-05-06 12:19:56 +08:00
Andrew Nicols 16b30d920b MDL-74511 core: Add phpcs default config 2022-05-06 12:01:32 +08:00
Ilya Tregubov 61d140e99f Merge branch 'MDL-41498-400' of https://github.com/ilyatregubov/moodle into MOODLE_400_STABLE 2022-05-06 08:26:59 +06:00
Robert Schrenk cc5eb38cea MDL-41498 course: courses participant page 2022-05-06 08:26:08 +06:00
Víctor Déniz 3b7bba9e75 Merge branch 'MDL-74612-400' of https://github.com/paulholden/moodle into MOODLE_400_STABLE 2022-05-05 13:25:37 +01:00
Víctor Déniz 2e020b506a Merge branch 'MDL-74128-400' of https://github.com/paulholden/moodle into MOODLE_400_STABLE 2022-05-05 12:09:59 +01:00
Víctor Déniz 74cdb7c4c8 Merge branch 'MDL-74567-400' of https://github.com/paulholden/moodle into MOODLE_400_STABLE 2022-05-05 09:39:39 +01:00
Paul Holden ef8d381a2f MDL-74567 core: re-factor flickr libraries to use shared user-agent. 2022-05-05 09:21:22 +01:00
Jun Pataleta 39717b72b1 MDL-74317 blocks: Revert checking of block usage against page format 2022-05-05 10:04:01 +08:00
Jun Pataleta e7ff99758e Merge branch 'MDL-74576-400' of https://github.com/snake/moodle into MOODLE_400_STABLE 2022-05-05 09:55:45 +08:00
Jun Pataleta 26492d4979 Merge branch 'MDL-73981-400' of https://github.com/ilyatregubov/moodle into MOODLE_400_STABLE 2022-05-05 09:49:06 +08:00
Ilya Tregubov 3abbcd9976 Merge branch 'MDL-74602-400' of https://github.com/snake/moodle into MOODLE_400_STABLE 2022-05-05 07:46:55 +06:00
Jun Pataleta 1456551ba6 Merge branch 'MDL-74546-400' of https://github.com/snake/moodle into MOODLE_400_STABLE 2022-05-05 09:41:34 +08:00
Ilya Tregubov c1ebec7cbc MDL-73981 tool_uploadcourse: Validate enrolment role from csv. 2022-05-05 07:39:50 +06:00
Jun Pataleta 812c8b37e5 Merge branch 'MDL-74511-400' of https://github.com/andrewnicols/moodle into MOODLE_400_STABLE 2022-05-05 09:37:31 +08:00
Jake Dallimore f2ab11aa5b MDL-73736 core_auth: Fix concurrency issue in login_attempt_failed()
This patch wraps the login_failed_count logic in a resource lock and
forces a user preferences cache reload. Each thread must wait for the
lock and must fetch the current count before incrementing it. This
ensures that login_failed_count is correct across threads and that the
lockout threshold is correctly honoured.

Co-Authored-By: Sujith Haridasan <sujith@moodle.com>
2022-05-04 14:45:00 +06:00
Michael Hawkins 240b402523 MDL-74333 badges: Check profile criteria valid when reviewing 2022-05-04 12:58:02 +08:00
Ilya Tregubov 34c4b3ce0c Merge branch 'MDL-74563-400' of https://github.com/paulholden/moodle into MOODLE_400_STABLE 2022-05-04 09:26:06 +06:00
Ferran Recio 8b098b4a4e MDL-71623 core_search: fix user document visibility 2022-05-04 03:55:18 +02:00
Amaia Anabitarte 38a40c5e4b MDL-74318 core_user: Description field as hidden on profile 2022-05-04 03:55:18 +02:00
Paul Holden 72580a5413 MDL-74204 mod_assign: escape identity fields in allocate marker form. 2022-05-04 03:55:18 +02:00
Jun Pataleta 6253ab0170 Merge branch 'MDL-73878-40' of https://github.com/aanabit/moodle into MOODLE_400_STABLE 2022-05-04 09:44:28 +08:00
Amaia Anabitarte ff89abc311 MDL-73878 core_user: Creating a new category for social fields 2022-05-03 17:46:18 +02:00
Amaia Anabitarte 7878d79359 MDL-73878 core_user: Fixing social fields upgrading issue 2022-05-03 17:43:22 +02:00
Jun Pataleta 93319747b2 Merge branch 'MDL-74543_400' of https://github.com/timhunt/moodle into MOODLE_400_STABLE 2022-05-03 22:22:45 +08:00
Tim Hunt f46c1b4fa9 MDL-74543 quiz attemptlib: covert string comparison to ===
Where obviously safe.
2022-05-03 13:48:04 +01:00
Tim Hunt a0297df86e MDL-74543 quiz: correctly record time when overdue attempt submitted 2022-05-03 13:48:04 +01:00
Víctor Déniz da9ab8a2f9 Merge branch 'MDL-74611_400' of https://github.com/timhunt/moodle into MOODLE_400_STABLE 2022-05-03 13:00:59 +01:00
Ilya Tregubov 33829b8b68 Merge branch 'mdl65478-fix-400' of https://github.com/matthewhilton/moodle into MOODLE_400_STABLE 2022-05-03 15:43:59 +06:00
Tim Hunt f5f468f6e1 MDL-74611 course settigns: re-order Common module settings
All the group-related settings should be next to each other.
2022-05-03 10:36:01 +01:00
Jake Dallimore 6b4f55c91b Merge branch 'MDL-74441-400' of https://github.com/cameron1729/moodle into MOODLE_400_STABLE 2022-05-03 16:54:50 +08:00
Cameron Ball 63ed56bc7c MDL-74441 mod_forum: Fix overflow in unified grader 2022-05-03 15:52:26 +08:00
Ilya Tregubov 841214dab2 Merge branch 'MDL-74598-400' of https://github.com/roland04/moodle into MOODLE_400_STABLE 2022-05-03 11:57:07 +06:00
Jason den Dulk 43cbc05a2d MDL-65478 backup, course format: Handle editor elements in course format
Modified course format options reading and writing to be able to handle Editor elements by enabling them to split array values into
multiple values before inserting into database, and combining multiple values into an array when reading from the database.
Modified backup and restore code to use backup_nested_elements, and to interact directly with the database.

Co-authored-by: Jason den Dulk <jasondendulk@catalyst-au.net>
Co-authored-by: Matthew Hilton <matthewhilton@catalyst-au.net>
2022-05-03 14:23:15 +10:00
Ilya Tregubov 973ea46697 Merge branch 'MDL-74450-400' of https://github.com/mihailges/moodle into MOODLE_400_STABLE 2022-05-03 09:52:26 +06:00
Jake Dallimore 98dbb93589 MDL-74602 mod_wiki: disable activity header in wiki print view 2022-05-03 11:09:00 +08:00
Jake Dallimore 5fa19e43b3 MDL-74602 mod_chat: disable activity header in ajax chat interface 2022-05-03 11:09:00 +08:00
Jake Dallimore 0d3a2c9181 MDL-74602 mod_book: disable activity header on book print page 2022-05-03 11:09:00 +08:00
Jake Dallimore 4f7bd72f72 MDL-74602 mod_assign: disable activity header on embedded grader page 2022-05-03 11:09:00 +08:00
Jake Dallimore cc182408aa MDL-74602 theme_boost: fix missing activity header in embedded layout
The activity header, which includes things like dates and completion
information, was moved out of the page $OUTPUT and into a separate
template context variable in 4.0, meaning it was missing from any layout
which didn't actively add it back to the page output. This fixes this
problem for the embedded layout only by adding support for the activity
header in the embedded template.
2022-05-03 11:09:00 +08:00
Jun Pataleta 8b3913605a Merge branch 'MDL-74317-400' of https://github.com/dravek/moodle into MOODLE_400_STABLE 2022-05-02 17:24:23 +08:00
Jun Pataleta 8ff67c91de Merge branch 'MDL-74436-400' of https://github.com/paulholden/moodle into MOODLE_400_STABLE 2022-05-02 16:30:06 +08:00
Jake Dallimore 4cbd6a0c6a MDL-74576 enrol_lti: allow unsupported scopes in ags_info
Instead of hard blocking launches with extension scopes, just ignore
the unsupported scopes and continue.
2022-05-02 15:39:20 +08:00
Paul Holden 203a55f57a MDL-74128 reportbuilder: Behat helper for navigating to view page. 2022-04-29 17:18:06 +01:00
Paul Holden 5b05a9378c MDL-74612 cohort: use data generator for populating cohort members. 2022-04-29 16:23:32 +01:00
Ilya Tregubov 81e5d425c4 weekly release 4.0+ 2022-04-29 07:55:31 +06:00
Ilya Tregubov 6362d7b51c Merge branch 'install_400_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_400_STABLE 2022-04-29 07:55:27 +06:00
Mikel Martín 51d3df8d30 MDL-74598 reportbuilder: Fix action menus alignment to the right 2022-04-28 16:46:00 +02:00
Paul Holden a60a6d41a3 MDL-74567 repository_flickr: replace user-agent value for API calls.
Flickr doesn't like user agent that looks like a bot. Effectively the
same reasoning/fix as b8ee31a0.
2022-04-28 14:45:37 +01:00
Mihail Geshoski d6ba7724ae MDL-74450 admin: Use the context from admin_settingpage as page context
The defined context in admin_settingpage does not always relate to the
system context. One example is the 'frontpagesettings' admin setting
page which specifies front page as it's default context. Therefore, the
page context in admin/settings.php should be consisent with the defined
context in the related admin_settingpage object to make sure that the
expected navigation menus are being displayed and properly highlighted.
Additionally, the code in admin/settings.php related to the breadcrumb
structure specific to 'frontpagesettings' has been removed as it is no
longer relevant.
2022-04-28 13:23:57 +08:00
Jun Pataleta 4f1be71581 Merge branch 'MDL-74478-400' of https://github.com/mihailges/moodle into MOODLE_400_STABLE 2022-04-28 12:54:26 +08:00
Peter Dias 58ddfe84c2 MDL-74478 badges: Check completion_fail when awarding badge
Bring the award_criteria_activity checks in line with the observer checks
2022-04-28 12:16:19 +08:00
Jun Pataleta dd156a4160 Merge branch 'MDL-74461-400' of https://github.com/HuongNV13/moodle into MOODLE_400_STABLE 2022-04-28 11:39:59 +08:00
Huong Nguyen 3bc8fa20bf MDL-74461 Navigation: Add section anchor to activity breadcrumb 2022-04-28 10:05:17 +07:00
Ilya Tregubov 954ae33aa9 Merge branch 'MOODLE_400-MDL-74427-v4' of https://github.com/golenkovm/moodle into MOODLE_400_STABLE 2022-04-28 08:10:29 +06:00
Jun Pataleta 24dbcc0008 Merge branch 'MDL-74537-400-enfix' of https://github.com/vmdef/moodle into MOODLE_400_STABLE 2022-04-27 15:47:30 +06:00
Ilya Tregubov ebb96c2850 Merge branch 'MDL-74510_400' of https://github.com/stronk7/moodle into MOODLE_400_STABLE 2022-04-27 15:47:30 +06:00
Paul Holden 870c5ffbde MDL-74479 reportbuilder: account for custom field context in output. 2022-04-27 15:47:29 +06:00
Eloy Lafuente (stronk7) 049e27a805 MDL-74320 lesson: Remove tested elsewhere parts of the scenario
The scenario has not only grades related completion conditions
but also timing conditions or reach the end conditions that
shouldn't be tested by a grades feature.

More yet, those completion conditions are already tested by
other feature files, namely:
- reach the end: completion_condition_end_reached.feature
- visit and timing: lesson_activity_completion.feature

So we have removed all those parts that are being tested
by other features.

Specially the visit and timing ones, because they lead to
random failures in slow environments and they have been
fixed in the lesson_activity_completion.feature scenarios
by MDL-72698.
2022-04-27 15:47:29 +06:00
Laurent David 6c7fa91d59 MDL-74514 mod_bigbluebuttonbn: BBB server is polled too often
* Fix issue with the roomupdater that should not force the updatecache flag
for get_meeting_info API
* Fix issue when meeting is not started (and does not exist) and bigbluebutton_proxy::get_meeting_info
throws an exception, skipping the cache and then polling the server every second.
2022-04-27 15:47:29 +06:00
Claude Vervoort 7c84b92be3 MDL-74481 mod_lti: fix role lookup for LTI 1.3 2022-04-27 15:47:29 +06:00
Mikhail Golenkov bb36c3a1bb MDL-74427 question: Re-use get_real_question_ids_in_category() 2022-04-27 12:35:08 +10:00
Mikhail Golenkov 990bfee024 MDL-74427 question: Implement get_real_question_ids_in_category() 2022-04-27 12:34:55 +10:00
David Matamoros 17584f9c92 MDL-74317 blocks: Blocks can be added to profile pages
- Fixed method user_can_addto to take into account that
blocks can also be added to user profile pages
2022-04-26 14:34:45 +02:00
David Matamoros f551072f9e MDL-74317 output: Remove capability checks in edit mode WS
- Removed validation on the change edit mode WS. Permission should
be handled where the edit button is rendered in order to show or not
the button.
2022-04-26 14:33:34 +02:00
Helen Foster 3447ab640c MDL-74537 lang: Import fixed English strings (en_fix) 2022-04-26 11:24:27 +01:00
Paul Holden 3dbad11471 MDL-74563 reportbuilder: cast operator parameter in number filter.
Add missing form labels, too.
2022-04-25 21:44:48 +01:00
Paul Holden 43b4b87fbf MDL-74436 calendar: validate course/category field during import. 2022-04-25 18:02:00 +01:00
Paul Holden 7d33385a1d MDL-74477 reportbuilder: sortable custom user profile field columns. 2022-04-25 13:40:12 +06:00
AMOS bot 657617ac21 Automatically generated installer lang files 2022-04-23 00:07:39 +00:00
Eloy Lafuente (stronk7) 205e4152cc MDL-74510 upgrade: add the 4.0.0 separation line to all upgrade scripts 2022-04-22 18:45:16 +02:00
Eloy Lafuente (stronk7) e36fb751e5 weekly release 4.0+ 2022-04-22 18:03:47 +02:00
Jake Dallimore a0c1acd7fd MDL-74546 enrol_lti: add missing d-flex to table to fix safari layout 2022-04-22 10:19:08 +08:00
Ilya Tregubov 2184ea5b64 Merge branch 'MDL-74394_400' of https://github.com/aolley/moodle into MOODLE_400_STABLE 2022-04-22 08:01:09 +06:00
Víctor Déniz c81b3dc384 Merge branch 'MDL-74312-400' of https://github.com/snake/moodle into MOODLE_400_STABLE 2022-04-21 16:39:51 +01:00
Adam Olley ca6a4b1953 MDL-74394 mnet: Better check for openssl_get_publickey() failure
In PHP8, this func now returns an object of type OpenSSLAsymmetricKey, not a
resource.

Reference:
https://www.php.net/manual/en/function.openssl-pkey-get-public.php
2022-04-21 22:08:20 +09:30
Jake Dallimore 20cefd70e2 MDL-74312 enrol_lti: fix copytoclipboard trigger element
This should be a button given it's an action, and making it a button
means it can be activated using either the space or enter keys, instead
of just enter.
2022-04-20 09:49:35 +08:00
Sara Arjona bc87d55a1f Merge branch 'MDL-74512_400' of https://github.com/stronk7/moodle into MOODLE_400_STABLE 2022-04-19 12:55:29 +02:00
Andrew Nicols 51c376cfca MDL-74511 core: Add phpcs config generator 2022-04-19 11:15:27 +08:00
Eloy Lafuente (stronk7) e0034b0566 MDL-74512 behat: Skip tests requiring lang install/upgrade
By adding the step:

Given remote langimport tests are enabled

We make the execution of scenarios requiring to install /
upgrade lang packs optional, based in the existence of the constant:

TOOL_LANGIMPORT_REMOTE_TESTS

That way, when we are skipping language upgrades with the CFG var:

$CFG->skiplangupgrade = true

We can decide not not execute those tests.

With other words, putting something like this in the config.php
file we will be controlling when to execute or no the behat tests:

    // Disable lang updates on upgrade.
    $CFG->skiplangupgrade = true;
    if (empty($CFG->skiplangupgrade)) {
        // Define it only if we aren't skipping lang upgrades.
        define('TOOL_LANGIMPORT_REMOTE_TESTS', true);
    }
2022-04-17 20:35:02 +02:00
248 changed files with 2238 additions and 442 deletions
+1
View File
@@ -54,3 +54,4 @@ moodle-plugin-ci.phar
/admin/tool/componentlibrary/docs
/admin/tool/componentlibrary/hugo/site/data/my-index.json
.hugo_build.lock
.phpcs.xml
+39
View File
@@ -21,6 +21,43 @@
*/
module.exports = grunt => {
/**
* Generate the PHPCS configuration.
*
* @param {Object} thirdPartyPaths
*/
const phpcsIgnore = (thirdPartyPaths) => {
const {toXML} = require('jstoxml');
const config = {
_name: 'ruleset',
_attrs: {
name: "MoodleCore",
},
_content: [
{
rule: {
_attrs: {
ref: './.phpcs.xml.dist',
},
},
},
],
};
thirdPartyPaths.forEach(library => {
config._content.push({
'exclude-pattern': library,
});
});
grunt.file.write('.phpcs.xml', toXML(config, {
header: true,
indent: ' ',
}) + "\n");
};
/**
* Generate ignore files (utilising thirdpartylibs.xml data)
*/
@@ -53,6 +90,8 @@ module.exports = grunt => {
'admin/tool/componentlibrary/hugo/dist/css/docs.css',
].concat(thirdPartyPaths);
grunt.file.write('.stylelintignore', stylelintIgnores.join('\n') + '\n');
phpcsIgnore(thirdPartyPaths);
};
grunt.registerTask('ignorefiles', 'Generate ignore files for linters', handler);
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="MoodleCore">
<rule ref="moodle"/>
</ruleset>
+15 -4
View File
@@ -38,11 +38,22 @@
}
if (!empty($show) and confirm_sesskey()) {
$class = \core_plugin_manager::resolve_plugininfo_class('mod');
$class::enable_plugin($show, true);
$canenablemodule = true;
$modulename = $show;
admin_get_root(true, false); // settings not required - only pages
redirect(new moodle_url('/admin/modules.php'));
// Invoking a callback function that enables plugins to force additional actions (e.g. displaying notifications,
// modals, etc.) and also specify through its returned value (bool) whether the process of enabling the plugin
// should continue after these actions or not.
if (component_callback_exists("mod_{$modulename}", 'pre_enable_plugin_actions')) {
$canenablemodule = component_callback("mod_{$modulename}", 'pre_enable_plugin_actions');
}
if ($canenablemodule) {
$class = \core_plugin_manager::resolve_plugininfo_class('mod');
$class::enable_plugin($show, true);
admin_get_root(true, false); // Settings not required - only pages.
redirect(new moodle_url('/admin/modules.php'));
}
}
echo $OUTPUT->header();
+10 -9
View File
@@ -33,8 +33,16 @@ if (!($settingspage->check_access())) {
die;
}
$hassiteconfig = has_capability('moodle/site:config', $PAGE->context);
if ($hassiteconfig) {
// If the context in the admin_settingpage object is explicitly defined and it is not system, reset the current
// page context and use that one instead. This ensures that the proper navigation is displayed and highlighted.
if ($settingspage->context && !$settingspage->context instanceof \context_system) {
$PAGE->set_context($settingspage->context);
}
$hassiteconfig = has_capability('moodle/site:config', context_system::instance());
// Display the admin search input element in the page header if the user has the capability to change the site
// configuration and the current page context is system.
if ($hassiteconfig && $PAGE->context instanceof \context_system) {
$PAGE->add_header_action($OUTPUT->render_from_template('core_admin/header_search_input', [
'action' => new moodle_url('/admin/search.php'),
]));
@@ -125,13 +133,6 @@ if (empty($SITE->fullname)) {
$PAGE->set_title("$SITE->shortname: " . implode(": ",$visiblepathtosection));
$PAGE->set_heading($SITE->fullname);
if ($section === 'frontpagesettings') {
$frontpagenode = $PAGE->settingsnav->find('frontpage', navigation_node::TYPE_SETTING);
$frontpagenode->make_active();
$PAGE->navbar->add(get_string('frontpage', 'admin'),
new moodle_url('/admin/category.php', ['category' => 'frontpage']));
$PAGE->navbar->add(get_string('frontpagesettings', 'admin'), $PAGE->url);
}
echo $OUTPUT->header();
if ($errormsg !== '') {
+9
View File
@@ -88,6 +88,15 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
$downloadcontentsitedefault->add_dependent_on('downloadcoursecontentallowed');
$temp->add($downloadcontentsitedefault);
$temp->add(
new admin_setting_configtext(
'moodlecourse/participantsperpage',
new lang_string('participants:perpage', 'course'),
new lang_string('participants:perpage_help', 'course'),
20
)
);
// Course format.
$temp->add(new admin_setting_heading('courseformathdr', new lang_string('type_format', 'plugin'), ''));
+4 -2
View File
@@ -18,6 +18,10 @@ Feature: An administrator can filter user accounts by role, cohort and other pro
And the following "cohorts" exist:
| name | idnumber |
| Cohort 1 | CH1 |
And the following "cohort members" exist:
| user | cohort |
| user2 | CH1 |
| user3 | CH1 |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
@@ -27,8 +31,6 @@ Feature: An administrator can filter user accounts by role, cohort and other pro
| user2 | C1 | student |
| user3 | C1 | student |
And I log in as "admin"
And I add "User Two (two@example.com)" user to "CH1" cohort members
And I add "User Three (three@example.com)" user to "CH1" cohort members
And I navigate to "Users > Accounts > Browse list of users" in site administration
Scenario: Filter user accounts by role and cohort
@@ -248,7 +248,7 @@ $string['printable:toolname'] = 'Summary report';
$string['printable:toolshortname'] = 'Summary report';
$string['printable:downloadpdf'] = 'Download PDF';
$string['printable:printreport'] = 'Printable report';
$string['error:nocoursespecified'] = 'This summary report requires a valid course id. Please access the accessibility toolkit from within a course, via the actions menu, which will then supply this required course id.';
$string['error:nocoursespecified'] = 'The summary report requires a valid course ID. Please access the accessibility toolkit from within a course.';
$string['pdf:filename'] = 'Brickfield_Summaryreport_CourseID-{$a}';
// Advanced page.
+3
View File
@@ -44,5 +44,8 @@ function xmldb_tool_cohortroles_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -41,5 +41,8 @@ function xmldb_tool_customlang_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -78,5 +78,8 @@ function xmldb_tool_dataprivacy_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2020061501, 'tool', 'dataprivacy');
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -45,5 +45,8 @@ function xmldb_tool_log_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
@@ -46,5 +46,8 @@ function xmldb_logstore_database_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
@@ -46,5 +46,8 @@ function xmldb_logstore_standard_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -51,5 +51,8 @@ function xmldb_tool_mobile_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -45,5 +45,8 @@ function xmldb_tool_monitor_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -164,5 +164,8 @@ function xmldb_tool_moodlenet_upgrade(int $oldversion) {
upgrade_plugin_savepoint(true, 2022021601, 'tool', 'moodlenet');
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
@@ -58,9 +58,8 @@ $string['mnetprofiledesc'] = '<p>Enter your MoodleNet profile details here to be
$string['moodlenetsettings'] = 'MoodleNet settings';
$string['moodlenetnotenabled'] = 'The MoodleNet integration must be enabled in Site administration / MoodleNet before resource imports can be processed.';
$string['notification'] = 'You are about to import the content "{$a->name} ({$a->type})" into your site. Select the course in which it should be added, or <a href="{$a->cancellink}">cancel</a>.';
$string['removedmnetprofilenotification'] = '<p>Due to some recent changes on the MoodleNet platform, users that have previously saved their MoodleNet profile on the site can no longer use this data to authenticate on the MoodleNet platform. The related data has now been removed as it is no longer useful.</p>
<p>The users will need to reset this information on the site by linking their MoodleNet profile ID which can be found on their MoodleNet profile.</p>';
$string['removedmnetprofilenotification_subject'] = 'Linked MoodleNet profiles removed.';
$string['removedmnetprofilenotification'] = 'Due to recent changes on the MoodleNet platform, any users who previously saved their MoodleNet profile ID on the site will need to enter a MoodleNet profile ID in the new format in order to authenticate on the MoodleNet platform.';
$string['removedmnetprofilenotification_subject'] = 'MoodleNet profile ID format change';
$string['searchcourses'] = "Search courses";
$string['selectpagetitle'] = 'Select page';
$string['pluginname'] = 'MoodleNet';
+3
View File
@@ -46,5 +46,8 @@ function xmldb_tool_policy_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+108 -38
View File
@@ -44,6 +44,12 @@ class tool_uploadcourse_course {
/** Outcome of the process: deleting the course */
const DO_DELETE = 3;
/** @var array assignable roles. */
protected $assignableroles = [];
/** @var array Roles context levels. */
protected $contextlevels = [];
/** @var array final import data. */
protected $data = array();
@@ -794,16 +800,15 @@ class tool_uploadcourse_course {
// Get enrolment data. Where the course already exists, we can also perform validation.
$this->enrolmentdata = tool_uploadcourse_helper::get_enrolment_data($this->rawdata);
if ($exists) {
$errors = $this->validate_enrolment_data($coursedata['id'], $this->enrolmentdata);
$courseid = $coursedata['id'] ?? 0;
$errors = $this->validate_enrolment_data($courseid, $this->enrolmentdata);
if (!empty($errors)) {
foreach ($errors as $key => $message) {
$this->error($key, $message);
}
return false;
if (!empty($errors)) {
foreach ($errors as $key => $message) {
$this->error($key, $message);
}
return false;
}
if (isset($this->rawdata['tags']) && strval($this->rawdata['tags']) !== '') {
@@ -913,6 +918,8 @@ class tool_uploadcourse_course {
* @return lang_string[] Errors keyed on error code
*/
protected function validate_enrolment_data(int $courseid, array $enrolmentdata): array {
global $DB;
// Nothing to validate.
if (empty($enrolmentdata)) {
return [];
@@ -924,46 +931,67 @@ class tool_uploadcourse_course {
$instances = enrol_get_instances($courseid, false);
foreach ($enrolmentdata as $method => $options) {
$plugin = $enrolmentplugins[$method];
// Find matching instances by enrolment method.
$methodinstances = array_filter($instances, static function(stdClass $instance) use ($method) {
return (strcmp($instance->enrol, $method) == 0);
});
if (isset($options['role'])) {
$role = $options['role'];
if ($courseid) {
if (!$this->validate_role_context($courseid, $role)) {
$errors['contextrolenotallowed'] = new lang_string('contextrolenotallowed', 'core_role', $role);
if (!empty($options['delete'])) {
// Ensure user is able to delete the instances.
foreach ($methodinstances as $methodinstance) {
if (!$plugin->can_delete_instance($methodinstance)) {
$errors['errorcannotdeleteenrolment'] = new lang_string('errorcannotdeleteenrolment', 'tool_uploadcourse',
$plugin->get_instance_name($methodinstance));
break;
}
} else {
// We can at least check that context level is correct while actual context not exist.
$roleid = $DB->get_field('role', 'id', ['shortname' => $role], MUST_EXIST);
if (!$this->validate_role_context_level($roleid)) {
$errors['contextrolenotallowed'] = new lang_string('contextrolenotallowed', 'core_role', $role);
break;
}
}
} else if (!empty($options['disable'])) {
// Ensure user is able to toggle instance statuses.
foreach ($methodinstances as $methodinstance) {
if (!$plugin->can_hide_show_instance($methodinstance)) {
$errors['errorcannotdisableenrolment'] =
new lang_string('errorcannotdisableenrolment', 'tool_uploadcourse',
}
if ($courseid) {
$plugin = $enrolmentplugins[$method];
// Find matching instances by enrolment method.
$methodinstances = array_filter($instances, static function (stdClass $instance) use ($method) {
return (strcmp($instance->enrol, $method) == 0);
});
if (!empty($options['delete'])) {
// Ensure user is able to delete the instances.
foreach ($methodinstances as $methodinstance) {
if (!$plugin->can_delete_instance($methodinstance)) {
$errors['errorcannotdeleteenrolment'] = new lang_string('errorcannotdeleteenrolment',
'tool_uploadcourse', $plugin->get_instance_name($methodinstance));
break;
}
}
} else if (!empty($options['disable'])) {
// Ensure user is able to toggle instance statuses.
foreach ($methodinstances as $methodinstance) {
if (!$plugin->can_hide_show_instance($methodinstance)) {
$errors['errorcannotdisableenrolment'] =
new lang_string('errorcannotdisableenrolment', 'tool_uploadcourse',
$plugin->get_instance_name($methodinstance));
break;
}
}
} else {
// Ensure user is able to create/update instance.
$methodinstance = empty($methodinstances) ? null : reset($methodinstances);
if ((empty($methodinstance) && !$plugin->can_add_instance($courseid)) ||
(!empty($methodinstance) && !$plugin->can_edit_instance($methodinstance))) {
$errors['errorcannotcreateorupdateenrolment'] =
new lang_string('errorcannotcreateorupdateenrolment', 'tool_uploadcourse',
$plugin->get_instance_name($methodinstance));
break;
}
}
} else {
// Ensure user is able to create/update instance.
$methodinstance = empty($methodinstances) ? null : reset($methodinstances);
if ((empty($methodinstance) && !$plugin->can_add_instance($courseid)) ||
(!empty($methodinstance) && !$plugin->can_edit_instance($methodinstance))) {
$errors['errorcannotcreateorupdateenrolment'] =
new lang_string('errorcannotcreateorupdateenrolment', 'tool_uploadcourse',
$plugin->get_instance_name($methodinstance));
break;
}
}
}
@@ -1079,8 +1107,15 @@ class tool_uploadcourse_course {
$instance->enrolenddate = $instance->enrolstartdate;
}
// Sort out the given role. This does not filter the roles allowed in the course.
// Sort out the given role.
if (isset($method['role'])) {
$role = $method['role'];
if (!$this->validate_role_context($course->id, $role)) {
$this->error('contextrolenotallowed',
new lang_string('contextrolenotallowed', 'core_role', $role));
break;
}
$roleids = tool_uploadcourse_helper::get_role_ids();
if (isset($roleids[$method['role']])) {
$instance->roleid = $roleids[$method['role']];
@@ -1093,6 +1128,41 @@ class tool_uploadcourse_course {
}
}
/**
* Check if role is allowed in course context
*
* @param int $courseid course context.
* @param string $role Role.
* @return bool
*/
protected function validate_role_context(int $courseid, string $role) : bool {
if (empty($this->assignableroles[$courseid])) {
$coursecontext = \context_course::instance($courseid);
$this->assignableroles[$courseid] = get_assignable_roles($coursecontext, ROLENAME_SHORT);
}
if (!in_array($role, $this->assignableroles[$courseid])) {
return false;
}
return true;
}
/**
* Check if role is allowed at this context level.
*
* @param int $roleid Role ID.
* @return bool
*/
protected function validate_role_context_level(int $roleid) : bool {
if (empty($this->contextlevels[$roleid])) {
$this->contextlevels[$roleid] = get_role_contextlevels($roleid);
}
if (!in_array(CONTEXT_COURSE, $this->contextlevels[$roleid])) {
return false;
}
return true;
}
/**
* Reset the current course.
*
@@ -325,6 +325,11 @@ class tool_uploadcourse_helper {
continue;
}
$rolenames['role_' . $rolesids[$matches[1]]] = $value;
} else if (preg_match('/^(.+)?_role$/', $field, $matches)) {
if (!isset($rolesids[$value])) {
$invalidroles[] = $value;
break;
}
}
}
@@ -219,6 +219,10 @@ class tool_uploadcourse_processor {
$data = array_merge($data, $course->get_data(), array('id' => $course->get_id()));
$tracker->output($this->linenb, true, $status, $data);
if ($course->has_errors()) {
$errors++;
$tracker->output($this->linenb, false, $course->get_errors(), $data);
}
} else {
$errors++;
$tracker->output($this->linenb, false, $course->get_errors(), $data);
@@ -105,3 +105,28 @@ Feature: An admin can create courses using a CSV file
And I should see "Field 3: b"
And I should see "Field 4: Hello"
And I should see "Field 5: Some text"
@javascript
Scenario: Validation of role for uploaded courses
Given I navigate to "Users > Permissions > Define roles" in site administration
And I click on "Add a new role" "button"
And I click on "Continue" "button"
And I set the following fields to these values:
| Short name | notallowed |
| Custom full name | notallowed |
| contextlevel80 | 1 |
And I click on "Create this role" "button"
And I navigate to "Courses > Upload courses" in site administration
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_role.csv" file to "File" filemanager
And I click on "Preview" "button"
And I should see "Invalid role names: notexist"
And I should see "Role notallowed not allowed in this context."
When I click on "Upload courses" "button"
And I should see "Course created"
And I should see "Courses total: 3"
And I should see "Courses created: 1"
And I should see "Courses errors: 2"
And I should see "Invalid role names: notexist"
And I should see "Role notallowed not allowed in this context."
And I am on site homepage
And I should see "coursez"
@@ -1561,6 +1561,94 @@ class course_test extends \advanced_testcase {
$this->assertArrayHasKey('cannotrenameshortnamealreadyinuse', $co->get_errors());
}
/**
* Test when role doesn't exist.
*
* @covers \tool_uploadcourse_course::prepare
*/
public function test_role_not_exist() {
$this->resetAfterTest();
$this->setAdminUser();
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
$upload = new tool_uploadcourse_course($mode, $updatemode, [
'category' => 1,
'fullname' => 'Testing',
'shortname' => 'T101',
'enrolment_1' => 'manual',
'enrolment_1_role' => 'notexist'
]);
$this->assertFalse($upload->prepare());
$this->assertArrayHasKey('invalidroles', $upload->get_errors());
}
/**
* Test when role not allowed in course context.
*
* @covers \tool_uploadcourse_course::proceed
*/
public function test_role_not_allowed() {
$this->resetAfterTest();
$this->setAdminUser();
$roleid = create_role('New student role', 'student2', 'New student description', 'student');
set_role_contextlevels($roleid, [CONTEXT_BLOCK]);
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
$upload = new tool_uploadcourse_course($mode, $updatemode, [
'category' => 1,
'fullname' => 'Testing',
'shortname' => 'T101',
'enrolment_1' => 'manual',
'enrolment_1_role' => 'student2'
]);
$this->assertFalse($upload->prepare());
$this->assertArrayHasKey('contextrolenotallowed', $upload->get_errors());
}
/**
* Test when role is allowed.
*
* @covers \tool_uploadcourse_course::proceed
*/
public function test_role_allowed() {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
$updatemode = tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS;
$course = $this->getDataGenerator()->create_course([
'shortname' => 'c1',
]);
$instances = enrol_get_instances($course->id, true);
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
$teacherrole = $DB->get_record('role', ['shortname' => 'teacher']);
$instance = reset($instances);
$this->assertEquals($studentrole->id, $instance->roleid);
$upload = new tool_uploadcourse_course($mode, $updatemode, [
'shortname' => 'c1',
'enrolment_1' => 'manual',
'enrolment_1_role' => 'teacher'
]);
$this->assertTrue($upload->prepare());
$upload->proceed();
$instances = enrol_get_instances($course->id, true);
$instance = reset($instances);
$this->assertEquals($teacherrole->id, $instance->roleid);
}
/**
* Get custom field plugin generator
*
@@ -0,0 +1,4 @@
shortname,fullname,category,enrolment_1,enrolment_1_role
CX,coursex,1,manual,notexist
CY,coursey,1,manual,notallowed
CZ,coursez,1,manual,student
1 shortname fullname category enrolment_1 enrolment_1_role
2 CX coursex 1 manual notexist
3 CY coursey 1 manual notallowed
4 CZ coursez 1 manual student
+3
View File
@@ -129,5 +129,8 @@ function xmldb_tool_usertours_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2022040602, 'tool', 'usertours');
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+7
View File
@@ -1,5 +1,12 @@
This files describes API changes in /admin/*.
=== 4.0.1 ===
* A new callback xxx_pre_enable_plugin_actions has been added in admin/modules.php. Plugins can use this callback to
force additional actions (e.g. displaying notifications, modals, etc.) before enabling the plugin. The expected
return value (bool) from the plugin callback method specifies whether the process of enabling the plugin should
continue after the added actions or not.
=== 3.11 ===
* New admin setting admin_setting_encryptedpassword allows passwords in admin settings to be
+3
View File
@@ -52,5 +52,8 @@ function xmldb_auth_cas_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2021052501, 'auth', 'cas');
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -44,5 +44,8 @@ function xmldb_auth_db_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -44,5 +44,8 @@ function xmldb_auth_email_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -52,5 +52,8 @@ function xmldb_auth_ldap_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2021052501, 'auth', 'ldap');
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -85,5 +85,8 @@ function xmldb_auth_lti_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2022030900, 'auth', 'lti');
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -44,5 +44,8 @@ function xmldb_auth_manual_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -44,5 +44,8 @@ function xmldb_auth_mnet_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -44,5 +44,8 @@ function xmldb_auth_none_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -81,5 +81,8 @@ function xmldb_auth_oauth2_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -66,5 +66,8 @@ function xmldb_auth_shibboleth_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2021052501, 'auth', 'shibboleth');
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+2 -1
View File
@@ -17,7 +17,8 @@ Feature: Test if the login form provides the correct feedback
Then I should see "Invalid login, please try again"
Scenario: Test login language selector
Given I log in as "admin"
Given remote langimport tests are enabled
And I log in as "admin"
And I navigate to "Language > Language packs" in site administration
And I set the field "Available language packs" to "nl,es"
And I press "Install selected language pack(s)"
+12 -6
View File
@@ -475,6 +475,11 @@ class backup_course_structure_step extends backup_structure_step {
'shortname', 'type', 'value', 'valueformat'
));
$courseformatoptions = new backup_nested_element('courseformatoptions');
$courseformatoption = new backup_nested_element('courseformatoption', [], [
'courseid', 'format', 'sectionid', 'name', 'value'
]);
// attach format plugin structure to $course element, only one allowed
$this->add_plugin_structure('format', $course, false);
@@ -512,17 +517,14 @@ class backup_course_structure_step extends backup_structure_step {
$course->add_child($customfields);
$customfields->add_child($customfield);
$course->add_child($courseformatoptions);
$courseformatoptions->add_child($courseformatoption);
// Set the sources
$courserec = $DB->get_record('course', array('id' => $this->task->get_courseid()));
$courserec->contextid = $this->task->get_contextid();
$formatoptions = course_get_format($courserec)->get_format_options();
$course->add_final_elements(array_keys($formatoptions));
foreach ($formatoptions as $key => $value) {
$courserec->$key = $value;
}
// Add 'numsections' in order to be able to restore in previous versions of Moodle.
// Even though Moodle does not officially support restore into older verions of Moodle from the
// version where backup was made, without 'numsections' restoring will go very wrong.
@@ -544,6 +546,10 @@ class backup_course_structure_step extends backup_structure_step {
backup_helper::is_sqlparam('course'),
backup::VAR_PARENTID));
$courseformatoption->set_source_sql('SELECT id, format, sectionid, name, value
FROM {course_format_options}
WHERE courseid = ?', [ backup::VAR_PARENTID ]);
$handler = core_course\customfield\course_handler::create();
$fieldsforbackup = $handler->get_instance_data_for_backup($this->task->get_courseid());
$customfield->set_source_array($fieldsforbackup);
+23 -4
View File
@@ -1806,7 +1806,8 @@ class restore_course_structure_step extends restore_structure_step {
$category = new restore_path_element('category', '/course/category');
$tag = new restore_path_element('tag', '/course/tags/tag');
$customfield = new restore_path_element('customfield', '/course/customfields/customfield');
$allowed_module = new restore_path_element('allowed_module', '/course/allowed_modules/module');
$courseformatoptions = new restore_path_element('course_format_option', '/course/courseformatoptions/courseformatoption');
$allowedmodule = new restore_path_element('allowed_module', '/course/allowed_modules/module');
// Apply for 'format' plugins optional paths at course level
$this->add_plugin_structure('format', $course);
@@ -1829,7 +1830,7 @@ class restore_course_structure_step extends restore_structure_step {
// Apply for admin tool plugins optional paths at course level.
$this->add_plugin_structure('tool', $course);
return array($course, $category, $tag, $customfield, $allowed_module);
return array($course, $category, $tag, $customfield, $allowedmodule, $courseformatoptions);
}
/**
@@ -1951,8 +1952,6 @@ class restore_course_structure_step extends restore_structure_step {
// Course record ready, update it
$DB->update_record('course', $data);
course_get_format($data)->update_course_format_options($data);
// Role name aliases
restore_dbops::set_course_role_names($this->get_restoreid(), $this->get_courseid());
}
@@ -1980,6 +1979,26 @@ class restore_course_structure_step extends restore_structure_step {
$handler->restore_instance_data_from_backup($this->task, $data);
}
/**
* Processes a course format option.
*
* @param array $data The record being restored.
* @throws base_step_exception
* @throws dml_exception
*/
public function process_course_format_option(array $data) : void {
global $DB;
$courseid = $this->get_courseid();
$record = $DB->get_record('course_format_options', [ 'courseid' => $courseid, 'name' => $data['name'] ], 'id');
if ($record !== false) {
$DB->update_record('course_format_options', (object) [ 'id' => $record->id, 'value' => $data['value'] ]);
} else {
$data['courseid'] = $courseid;
$DB->insert_record('course_format_options', (object) $data);
}
}
public function process_allowed_module($data) {
$data = (object)$data;
+6 -1
View File
@@ -248,8 +248,11 @@ class award_criteria_activity extends award_criteria {
if (!empty($moduledata)) {
$extraon = implode(' OR ', $moduledata);
$join = " JOIN {course_modules_completion} cmc ON cmc.userid = u.id AND
( cmc.completionstate = :completionpass OR cmc.completionstate = :completioncomplete ) AND ({$extraon})";
( cmc.completionstate = :completionfail OR
cmc.completionstate = :completionpass OR
cmc.completionstate = :completioncomplete ) AND ({$extraon})";
$params["completionpass"] = COMPLETION_COMPLETE_PASS;
$params["completionfail"] = COMPLETION_COMPLETE_FAIL;
$params["completioncomplete"] = COMPLETION_COMPLETE;
}
return array($join, $where, $params);
@@ -259,10 +262,12 @@ class award_criteria_activity extends award_criteria {
cmc{$param['module']}.userid = u.id AND
cmc{$param['module']}.coursemoduleid = :completedmodule{$param['module']} AND
( cmc{$param['module']}.completionstate = :completionpass{$param['module']} OR
cmc{$param['module']}.completionstate = :completionfail{$param['module']} OR
cmc{$param['module']}.completionstate = :completioncomplete{$param['module']} )";
$where .= " AND cmc{$param['module']}.coursemoduleid IS NOT NULL ";
$params["completedmodule{$param['module']}"] = $param['module'];
$params["completionpass{$param['module']}"] = COMPLETION_COMPLETE_PASS;
$params["completionfail{$param['module']}"] = COMPLETION_COMPLETE_FAIL;
$params["completioncomplete{$param['module']}"] = COMPLETION_COMPLETE;
}
return array($join, $where, $params);
+2 -2
View File
@@ -202,8 +202,8 @@ class award_criteria_profile extends award_criteria {
$join .= " LEFT JOIN {user_info_data} uid{$idx} ON uid{$idx}.userid = u.id AND uid{$idx}.fieldid = :fieldid{$idx} ";
$sqlparams["fieldid{$idx}"] = $param['field'];
$whereparts[] = "uid{$idx}.id IS NOT NULL";
} else {
// This is a field from {user} table.
} else if (in_array($param['field'], $this->allowed_default_fields)) {
// This is a valid field from {user} table.
if ($param['field'] == 'picture') {
// The picture field is numeric and requires special handling.
$whereparts[] = "u.{$param['field']} != 0";
+58 -4
View File
@@ -8,6 +8,7 @@ Feature: Award badges based on activity completion
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 1 | student2@example.com |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | category | enablecompletion |
@@ -16,6 +17,7 @@ Feature: Award badges based on activity completion
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following config values are set as admin:
| grade_item_advanced | hiddenuntil |
And the following "question categories" exist:
@@ -33,6 +35,9 @@ Feature: Award badges based on activity completion
And user "student1" has attempted "Test quiz name" with responses:
| slot | response |
| 1 | False |
And user "student2" has attempted "Test quiz name" with responses:
| slot | response |
| 1 | False |
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I change window size to "large"
@@ -42,6 +47,13 @@ Feature: Award badges based on activity completion
| Description | Course badge description |
And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
And I press "Create badge"
Scenario: Student earns a badge using activity completion, but does not get passing grade
Given I am on the "Course 1" course page logged in as teacher1
And I navigate to "Badges" in current page administration
And I press "Manage badges"
And I follow "Course Badge"
And I select "Criteria" from the "jump" singleselect
And I set the field "type" to "Activity completion"
And I set the field "Quiz - Test quiz name" to "1"
And I press "Save"
@@ -49,10 +61,7 @@ Feature: Award badges based on activity completion
And I press "Continue"
And I should see "Recipients (0)"
And I log out
Scenario: Student earns a badge using activity completion, but does not get passing grade
Given I log in as "student1"
And I am on "Course 1" course homepage
And I am on the "Course 1" course page logged in as student1
And the "Receive a grade" completion condition of "Test quiz name" is displayed as "done"
And the "Receive a passing grade" completion condition of "Test quiz name" is displayed as "failed"
And the "Receive a pass grade or complete all available attempts" completion condition of "Test quiz name" is displayed as "todo"
@@ -68,3 +77,48 @@ Feature: Award badges based on activity completion
And I navigate to "Badges > Manage badges" in current page administration
And I follow "Course Badge"
Then I should see "Recipients (1)"
Scenario Outline: Previously graded pass/fail students should earn a badge after enabling a badge
Given I am on the "Course 1" course page logged in as teacher1
And I navigate to "Badges" in current page administration
And I press "Manage badges"
And I follow "Course Badge"
And I select "Criteria" from the "jump" singleselect
And I set the field "type" to "Activity completion"
And I click on "Expand all" "link"
And I set the field "Quiz - Test quiz name" to "1"
And I set the field "<aggregationcriteria>" to "1"
And I press "Save"
# Fail grade with student2
And I am on the "Course 1" course page logged in as student2
And I am on the "Test quiz name" "quiz activity" page
And I press "Re-attempt quiz"
And I set the field "False" to "1"
And I press "Finish attempt ..."
And I press "Submit all and finish"
And I click on "Submit all and finish" "button" in the "Confirmation" "dialogue"
And I log out
# Pass grade with student1
And I am on the "Course 1" course page logged in as student1
And I am on the "Test quiz name" "quiz activity" page
And I press "Re-attempt quiz"
And I set the field "False" to "0"
And I press "Finish attempt ..."
And I press "Submit all and finish"
And I click on "Submit all and finish" "button" in the "Confirmation" "dialogue"
And I log out
# Enable badge access once all students have completed an activity.
And I am on the "Course 1" course page logged in as teacher1
And I navigate to "Badges > Manage badges" in current page administration
And I follow "Course Badge"
When I press "Enable access"
And I press "Continue"
Then I should see "Recipients (2)"
Examples:
| aggregationcriteria |
| Any of the selected activities is complete |
| All of the selected activities are complete |
+8 -6
View File
@@ -405,6 +405,12 @@ Feature: Award badges based on cohort
| user1 | First | User | first@example.com |
| user2 | Second | User | second@example.com |
| user3 | Third | User | third@example.com |
And the following "cohort members" exist:
| user | cohort |
| user1 | CH1 |
| user1 | CH2 |
| user2 | CH2 |
| user2 | CH3 |
And I log in as "admin"
And I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
@@ -419,7 +425,7 @@ Feature: Award badges based on cohort
And I press "Save"
And I press "Enable access"
When I press "Continue"
And I should see "Recipients (0)"
And I should see "Recipients (1)"
And I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
| Name | Site Badge 2 |
@@ -433,11 +439,7 @@ Feature: Award badges based on cohort
And I press "Save"
And I press "Enable access"
And I press "Continue"
Then I navigate to "Users > Accounts >Cohorts" in site administration
And I add "First User (first@example.com)" user to "CH1" cohort members
And I add "First User (first@example.com)" user to "CH2" cohort members
And I add "Second User (second@example.com)" user to "CH2" cohort members
And I add "Second User (second@example.com)" user to "CH3" cohort members
And I should see "Recipients (1)"
And I log out
And I log in as "user1"
And I follow "Profile" in the user menu
+3
View File
@@ -57,5 +57,8 @@ function xmldb_block_badges_upgrade($oldversion, $block) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -72,5 +72,8 @@ function xmldb_block_calendar_month_upgrade($oldversion, $block) {
upgrade_block_savepoint(true, 2022041901, 'calendar_month', false);
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -57,5 +57,8 @@ function xmldb_block_calendar_upcoming_upgrade($oldversion, $block) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -60,5 +60,8 @@ function xmldb_block_completionstatus_upgrade($oldversion, $block) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -71,5 +71,8 @@ function xmldb_block_course_summary_upgrade($oldversion, $block) {
upgrade_block_savepoint(true, 2021121600, 'course_summary', false);
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -58,5 +58,8 @@ function xmldb_block_feedback_upgrade($oldversion, $block) {
upgrade_block_savepoint(true, 2021121600, 'feedback', false);
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -45,5 +45,8 @@ function xmldb_block_html_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+11
View File
@@ -597,9 +597,20 @@ class block_base {
* @return boolean
*/
function user_can_addto($page) {
global $CFG;
require_once($CFG->dirroot . '/user/lib.php');
// List of formats this block supports.
$formats = $this->applicable_formats();
// Check if user is trying to add blocks to their profile page.
$userpagetypes = user_page_type_list($page->pagetype, null, null);
if (array_key_exists($page->pagetype, $userpagetypes)) {
$capability = 'block/' . $this->name() . ':addinstance';
return $this->has_add_block_capability($page, $capability)
&& has_capability('moodle/user:manageownblocks', $page->context);
}
// The blocks in My Moodle are a special case and use a different capability.
$mypagetypes = my_page_type_list($page->pagetype); // Get list of possible my page types.
+3
View File
@@ -120,5 +120,8 @@ function xmldb_block_myoverview_upgrade($oldversion) {
upgrade_block_savepoint(true, 2022041901, 'myoverview', false);
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -67,5 +67,8 @@ function xmldb_block_navigation_upgrade($oldversion, $block) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -59,5 +59,8 @@ function xmldb_block_recent_activity_upgrade($oldversion, $block) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
@@ -88,5 +88,8 @@ function xmldb_block_recentlyaccesseditems_upgrade($oldversion, $block) {
upgrade_block_savepoint(true, 2022041901, 'recentlyaccesseditems', false);
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -56,5 +56,8 @@ function xmldb_block_rss_client_upgrade($oldversion) {
upgrade_block_savepoint(true, 2021121600, 'rss_client', false);
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -61,5 +61,8 @@ function xmldb_block_section_links_upgrade($oldversion, $block) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -71,5 +71,8 @@ function xmldb_block_selfcompletion_upgrade($oldversion, $block) {
upgrade_block_savepoint(true, 2021121600, 'selfcompletion', false);
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -67,5 +67,8 @@ function xmldb_block_settings_upgrade($oldversion, $block) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -132,5 +132,8 @@ function xmldb_block_tag_youtube_upgrade($oldversion) {
upgrade_block_savepoint(true, 2021052501, 'tag_youtube', false);
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -71,5 +71,8 @@ function xmldb_block_timeline_upgrade($oldversion, $block) {
upgrade_block_savepoint(true, 2022041901, 'timeline', false);
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
@@ -140,6 +140,13 @@ class managesubscriptions extends \moodleform {
$errors['url'] = get_string('errorrequiredurlorfile', 'calendar');
}
// Validate course/category event types (ensure appropriate field is also filled in).
if ($eventtype === 'course' && empty($data['courseid'])) {
$errors['courseid'] = get_string('selectacourse');
} else if ($eventtype === 'category' && empty($data['categoryid'])) {
$errors['categoryid'] = get_string('required');
}
return $errors;
}
+7 -9
View File
@@ -908,15 +908,12 @@ abstract class base {
'format' => $this->format,
'sectionid' => $sectionid
), '', 'id,name,value');
$indexedrecords = [];
foreach ($records as $record) {
if (array_key_exists($record->name, $this->formatoptions[$sectionid])) {
$value = $record->value;
if ($value !== null && isset($options[$record->name]['type'])) {
// This will convert string value to number if needed.
$value = clean_param($value, $options[$record->name]['type']);
}
$this->formatoptions[$sectionid][$record->name] = $value;
}
$indexedrecords[$record->name] = $record->value;
}
foreach ($options as $optionname => $option) {
contract_value($this->formatoptions[$sectionid], $indexedrecords, $option, $optionname);
}
}
}
@@ -1011,7 +1008,7 @@ abstract class base {
$data = array_intersect_key($rawdata, $allformatoptions);
foreach ($data as $key => $value) {
$option = $allformatoptions[$key] + ['type' => PARAM_RAW, 'element_type' => null, 'element_attributes' => [[]]];
$data[$key] = clean_param($value, $option['type']);
expand_value($data, $data, $option, $key);
if ($option['element_type'] === 'select' && !array_key_exists($data[$key], $option['element_attributes'][0])) {
// Value invalid for select element, skip.
unset($data[$key]);
@@ -1060,6 +1057,7 @@ abstract class base {
if (array_key_exists('default', $option)) {
$defaultoptions[$key] = $option['default'];
}
expand_value($defaultoptions, $defaultoptions, $option, $key);
$cached[$key] = ($sectionid === 0 || !empty($option['cache']));
}
$records = $DB->get_records('course_format_options',
+64
View File
@@ -135,3 +135,67 @@ class format_site extends course_format {
return 1;
}
}
/**
* 'Converts' a value from what is stored in the database into what is used by edit forms.
*
* @param array $dest The destination array
* @param array $source The source array
* @param array $option The definition structure of the option.
* @param string $optionname The name of the option, as provided in the definition.
*/
function contract_value(array &$dest, array $source, array $option, string $optionname) : void {
if (substr($optionname, -7) == '_editor') { // Suffix '_editor' indicates that the element is an editor.
$name = substr($optionname, 0, -7);
if (isset($source[$name])) {
$dest[$optionname] = [
'text' => clean_param_if_not_null($source[$name], $option['type'] ?? PARAM_RAW),
'format' => clean_param_if_not_null($source[$name . 'format'], PARAM_INT),
];
}
} else {
if (isset($source[$optionname])) {
$dest[$optionname] = clean_param_if_not_null($source[$optionname], $option['type'] ?? PARAM_RAW);
}
}
}
/**
* Cleans the given param, unless it is null.
*
* @param mixed $param The variable we are cleaning.
* @param string $type Expected format of param after cleaning.
* @return mixed Null if $param is null, otherwise the cleaned value.
* @throws coding_exception
*/
function clean_param_if_not_null($param, string $type = PARAM_RAW) {
if ($param === null) {
return null;
} else {
return clean_param($param, $type);
}
}
/**
* 'Converts' a value from what is used in edit forms into a value(s) to be stored in the database.
*
* @param array $dest The destination array
* @param array $source The source array
* @param array $option The definition structure of the option.
* @param string $optionname The name of the option, as provided in the definition.
*/
function expand_value(array &$dest, array $source, array $option, string $optionname) : void {
if (substr($optionname, -7) == '_editor') { // Suffix '_editor' indicates that the element is an editor.
$name = substr($optionname, 0, -7);
if (is_string($source[$optionname])) {
$dest[$name] = clean_param($source[$optionname], $option['type'] ?? PARAM_RAW);
$dest[$name . 'format'] = 1;
} else {
$dest[$name] = clean_param($source[$optionname]['text'], $option['type'] ?? PARAM_RAW);
$dest[$name . 'format'] = clean_param($source[$optionname]['format'], PARAM_INT);
}
unset($dest[$optionname]);
} else {
$dest[$optionname] = clean_param($source[$optionname], $option['type'] ?? PARAM_RAW);
}
}
+29
View File
@@ -36,6 +36,35 @@ class base_test extends advanced_testcase {
require_once($CFG->dirroot . '/course/format/tests/fixtures/format_theunittest_output_course_format_invalidoutput.php');
}
/**
* Tests the save and load functionality.
*
* @author Jason den Dulk
* @covers \core_courseformat
*/
public function test_courseformat_saveandload() {
$this->resetAfterTest();
$courseformatoptiondata = (object) [
"hideoddsections" => 1,
'summary_editor' => [
'text' => '<p>Somewhere over the rainbow</p><p>The <b>quick</b> brown fox jumpos over the lazy dog.</p>',
'format' => 1
]
];
$generator = $this->getDataGenerator();
$course1 = $generator->create_course(array('format' => 'theunittest'));
$this->assertEquals('theunittest', $course1->format);
course_create_sections_if_missing($course1, array(0, 1));
$courseformat = course_get_format($course1);
$courseformat->update_course_format_options($courseformatoptiondata);
$savedcourseformatoptiondata = $courseformat->get_format_options();
$this->assertEqualsCanonicalizing($courseformatoptiondata, (object) $savedcourseformatoptiondata);
}
public function test_available_hook() {
global $DB;
$this->resetAfterTest();
+9 -1
View File
@@ -37,6 +37,10 @@ class format_theunittest extends core_courseformat\base {
'default' => 0,
'type' => PARAM_INT,
),
'summary_editor' => array(
'default' => '',
'type' => PARAM_RAW,
),
);
}
if ($foreditform && !isset($courseformatoptions['hideoddsections']['label'])) {
@@ -51,6 +55,10 @@ class format_theunittest extends core_courseformat\base {
'element_type' => 'select',
'element_attributes' => array($sectionmenu),
),
'summary_editor' => array(
'label' => 'Summary Text',
'element_type' => 'editor',
),
);
$courseformatoptions = array_merge_recursive($courseformatoptions, $courseformatoptionsedit);
}
@@ -74,4 +82,4 @@ class format_theunittest extends core_courseformat\base {
}
}
}
}
}
+3
View File
@@ -45,5 +45,8 @@ function xmldb_format_topics_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -45,5 +45,8 @@ function xmldb_format_weeks_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3 -2
View File
@@ -391,12 +391,13 @@ function course_integrity_check($courseid, $rawmods = null, $sections = null, $f
* and the value is a lang_string object with a human-readable string.
*
* @param bool $plural If true, the function returns the plural forms of the names.
* @param bool $resetcache If true, the static cache will be reset
* @return lang_string[] Localised human-readable names of all used modules.
*/
function get_module_types_names($plural = false) {
function get_module_types_names($plural = false, $resetcache = false) {
static $modnames = null;
global $DB, $CFG;
if ($modnames === null) {
if ($modnames === null || $resetcache) {
$modnames = array(0 => array(), 1 => array());
if ($allmods = $DB->get_records("modules")) {
foreach ($allmods as $mod) {
+8 -8
View File
@@ -630,14 +630,6 @@ abstract class moodleform_mod extends moodleform {
$mform->addHelpButton('cmidnumber', 'idnumbermod');
}
if ($this->_features->groups) {
$options = array(NOGROUPS => get_string('groupsnone'),
SEPARATEGROUPS => get_string('groupsseparate'),
VISIBLEGROUPS => get_string('groupsvisible'));
$mform->addElement('select', 'groupmode', get_string('groupmode', 'group'), $options, NOGROUPS);
$mform->addHelpButton('groupmode', 'groupmode', 'group');
}
if ($CFG->downloadcoursecontentallowed) {
$choices = [
DOWNLOAD_COURSE_CONTENT_DISABLED => get_string('no'),
@@ -654,6 +646,14 @@ abstract class moodleform_mod extends moodleform {
}
}
if ($this->_features->groups) {
$options = array(NOGROUPS => get_string('groupsnone'),
SEPARATEGROUPS => get_string('groupsseparate'),
VISIBLEGROUPS => get_string('groupsvisible'));
$mform->addElement('select', 'groupmode', get_string('groupmode', 'group'), $options, NOGROUPS);
$mform->addHelpButton('groupmode', 'groupmode', 'group');
}
if ($this->_features->groupings) {
// Groupings selector - used to select grouping for groups in activity.
$options = array();
+46
View File
@@ -26,6 +26,7 @@ global $CFG;
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
require_once($CFG->dirroot . '/course/format/tests/fixtures/format_theunittest.php');
/**
* Course restore testcase.
@@ -534,4 +535,49 @@ class restore_test extends \advanced_testcase {
$this->assertEquals($chat2->chattime, $restoredchat2->chattime);
$this->assertEquals($c2->startdate + 1 * WEEKSECS, $restoredchat2->chattime);
}
/**
* Tests course restore with editor in course format.
*
* @author Matthew Hilton
* @covers \core_courseformat
*/
public function test_restore_editor_courseformat() {
$this->resetAfterTest();
// Setup user with restore permissions.
$dg = $this->getDataGenerator();
$u1 = $dg->create_user();
$managers = get_archetype_roles('manager');
$manager = array_shift($managers);
$dg->role_assign($manager->id, $u1->id);
// Create a course with an editor item in the course format.
$courseformatoptiondata = (object) [
"hideoddsections" => 1,
'summary_editor' => [
'text' => '<p>Somewhere over the rainbow</p><p>The <b>quick</b> brown fox jumpos over the lazy dog.</p>',
'format' => 1
]
];
$course1 = $dg->create_course(['format' => 'theunittest']);
$course2 = $dg->create_course(['format' => 'theunittest']);
$this->assertEquals('theunittest', $course1->format);
course_create_sections_if_missing($course1, array(0, 1));
// Set the course format.
$courseformat = course_get_format($course1);
$courseformat->update_course_format_options($courseformatoptiondata);
// Backup and restore the course.
$backupid = $this->backup_course($course1->id);
$this->restore_to_existing_course($backupid, $course2->id, $u1->id);
// Get the restored course format.
$restoredformat = course_get_format($course2);
$restoredformatoptions = $restoredformat->get_format_options();
$this->assertEqualsCanonicalizing($courseformatoptiondata, (object) $restoredformatoptions);
}
}
@@ -0,0 +1,105 @@
<?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/>.
namespace core_course;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/course/lib.php');
require_once($CFG->dirroot . '/course/format/tests/fixtures/format_theunittest.php');
require_once($CFG->dirroot . '/course/format/lib.php');
/**
* Course format function unit tests
*
* @package core_course
* @copyright 2021 Catalyst IT Pty Ltd
* @author Jason den Dulk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_format_function_test extends \basic_testcase {
/**
* Tests clean_param_if_not_null function
* @covers ::clean_param_if_not_null
*/
public function test_clean_param_if_not_null() {
$this->assertNull(clean_param_if_not_null(null));
$n = '3x';
$this->assertEquals(clean_param($n, PARAM_INT), clean_param_if_not_null($n, PARAM_INT));
$this->assertEquals(clean_param($n, PARAM_RAW), clean_param_if_not_null($n, PARAM_RAW));
$this->assertEquals(clean_param($n, PARAM_ALPHANUM), clean_param_if_not_null($n, PARAM_ALPHANUM));
$this->assertEquals(clean_param($n, PARAM_ALPHA), clean_param_if_not_null($n, PARAM_ALPHA));
$s = '<abc>xyz</abc>';
$this->assertEquals(clean_param($s, PARAM_ALPHANUM), clean_param_if_not_null($s, PARAM_ALPHANUM));
$this->assertEquals(clean_param($s, PARAM_RAW), clean_param_if_not_null($s, PARAM_RAW));
}
/**
* Tests contract_value function
* @covers ::contract_value
*/
public function test_contract_value() {
$input = [
'abc' => '<p>All together Now</p>',
'abcformat' => '1',
'jolly' => 'Roger'
];
$expected = [
'abc_editor' => [ 'text' => $input['abc'], 'format' => $input['abcformat'] ],
'jolly' => $input['jolly'],
];
$defs = [
'abc_editor' => [],
'jolly' => [ 'type' => PARAM_ALPHA ],
];
$dest = [];
foreach ($defs as $name => $def) {
contract_value($dest, $input, $def, $name);
}
$this->assertEquals($expected, $dest);
}
/**
* Tests expand_value function
* @covers ::expand_value
*/
public function test_expand_value() {
$input = [
'abc_editor' => [ 'text' => '<p>All together Now</p>', 'format' => '1' ],
'jolly' => 'Roger',
];
$expected = [
'abc' => $input['abc_editor']['text'],
'abcformat' => $input['abc_editor']['format'],
'jolly' => $input['jolly'],
];
$defs = [
'abc_editor' => [],
'jolly' => [ 'type' => PARAM_ALPHA ],
];
$dest = [];
foreach ($defs as $name => $def) {
expand_value($dest, $input, $def, $name);
}
$this->assertEquals($expected, $dest);
}
}
+1 -9
View File
@@ -34,14 +34,6 @@ require_login();
$context = \context_helper::instance_by_id($contextid);
$PAGE->set_context($context);
if ($context->id === \context_user::instance($USER->id)->id) {
$PAGE->set_blocks_editing_capability('moodle/my:manageblocks');
}
if ($PAGE->user_allowed_editing()) {
$USER->editing = $setmode;
} else {
\core\notification::add(get_string('cannotswitcheditmodeon', 'error'), \core\notification::ERROR);
}
$USER->editing = $setmode;
redirect($pageurl);
+3
View File
@@ -39,5 +39,8 @@ function xmldb_enrol_database_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -39,5 +39,8 @@ function xmldb_enrol_flatfile_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -39,5 +39,8 @@ function xmldb_enrol_guest_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -45,5 +45,8 @@ function xmldb_enrol_imsenterprise_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
@@ -86,7 +86,7 @@ class ags_info {
* @throws \coding_exception if any of the scopes is invalid.
*/
private function validate_scopes(array $scopes): void {
$validscopes = [
$supportedscopes = [
self::SCOPES_LINEITEM_READONLY,
self::SCOPES_LINEITEM_MANAGE,
self::SCOPES_RESULT_READONLY,
@@ -96,17 +96,14 @@ class ags_info {
if (!is_string($scope)) {
throw new \coding_exception('Scope must be a string value');
}
$key = array_search($scope, $validscopes);
if ($key === false) {
throw new \coding_exception("Scope '{$scope}' is invalid.");
}
if ($key == 0) {
$key = array_search($scope, $supportedscopes);
if ($key === 0) {
$this->lineitemscopes[] = self::SCOPES_LINEITEM_READONLY;
} else if ($key == 1) {
} else if ($key === 1) {
$this->lineitemscopes[] = self::SCOPES_LINEITEM_MANAGE;
} else if ($key == 2) {
} else if ($key === 2) {
$this->resultscope = self::SCOPES_RESULT_READONLY;
} else if ($key == 3) {
} else if ($key === 3) {
$this->scorescope = self::SCOPES_SCORES_POST;
}
}
+3
View File
@@ -456,5 +456,8 @@ function xmldb_enrol_lti_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2022031402, 'enrol', 'lti');
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+2 -2
View File
@@ -43,7 +43,7 @@ $string['deploymentdeleteconfirm'] = 'Warning: Deleting a deployment will result
$string['deploymentdeletenotice'] = 'Deployment deleted';
$string['deploymentid'] = 'Deployment ID';
$string['deployments'] = 'Deployments';
$string['deploymentsinfo'] = 'A deployment ID will be generated by the platform when the tool is made available in a context. This may occur at the time of registration, or may occur later, when the tool is made available to a specific course or category. The Deployment ID must be entered here before launches from that tool deployment are permitted.';
$string['deploymentsinfo'] = 'A deployment ID is generated by the platform when the tool is made available in a context. This may occur at the time of registration, or later, when the tool is made available to a specific course or category. The deployment ID must be entered here before launches from the tool deployment are permitted.';
$string['details'] = 'Details';
$string['editplatformdetails'] = 'Edit platform details';
$string['endpointltiversionnotice'] = 'The tool endpoints below are for manual LTI 1.3 setup only. For earlier versions (1.1/2.0), details for consumers can be found on the \'Published as LTI tools\' page, accessible via course navigation.';
@@ -114,7 +114,7 @@ $string['publishedcontent_help'] = 'A published resource or activity can be used
$string['platformdetails'] = 'Platform details';
$string['platformdetailsinfo'] = 'Once the tool has been set up in the platform, details from the platform must be recorded here to complete the registration.
<ul>
<li>For dynamic registrations, this information will have been set automatically and no further changes are required</li>
<li>For dynamic registrations, this information will have been set automatically and no further changes are required.</li>
<li>For manual registrations, this information must be manually copied from the platform.</li>
</ul>';
$string['pluginname'] = 'Publish as LTI tool';
+2 -2
View File
@@ -1,11 +1,11 @@
/* enrol_lti styles */
#registration-tab-content .input-group-append a.btn {
#registration-tab-content .input-group-append button.btn {
border: solid 1px #8f959e;
}
form#auto_submit {
display: none;
}
#registration-tab-content .input-group-append a.btn span.pix {
#registration-tab-content .input-group-append button.btn span.pix {
padding-left: 0.5rem;
}
@@ -62,7 +62,7 @@
<table class="admintable generaltable">
<thead></thead>
<tbody>
<tr>
<tr class="d-flex">
<th class="col-3" id="lti_tool_endpoint_name_{{id}}">
<label for="lti_tool_endpoint_url_{{id}}">{{name}}</label>
</th>
@@ -70,9 +70,7 @@
<div class="input-group col-md-6 ml-0 pl-0">
<input class="form-control" type="text" value="{{url}}" aria-label="{{url}}" id="lti_tool_endpoint_url_{{id}}" readonly>
<div class="input-group-append">
<a class="btn btn-secondary"
href="#"
role="button"
<button class="btn btn-secondary"
aria-label="{{#str}} copytoclipboard, enrol_lti {{/str}}"
data-action="copytoclipboard"
data-clipboard-success-message="{{#str}}copiedtoclipboard, enrol_lti, {{name}}{{/str}}"
@@ -80,7 +78,7 @@
id="{{id}}"
title="{{#str}}copytoclipboard, enrol_lti{{/str}}">
<span class="pix">{{#pix}} t/clipboard, core {{/pix}}</span>
</a>
</button>
</div>
</div>
</td>
@@ -95,14 +93,14 @@
<div id="lti_tool_endpoints">
<table class="admintable generaltable">
<thead>
<tr>
<tr class="d-flex">
<th>{{#str}}name, core{{/str}}</th>
<th>{{#str}}url, core{{/str}}</th>
</tr>
</thead>
<tbody>
{{#manual_registration_urls}}
<tr>
<tr class="d-flex">
<th class="col-3" id="lti_tool_endpoint_name_{{id}}">
<label for="lti_tool_endpoint_url_{{id}}">{{name}}</label>
</th>
@@ -110,9 +108,7 @@
<div class="input-group col-md-6 ml-0 pl-0">
<input class="form-control" type="text" value="{{url}}" aria-label="{{url}}" id="lti_tool_endpoint_url_{{id}}" readonly>
<div class="input-group-append">
<a class="btn btn-secondary"
href="#"
role="button"
<button class="btn btn-secondary"
aria-label="{{#str}} copytoclipboard, enrol_lti {{/str}}"
data-action="copytoclipboard"
data-clipboard-success-message="{{#str}}copiedtoclipboard, enrol_lti, {{name}}{{/str}}"
@@ -120,7 +116,7 @@
id="{{id}}"
title="{{#str}}copytoclipboard, enrol_lti{{/str}}">
<span class="pix">{{#pix}} t/clipboard, core {{/pix}}</span>
</a>
</button>
</div>
</div>
</td>
@@ -198,18 +198,18 @@ Feature: Register a platform with the tool
And the "value" attribute of "Initiate login URL" "field" should contain "enrol/lti/login.php"
And the "value" attribute of "JWKS URL" "field" should contain "enrol/lti/jwks.php"
And the "value" attribute of "Deep linking URL" "field" should contain "enrol/lti/launch_deeplink.php"
And "Copy to clipboard" "link" should exist in the "Registration URL" "table_row"
And "Copy to clipboard" "link" should exist in the "Tool URL" "table_row"
And "Copy to clipboard" "link" should exist in the "Initiate login URL" "table_row"
And "Copy to clipboard" "link" should exist in the "JWKS URL" "table_row"
And "Copy to clipboard" "link" should exist in the "Deep linking URL" "table_row"
When I click on "Copy to clipboard" "link" in the "Registration URL" "table_row"
And "Copy to clipboard" "button" should exist in the "Registration URL" "table_row"
And "Copy to clipboard" "button" should exist in the "Tool URL" "table_row"
And "Copy to clipboard" "button" should exist in the "Initiate login URL" "table_row"
And "Copy to clipboard" "button" should exist in the "JWKS URL" "table_row"
And "Copy to clipboard" "button" should exist in the "Deep linking URL" "table_row"
When I click on "Copy to clipboard" "button" in the "Registration URL" "table_row"
Then I should see "Registration URL copied to clipboard"
And I click on "Copy to clipboard" "link" in the "Tool URL" "table_row"
And I click on "Copy to clipboard" "button" in the "Tool URL" "table_row"
And I should see "Tool URL copied to clipboard"
And I click on "Copy to clipboard" "link" in the "Initiate login URL" "table_row"
And I click on "Copy to clipboard" "button" in the "Initiate login URL" "table_row"
And I should see "Initiate login URL copied to clipboard"
And I click on "Copy to clipboard" "link" in the "JWKS URL" "table_row"
And I click on "Copy to clipboard" "button" in the "JWKS URL" "table_row"
And I should see "JWKS URL copied to clipboard"
And I click on "Copy to clipboard" "link" in the "Deep linking URL" "table_row"
And I click on "Copy to clipboard" "button" in the "Deep linking URL" "table_row"
And I should see "Deep linking URL copied to clipboard"
@@ -42,7 +42,12 @@ class ags_info_test extends \advanced_testcase {
$agsinfo = ags_info::create(...array_values($args));
$this->assertEquals($args['lineitemsurl'], $agsinfo->get_lineitemsurl());
$this->assertEquals($args['lineitemurl'], $agsinfo->get_lineitemurl());
$this->assertEquals($args['scopes'], $agsinfo->get_scopes());
if (isset($expectations['scopes'])) {
$this->assertEquals($expectations['scopes'], $agsinfo->get_scopes());
} else {
$this->assertEquals($args['scopes'], $agsinfo->get_scopes());
}
$this->assertEquals($expectations['lineitemscope'], $agsinfo->get_lineitemscope());
$this->assertEquals($expectations['scorescope'], $agsinfo->get_scorescope());
$this->assertEquals($expectations['resultscope'], $agsinfo->get_resultscope());
@@ -239,7 +244,7 @@ class ags_info_test extends \advanced_testcase {
'exceptionmessage' => 'Scope must be a string value'
]
],
'Both lineitems and lineitem URL provided with invalid scopes' => [
'Both lineitems and lineitem URL provided with unsupported scopes' => [
'args' => [
'lineitemsurl' => new \moodle_url('https://platform.example.org/10/lineitems'),
'lineitemurl' => new \moodle_url('https://platform.example.org/10/lineitems/4/lineitem'),
@@ -248,13 +253,23 @@ class ags_info_test extends \advanced_testcase {
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly',
'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly',
'https://purl.imsglobal.org/spec/lti-ags/scope/score',
'https://example.com/invalid/scope'
'https://example.com/unsupported/scope'
]
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Scope 'https://example.com/invalid/scope' is invalid."
'valid' => true,
'scopes' => [
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem',
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly',
'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly',
'https://purl.imsglobal.org/spec/lti-ags/scope/score',
],
'lineitemscope' => [
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem',
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly'
],
'resultscope' => 'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly',
'scorescope' => 'https://purl.imsglobal.org/spec/lti-ags/scope/score'
]
],
'Both lineitems and lineitem URL provided with invalid scope types' => [
+3
View File
@@ -39,5 +39,8 @@ function xmldb_enrol_manual_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -39,5 +39,8 @@ function xmldb_enrol_mnet_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -57,5 +57,8 @@ function xmldb_enrol_paypal_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -39,5 +39,8 @@ function xmldb_enrol_self_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -60,5 +60,8 @@ function xmldb_filter_displayh5p_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -67,5 +67,8 @@ function xmldb_filter_mathjaxloader_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2021052501, 'filter', 'mathjaxloader');
}
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -44,5 +44,8 @@ function xmldb_filter_mediaplugin_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -44,5 +44,8 @@ function xmldb_filter_tex_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -49,5 +49,8 @@ function xmldb_gradingform_guide_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -45,5 +45,8 @@ function xmldb_gradingform_rubric_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -46,5 +46,8 @@ function xmldb_gradereport_history_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -44,5 +44,8 @@ function xmldb_gradereport_overview_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -41,5 +41,8 @@ function xmldb_gradereport_user_upgrade($oldversion) {
// Automatically generated Moodle v3.9.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.0.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+2
View File
@@ -38,3 +38,5 @@ $string['clitypevaluedefault'] = 'တန်ဖိုး ထည့်သွင်
$string['cliunknowoption'] = 'အသိအမှတ် မပြုထားသော ရွေးချယ်မှုများ :
{$a}
ကျေးဇူးပြု၍ -- အကူအညီရယူရန် ကိုနှိပ်ပါ။';
$string['environmentrequireinstall'] = 'ထည့်သွင်းပြီး အသုံးပြုခွင့်ဖွင့်ပါ';
$string['environmentrequireversion'] = 'ဗားရှင်း{$a->needed} ကို လိုအပ်ပြီး သင်သည် {$a->current} ကိုအသုံးပြုနေပါသည်';
+2
View File
@@ -101,6 +101,8 @@ $string['norecentaccessesinfomessage'] = 'Hi {$a->userfirstname},
<p>A number of students in {$a->coursename} have not accessed the course recently.</p>';
$string['noteachinginfomessage'] = 'Hi {$a->userfirstname},
<p>Courses with start dates in the next week have been identified as having no teacher or student enrolments.</p>';
$string['participants:perpage'] = 'Number of participants per page';
$string['participants:perpage_help'] = 'Specify the number of participants that are shown per page';
$string['participantsnavigation'] = 'Participants tertiary navigation.';
$string['privacy:perpage'] = 'The number of courses to show per page.';
$string['privacy:completionpath'] = 'Course completion';

Some files were not shown because too many files have changed in this diff Show More