Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ec02c2a67 | |||
| 8a709503ac | |||
| 2c7f5745c6 | |||
| 9a18af46d7 | |||
| e09160b6db | |||
| 884a9b9706 | |||
| 753b2fb53e | |||
| 7806835569 | |||
| a937101b06 | |||
| 8596b6a299 | |||
| b5c8188961 | |||
| ed3ee9dd5c | |||
| 87aaffc3d1 | |||
| 4fae57fc97 | |||
| 9b9237fde1 | |||
| 6f3450ef1e | |||
| 089f5bb0ec | |||
| 0391a4afc7 | |||
| 0b26fce0b6 | |||
| 361070ef5d | |||
| 81cb18a67c | |||
| f4b92ca799 | |||
| e563d32ca2 | |||
| 2575add511 | |||
| 61b3c4b453 | |||
| d82d2bd6d6 | |||
| a7a8db38aa | |||
| a4e45e8159 | |||
| 814c80da13 | |||
| f7ffb41db4 | |||
| c9efe1b1ac | |||
| 82c845d8d3 | |||
| 30da75a460 | |||
| fdb7aa8035 | |||
| 4e14e2631c | |||
| 2ae147c9af | |||
| 6cacc0a4a3 | |||
| c66c1a04b5 | |||
| 06001d9c9d | |||
| 9f01ded64e | |||
| 7bd088e443 | |||
| f67020d1c2 | |||
| 2bc1e50886 |
@@ -55,9 +55,16 @@ jobs:
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
php: 7.2
|
||||
# Need to disable both redis and memcached (both are optional) in order to keep igbinary installed.
|
||||
# (there are dependencies that become broken if we just switch the igbinary version).
|
||||
# Need to do this for php 7.2 because of a bug with current default igbinary
|
||||
# versions used by GHA being >= 3.2.2 and <= 3.2.4 (buggy ones).
|
||||
# TODO: MDL-72399 - Revert this patch once igbinary 3.2.5 or up is the default @ GHA.
|
||||
extensions: :redis, :memcached, igbinary-3.2.6
|
||||
db: mysqli
|
||||
- os: ubuntu-18.04
|
||||
php: 7.4
|
||||
extensions:
|
||||
db: pgsql
|
||||
|
||||
steps:
|
||||
@@ -88,6 +95,7 @@ jobs:
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: ${{ matrix.extensions }}
|
||||
coverage: none
|
||||
|
||||
- name: Checking out code from ${{ env.GITHUB_REF_SLUG }}
|
||||
|
||||
@@ -3163,6 +3163,11 @@
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_igbinary322_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="igbinary322buggyversion" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.10" requires="3.5">
|
||||
@@ -3345,6 +3350,11 @@
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_igbinary322_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="igbinary322buggyversion" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.11" requires="3.6">
|
||||
|
||||
@@ -53,11 +53,8 @@ class behat_admin extends behat_base {
|
||||
}
|
||||
|
||||
foreach ($data as $label => $value) {
|
||||
$this->execute('behat_navigation::i_select_from_flat_navigation_drawer', [get_string('administrationsite')]);
|
||||
|
||||
// Search by label.
|
||||
$this->execute('behat_forms::i_set_the_field_to', [get_string('query', 'admin'), $label]);
|
||||
$this->execute("behat_forms::press_button", get_string('search', 'admin'));
|
||||
// Navigate straight to the search results fo rthis label.
|
||||
$this->execute('behat_general::i_visit', ["/admin/search.php?query=" . urlencode($label)]);
|
||||
|
||||
// Admin settings does not use the same DOM structure than other moodle forms
|
||||
// but we also need to use lib/behat/form_field/* to deal with the different moodle form elements.
|
||||
|
||||
@@ -9,10 +9,8 @@ Feature: Manage data requests
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | John | Doe | s1@example.com |
|
||||
| student2 | Jane | Doe | s2@example.com |
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
| contactdataprotectionofficer | 1 |
|
||||
And I log out
|
||||
And the following config values are set as admin:
|
||||
| contactdataprotectionofficer | 1 | tool_dataprivacy |
|
||||
|
||||
@javascript
|
||||
Scenario: Marking general enquiries as complete
|
||||
|
||||
@@ -50,6 +50,7 @@ switch ($action) {
|
||||
case 'disable':
|
||||
unset($enabled[$enrol]);
|
||||
set_config('enabled_stores', implode(',', array_keys($enabled)), 'tool_log');
|
||||
add_to_config_log('tool_logstore_visibility', '1', '0', $enrol);
|
||||
break;
|
||||
|
||||
case 'enable':
|
||||
@@ -59,6 +60,7 @@ switch ($action) {
|
||||
$enabled = array_keys($enabled);
|
||||
$enabled[] = $enrol;
|
||||
set_config('enabled_stores', implode(',', $enabled), 'tool_log');
|
||||
add_to_config_log('tool_logstore_visibility', '0', '1', $enrol);
|
||||
break;
|
||||
|
||||
case 'up':
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
@tool_log @report @report_configlog
|
||||
Feature: In a report, admin can see logstore visibility changes
|
||||
|
||||
# Change log stores visibility so the report contains known data.
|
||||
Background:
|
||||
Given I log in as "admin"
|
||||
And the following config values are set as admin:
|
||||
| enabled_stores | logstore_standard | tool_log |
|
||||
|
||||
@javascript
|
||||
Scenario: Display configuration changes report
|
||||
When I navigate to "Plugins > Logging > Manage log stores" in site administration
|
||||
And I click on "Disable" "icon" in the "Standard log" "table_row"
|
||||
And I click on "Enable" "icon" in the "External database log" "table_row"
|
||||
And I navigate to "Reports > Config changes" in site administration
|
||||
Then the following should exist in the "report-configlog-report-table" table:
|
||||
| User | Plugin | Setting | New value | Original value |
|
||||
| Admin User | logstore_standard | tool_logstore_visibility | 0 | 1 |
|
||||
| Admin User | logstore_database | tool_logstore_visibility | 1 | 0 |
|
||||
+3
-3
@@ -131,11 +131,11 @@ class auth_plugin_db extends auth_plugin_base {
|
||||
$authdb->Close();
|
||||
|
||||
if ($this->config->passtype === 'plaintext') {
|
||||
return ($fromdb == $extpassword);
|
||||
return ($fromdb === $extpassword);
|
||||
} else if ($this->config->passtype === 'md5') {
|
||||
return (strtolower($fromdb) == md5($extpassword));
|
||||
return (strtolower($fromdb) === md5($extpassword));
|
||||
} else if ($this->config->passtype === 'sha1') {
|
||||
return (strtolower($fromdb) == sha1($extpassword));
|
||||
return (strtolower($fromdb) === sha1($extpassword));
|
||||
} else if ($this->config->passtype === 'saltedcrypt') {
|
||||
return password_verify($extpassword, $fromdb);
|
||||
} else {
|
||||
|
||||
@@ -335,12 +335,28 @@ class auth_db_testcase extends advanced_testcase {
|
||||
$DB->update_record('auth_db_users', $user3);
|
||||
$this->assertTrue($auth->user_login('u3', 'heslo'));
|
||||
|
||||
// Test user created to see if the checking happens strictly.
|
||||
$usermd5 = (object)['name' => 'usermd5', 'pass' => '0e462097431906509019562988736854'];
|
||||
$usermd5->id = $DB->insert_record('auth_db_users', $usermd5);
|
||||
|
||||
// md5('240610708') === '0e462097431906509019562988736854'.
|
||||
$this->assertTrue($auth->user_login('usermd5', '240610708'));
|
||||
$this->assertFalse($auth->user_login('usermd5', 'QNKCDZO'));
|
||||
|
||||
set_config('passtype', 'sh1', 'auth_db');
|
||||
$auth->config->passtype = 'sha1';
|
||||
$user3->pass = sha1('heslo');
|
||||
$DB->update_record('auth_db_users', $user3);
|
||||
$this->assertTrue($auth->user_login('u3', 'heslo'));
|
||||
|
||||
// Test user created to see if the checking happens strictly.
|
||||
$usersha1 = (object)['name' => 'usersha1', 'pass' => '0e66507019969427134894567494305185566735'];
|
||||
$usersha1->id = $DB->insert_record('auth_db_users', $usersha1);
|
||||
|
||||
// sha1('aaroZmOk') === '0e66507019969427134894567494305185566735'.
|
||||
$this->assertTrue($auth->user_login('usersha1', 'aaroZmOk'));
|
||||
$this->assertFalse($auth->user_login('usersha1', 'aaK1STfY'));
|
||||
|
||||
set_config('passtype', 'saltedcrypt', 'auth_db');
|
||||
$auth->config->passtype = 'saltedcrypt';
|
||||
$user3->pass = password_hash('heslo', PASSWORD_BCRYPT);
|
||||
|
||||
@@ -93,13 +93,12 @@ class helper {
|
||||
|
||||
foreach ($sessions as $session) {
|
||||
// Get user session from DB.
|
||||
if (session_decode(base64_decode($session->sessdata))) {
|
||||
if (isset($_SESSION['SESSION']) && isset($_SESSION['SESSION']->shibboleth_session_id)) {
|
||||
// If there is a match, kill the session.
|
||||
if ($_SESSION['SESSION']->shibboleth_session_id == trim($spsessionid)) {
|
||||
// Delete this user's sessions.
|
||||
\core\session\manager::kill_user_sessions($session->userid);
|
||||
}
|
||||
$usersession = self::unserializesession(base64_decode($session->sessdata));
|
||||
if (isset($usersession['SESSION']) && isset($usersession['SESSION']->shibboleth_session_id)) {
|
||||
// If there is a match, kill the session.
|
||||
if ($usersession['SESSION']->shibboleth_session_id == trim($spsessionid)) {
|
||||
// Delete this user's sessions.
|
||||
\core\session\manager::kill_user_sessions($session->userid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,10 +41,8 @@ Feature: Authentication
|
||||
Then I should see "You are not logged in" in the "page-footer" "region"
|
||||
|
||||
Scenario Outline: Checking the display of the Remember username checkbox
|
||||
Given I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
Given the following config values are set as admin:
|
||||
| rememberusername | <settingvalue> |
|
||||
And I log out
|
||||
And I am on homepage
|
||||
When I click on "Log in" "link" in the ".logininfo" "css_element"
|
||||
Then I should <expect> "Remember username"
|
||||
|
||||
@@ -358,6 +358,15 @@ class backup_controller extends base_controller {
|
||||
return $this->plan;
|
||||
}
|
||||
|
||||
/**
|
||||
* For debug only. Get a simple test display of all the settings.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function debug_display_all_settings_values(): string {
|
||||
return $this->get_plan()->debug_display_all_settings_values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user roles that should be kept in the destination course
|
||||
* for a course copy operation.
|
||||
|
||||
@@ -350,6 +350,15 @@ class restore_controller extends base_controller {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For debug only. Get a simple test display of all the settings.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function debug_display_all_settings_values(): string {
|
||||
return $this->get_plan()->debug_display_all_settings_values();
|
||||
}
|
||||
|
||||
public function get_info() {
|
||||
return $this->info;
|
||||
}
|
||||
|
||||
@@ -2110,7 +2110,6 @@ class restore_ras_and_caps_structure_step extends restore_structure_step {
|
||||
|
||||
public function process_override($data) {
|
||||
$data = (object)$data;
|
||||
|
||||
// Check roleid is one of the mapped ones
|
||||
$newrole = $this->get_mapping('role', $data->roleid);
|
||||
$newroleid = $newrole->newitemid ?? false;
|
||||
@@ -2128,8 +2127,8 @@ class restore_ras_and_caps_structure_step extends restore_structure_step {
|
||||
// Check if the new role is an overrideable role AND if the user performing the restore has the
|
||||
// capability to assign the capability.
|
||||
if (in_array($newrole->info['shortname'], $overrideableroles) &&
|
||||
($safecapability && has_capability('moodle/role:safeoverride', $context, $userid) ||
|
||||
!$safecapability && has_capability('moodle/role:override', $context, $userid))
|
||||
(has_capability('moodle/role:override', $context, $userid) ||
|
||||
($safecapability && has_capability('moodle/role:safeoverride', $context, $userid)))
|
||||
) {
|
||||
assign_capability($data->capability, $data->permission, $newroleid, $this->task->get_contextid());
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
<?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/>.
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
// Include all the needed stuff.
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
|
||||
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for how backup and restore handles role-related things.
|
||||
*
|
||||
* @package core_backup
|
||||
* @copyright 2021 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class roles_backup_restore_test extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Create a course where the (non-editing) Teacher role is overridden
|
||||
* to have 'moodle/user:loginas' and 'moodle/site:accessallgroups'.
|
||||
*
|
||||
* @return stdClass the new course.
|
||||
*/
|
||||
protected function create_course_with_role_overrides(): stdClass {
|
||||
$generator = $this->getDataGenerator();
|
||||
$course = $generator->create_course();
|
||||
$teacher = $generator->create_user();
|
||||
|
||||
$context = context_course::instance($course->id);
|
||||
$generator->enrol_user($teacher->id, $course->id, 'teacher');
|
||||
|
||||
$editingteacherrole = $this->get_role('teacher');
|
||||
role_change_permission($editingteacherrole->id, $context, 'moodle/user:loginas', CAP_ALLOW);
|
||||
role_change_permission($editingteacherrole->id, $context, 'moodle/site:accessallgroups', CAP_ALLOW);
|
||||
|
||||
return $course;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the role id from a shortname.
|
||||
*
|
||||
* @param string $shortname the role shortname.
|
||||
* @return stdClass the role from the DB.
|
||||
*/
|
||||
protected function get_role(string $shortname): stdClass {
|
||||
global $DB;
|
||||
return $DB->get_record('role', ['shortname' => $shortname]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array capability => CAP_... constant for all the orverrides set for a given role on a given context.
|
||||
*
|
||||
* @param string $shortname role shortname.
|
||||
* @param context $context context.
|
||||
* @return array the overrides set here.
|
||||
*/
|
||||
protected function get_overrides_for_role_on_context(string $shortname, context $context): array {
|
||||
$overridedata = get_capabilities_from_role_on_context($this->get_role($shortname), $context);
|
||||
$overrides = [];
|
||||
foreach ($overridedata as $override) {
|
||||
$overrides[$override->capability] = $override->permission;
|
||||
}
|
||||
return $overrides;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a backup of the course.
|
||||
*
|
||||
* @param stdClass $course The course object.
|
||||
* @return string Unique identifier for this backup.
|
||||
*/
|
||||
protected function backup_course(\stdClass $course): string {
|
||||
global $CFG, $USER;
|
||||
|
||||
// Turn off file logging, otherwise it can't delete the file (Windows).
|
||||
$CFG->backup_file_logger_level = backup::LOG_NONE;
|
||||
|
||||
// Do backup with default settings. MODE_IMPORT means it will just
|
||||
// create the directory and not zip it.
|
||||
$bc = new \backup_controller(backup::TYPE_1COURSE, $course->id,
|
||||
backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT,
|
||||
$USER->id);
|
||||
$backupid = $bc->get_backupid();
|
||||
$bc->execute_plan();
|
||||
$bc->destroy();
|
||||
|
||||
return $backupid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores a backup that has been made earlier.
|
||||
*
|
||||
* @param string $backupid The unique identifier of the backup.
|
||||
* @param string $asroleshortname Which role in the new cousre the restorer should have.
|
||||
* @return int The new course id.
|
||||
*/
|
||||
protected function restore_adding_to_course(string $backupid, string $asroleshortname): int {
|
||||
global $CFG, $USER;
|
||||
|
||||
// Create course to restore into, and a user to do the restore.
|
||||
$generator = $this->getDataGenerator();
|
||||
$course = $generator->create_course();
|
||||
$restorer = $generator->create_user();
|
||||
|
||||
$generator->enrol_user($restorer->id, $course->id, $asroleshortname);
|
||||
$this->setUser($restorer);
|
||||
|
||||
// Turn off file logging, otherwise it can't delete the file (Windows).
|
||||
$CFG->backup_file_logger_level = backup::LOG_NONE;
|
||||
|
||||
// Do restore to new course with default settings.
|
||||
$rc = new \restore_controller($backupid, $course->id,
|
||||
backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id,
|
||||
backup::TARGET_CURRENT_ADDING);
|
||||
|
||||
$precheck = $rc->execute_precheck();
|
||||
$this->assertTrue($precheck);
|
||||
$rc->get_plan()->get_setting('role_assignments')->set_value(true);
|
||||
$rc->execute_plan();
|
||||
$rc->destroy();
|
||||
|
||||
return $course->id;
|
||||
}
|
||||
|
||||
public function test_restore_role_overrides_as_manager(): void {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create a course and back it up.
|
||||
$course = $this->create_course_with_role_overrides();
|
||||
$backupid = $this->backup_course($course);
|
||||
|
||||
// When manager restores, both role overrides should be restored.
|
||||
$newcourseid = $this->restore_adding_to_course($backupid, 'manager');
|
||||
|
||||
// Verify.
|
||||
$overrides = $this->get_overrides_for_role_on_context('teacher',
|
||||
context_course::instance($newcourseid));
|
||||
$this->assertArrayHasKey('moodle/user:loginas', $overrides);
|
||||
$this->assertEquals(CAP_ALLOW, $overrides['moodle/user:loginas']);
|
||||
$this->assertArrayHasKey('moodle/site:accessallgroups', $overrides);
|
||||
$this->assertEquals(CAP_ALLOW, $overrides['moodle/site:accessallgroups']);
|
||||
}
|
||||
|
||||
public function test_restore_role_overrides_as_teacher(): void {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create a course and back it up.
|
||||
$course = $this->create_course_with_role_overrides();
|
||||
$backupid = $this->backup_course($course);
|
||||
|
||||
// When teacher restores, only the safe override should be restored.
|
||||
$newcourseid = $this->restore_adding_to_course($backupid, 'editingteacher');
|
||||
|
||||
// Verify.
|
||||
$overrides = $this->get_overrides_for_role_on_context('teacher',
|
||||
context_course::instance($newcourseid));
|
||||
$this->assertArrayNotHasKey('moodle/user:loginas', $overrides);
|
||||
$this->assertArrayHasKey('moodle/site:accessallgroups', $overrides);
|
||||
$this->assertEquals(CAP_ALLOW, $overrides['moodle/site:accessallgroups']);
|
||||
}
|
||||
}
|
||||
@@ -126,6 +126,19 @@ abstract class base_plan implements checksumable, executable {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* For debug only. Get a simple test display of all the settings.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function debug_display_all_settings_values(): string {
|
||||
$result = '';
|
||||
foreach ($this->settings as $name => $setting) {
|
||||
$result .= $name . ': ' . $setting->get_value() . "\n";
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper over @get_setting() that returns if the requested setting exists or no
|
||||
*/
|
||||
|
||||
@@ -14,10 +14,8 @@ Feature: Duplicate activities
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
| backup_import_activities | 0 |
|
||||
And I log out
|
||||
And the following config values are set as admin:
|
||||
| backup_import_activities | 0 | backup |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Database" to section "1" and I fill the form with:
|
||||
|
||||
@@ -24,9 +24,9 @@ Feature: Edit activities in main menu block
|
||||
|
||||
@javascript
|
||||
Scenario: Activities in main menu block can be made available but not visible on a course page
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
Given the following config values are set as admin:
|
||||
| allowstealth | 1 |
|
||||
And I log in as "admin"
|
||||
And I am on site homepage
|
||||
And I navigate to "Turn editing on" in current page administration
|
||||
And I add the "Main menu" block
|
||||
|
||||
@@ -33,10 +33,8 @@ Feature: Edit activities in social activities block
|
||||
|
||||
@javascript
|
||||
Scenario: Activities in social activities block can be made available but not visible on a course page
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
Given the following config values are set as admin:
|
||||
| allowstealth | 1 |
|
||||
And I log out
|
||||
And I log in as "user1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add the "Recent activity" block
|
||||
|
||||
@@ -2154,11 +2154,7 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
// Change our event's date to the 20th Monday of the current year.
|
||||
$twentiethmonday = new DateTime(date('Y-01-01'));
|
||||
$twentiethmonday->modify('+20 Monday');
|
||||
$startdatetime = $this->change_event_startdate($twentiethmonday->format('Ymd\T090000'), 'US/Eastern');
|
||||
|
||||
$startdate = new DateTime($startdatetime->format('Y-m-d'));
|
||||
|
||||
$offset = $startdatetime->diff($startdate, true);
|
||||
$startdatetime = $this->change_event_startdate($twentiethmonday->format('Ymd\T000000'), 'US/Eastern');
|
||||
|
||||
$interval = new DateInterval('P1Y');
|
||||
|
||||
@@ -2183,7 +2179,6 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
$expecteddate->modify('January 1');
|
||||
$expecteddate->add($interval);
|
||||
$expecteddate->modify("+20 Monday");
|
||||
$expecteddate->add($offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,10 +131,8 @@ Feature: Toggle activities visibility from the course page
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
And the following config values are set as admin:
|
||||
| allowstealth | 1 |
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add the "Recent activity" block
|
||||
|
||||
@@ -42,9 +42,9 @@ Feature: Activity navigation
|
||||
| wiki | Wiki 1 | Test wiki description | C1 | wiki1 | 6 |
|
||||
| workshop | Workshop 1 | Test workshop description | C1 | workshop1 | 6 |
|
||||
| assign | Assignment 1 | Test assignment description | C2 | assign21 | 0 |
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
And the following config values are set as admin:
|
||||
| allowstealth | 1 |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
# Stealth activity.
|
||||
And I click on "Hide" "link" in the "Forum 1" activity
|
||||
|
||||
@@ -11,16 +11,16 @@ Feature: Add activities to courses
|
||||
| student2 | Student | 2 | student2@example.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | format |
|
||||
| Course 1 | C1 | topics |
|
||||
| Course 1 | Course 1 | topics |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
And I log in as "admin"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
| student1 | Course 1 | student |
|
||||
| student2 | Course 1 | student |
|
||||
|
||||
@javascript
|
||||
Scenario: Add an activity to a course
|
||||
Given I am on the "Course 1" Course page logged in as admin
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
When I add a "Database" to section "3" and I fill the form with:
|
||||
| Name | Test name |
|
||||
| Description | Test database description |
|
||||
@@ -38,15 +38,18 @@ Feature: Add activities to courses
|
||||
|
||||
@javascript
|
||||
Scenario: Add an activity supplying only the name
|
||||
Given I am on the "Course 1" Course page logged in as admin
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
When I add a "Database" to section "3" and I fill the form with:
|
||||
| Name | Test name |
|
||||
Then I should see "Test name"
|
||||
|
||||
@javascript
|
||||
Scenario: Set activity description to required then add an activity supplying only the name
|
||||
Given I set the following administration settings values:
|
||||
| requiremodintro | Yes |
|
||||
When I am on "Course 1" course homepage
|
||||
Given the following config values are set as admin:
|
||||
| requiremodintro | 1 |
|
||||
And I am on the "Course 1" Course page logged in as admin
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Database" to section "3" and I fill the form with:
|
||||
| Name | Test name |
|
||||
Then I should see "Required"
|
||||
|
||||
@@ -6,9 +6,9 @@ Feature: Courses can be set up to display dates relative to the user's enrolment
|
||||
|
||||
@javascript
|
||||
Scenario: Create a course with relative dates feature disabled
|
||||
Given I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
Given the following config values are set as admin:
|
||||
| enablecourserelativedates | 0 |
|
||||
And I log in as "admin"
|
||||
And I am on site homepage
|
||||
And I turn editing mode on
|
||||
When I press "Add a new course"
|
||||
@@ -18,20 +18,19 @@ Feature: Courses can be set up to display dates relative to the user's enrolment
|
||||
|
||||
@javascript
|
||||
Scenario: Create a course with relative dates feature enabled
|
||||
Given I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
Given the following config values are set as admin:
|
||||
| enablecourserelativedates | 1 |
|
||||
And I log in as "admin"
|
||||
And I am on site homepage
|
||||
And I turn editing mode on
|
||||
When I press "Add a new course"
|
||||
And I wait until the page is ready
|
||||
Then I should see "Relative dates mode"
|
||||
And I should see "Relative dates mode cannot be changed once the course has been created."
|
||||
|
||||
Scenario: Edit courses with relative dates feature enabled
|
||||
Given I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
Given the following config values are set as admin:
|
||||
| enablecourserelativedates | 1 |
|
||||
And I log in as "admin"
|
||||
And I create a course with:
|
||||
| Course full name | Course 1 |
|
||||
| Course short name | C1 |
|
||||
|
||||
@@ -13,11 +13,11 @@ Feature: Users can request and approve courses
|
||||
|
||||
Scenario: Simple course request workflow
|
||||
Given the following "system role assigns" exist:
|
||||
| user | course | role |
|
||||
| user | course | role |
|
||||
| user2 | Acceptance test site | manager |
|
||||
Given I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
And the following config values are set as admin:
|
||||
| lockrequestcategory | 1 |
|
||||
Given I log in as "admin"
|
||||
And I set the following system permissions of "Authenticated user" role:
|
||||
| capability | permission |
|
||||
| moodle/course:request | Allow |
|
||||
|
||||
+13
-10
@@ -47,23 +47,23 @@
|
||||
* @param int $fontsize the font size
|
||||
* @return string the latex document
|
||||
*/
|
||||
function construct_latex_document( $formula, $fontsize=12 ) {
|
||||
global $CFG;
|
||||
|
||||
$formula = filter_tex_sanitize_formula($formula);
|
||||
|
||||
function construct_latex_document($formula, $fontsize = 12) {
|
||||
// $fontsize don't affects to formula's size. $density can change size
|
||||
$doc = "\\documentclass[{$fontsize}pt]{article}\n";
|
||||
$doc = "\\documentclass[{$fontsize}pt]{article}\n";
|
||||
$doc .= get_config('filter_tex', 'latexpreamble');
|
||||
$doc .= "\\pagestyle{empty}\n";
|
||||
$doc .= "\\begin{document}\n";
|
||||
//dlnsk $doc .= "$ {$formula} $\n";
|
||||
if (preg_match("/^[[:space:]]*\\\\begin\\{(gather|align|alignat|multline).?\\}/i",$formula)) {
|
||||
if (preg_match("/^[[:space:]]*\\\\begin\\{(gather|align|alignat|multline).?\\}/i", $formula)) {
|
||||
$doc .= "$formula\n";
|
||||
} else {
|
||||
$doc .= "$ {$formula} $\n";
|
||||
}
|
||||
$doc .= "\\end{document}\n";
|
||||
|
||||
// Sanitize the whole document (rather than just the formula) to make sure no one can bypass sanitization
|
||||
// by using \newcommand in preamble to give an alias to a blocked command.
|
||||
$doc = filter_tex_sanitize_formula($doc);
|
||||
|
||||
return $doc;
|
||||
}
|
||||
|
||||
@@ -114,11 +114,14 @@
|
||||
$convertformat = 'png';
|
||||
}
|
||||
$filename = str_replace(".{$convertformat}", '', $filename);
|
||||
$tex = "{$this->temp_dir}/$filename.tex";
|
||||
$tex = "$filename.tex"; // Absolute paths won't work with openin_any = p setting.
|
||||
$dvi = "{$this->temp_dir}/$filename.dvi";
|
||||
$ps = "{$this->temp_dir}/$filename.ps";
|
||||
$img = "{$this->temp_dir}/$filename.{$convertformat}";
|
||||
|
||||
// Change directory to temp dir so that we can work with relative paths.
|
||||
chdir($this->temp_dir);
|
||||
|
||||
// turn the latex doc into a .tex file in the temp area
|
||||
$fh = fopen( $tex, 'w' );
|
||||
fputs( $fh, $doc );
|
||||
@@ -126,7 +129,7 @@
|
||||
|
||||
// run latex on document
|
||||
$command = "$pathlatex --interaction=nonstopmode --halt-on-error $tex";
|
||||
chdir( $this->temp_dir );
|
||||
|
||||
if ($this->execute($command, $log)) { // It allways False on Windows
|
||||
// return false;
|
||||
}
|
||||
|
||||
+30
-2
@@ -61,7 +61,7 @@ function filter_tex_get_executable($debug=false) {
|
||||
|
||||
function filter_tex_sanitize_formula($texexp) {
|
||||
/// Check $texexp against blacklist (whitelisting could be more complete but also harder to maintain)
|
||||
$tex_blacklist = array(
|
||||
$denylist = [
|
||||
'include','command','loop','repeat','open','toks','output',
|
||||
'input','catcode','name','^^',
|
||||
'\def','\edef','\gdef','\xdef',
|
||||
@@ -71,9 +71,37 @@ function filter_tex_sanitize_formula($texexp) {
|
||||
'\afterassignment','\expandafter','\noexpand','\special',
|
||||
'\let', '\futurelet','\else','\fi','\chardef','\makeatletter','\afterground',
|
||||
'\noexpand','\line','\mathcode','\item','\section','\mbox','\declarerobustcommand'
|
||||
];
|
||||
|
||||
$allowlist = ['inputenc'];
|
||||
|
||||
// Prepare the denylist for regular expression.
|
||||
$denylist = array_map(function($value){
|
||||
return '/' . preg_quote($value, '/') . '/i';
|
||||
}, $denylist);
|
||||
|
||||
// Prepare the allowlist for regular expression.
|
||||
$allowlist = array_map(function($value){
|
||||
return '/\bforbiddenkeyword_(' . preg_quote($value, '/') . ')\b/i';
|
||||
}, $allowlist);
|
||||
|
||||
// First, mangle all denied words.
|
||||
$texexp = preg_replace_callback($denylist,
|
||||
function($matches) {
|
||||
return 'forbiddenkeyword_' . $matches[0];
|
||||
},
|
||||
$texexp
|
||||
);
|
||||
|
||||
return str_ireplace($tex_blacklist, 'forbiddenkeyword', $texexp);
|
||||
// Then, change back the allowed words.
|
||||
$texexp = preg_replace_callback($allowlist,
|
||||
function($matches) {
|
||||
return $matches[1];
|
||||
},
|
||||
$texexp
|
||||
);
|
||||
|
||||
return $texexp;
|
||||
}
|
||||
|
||||
function filter_tex_get_cmd($pathname, $texexp) {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Tex filter library functions tests
|
||||
*
|
||||
* @package filter_tex
|
||||
* @category test
|
||||
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace filter_tex;
|
||||
|
||||
use advanced_testcase;
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/filter/tex/lib.php');
|
||||
|
||||
/**
|
||||
* Tex filter library functions tests
|
||||
*
|
||||
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class lib_test extends advanced_testcase {
|
||||
/**
|
||||
* Data provider for test_filter_tex_sanitize_formula.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filter_tex_sanitize_formula_provider() : array {
|
||||
return [
|
||||
['x\ =\ \frac{\sqrt{144}}{2}\ \times\ (y\ +\ 12)', 'x\ =\ \frac{\sqrt{144}}{2}\ \times\ (y\ +\ 12)'],
|
||||
['\usepackage[latin1]{inputenc}', '\usepackage[latin1]{inputenc}'],
|
||||
['\newcommand{\A}{\verbatiminput}', '\newforbiddenkeyword_command{\A}{\verbatimforbiddenkeyword_input}'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for filter_tex_sanitize_formula() function.
|
||||
*
|
||||
* @dataProvider filter_tex_sanitize_formula_provider
|
||||
* @param $formula The formula to test
|
||||
* @param $expected The sanitized version of the formula we expect to get
|
||||
*/
|
||||
public function test_filter_tex_sanitize_formula(string $formula, string $expected) {
|
||||
$this->assertEquals($expected, filter_tex_sanitize_formula($formula));
|
||||
}
|
||||
}
|
||||
@@ -238,12 +238,15 @@
|
||||
$output .= "<p>base filename for expression is '$md5'</p>\n";
|
||||
|
||||
// temporary paths
|
||||
$tex = "$latex->temp_dir/$md5.tex";
|
||||
$tex = "$md5.tex"; // Absolute paths won't work with openin_any = p setting.
|
||||
$dvi = "$latex->temp_dir/$md5.dvi";
|
||||
$ps = "$latex->temp_dir/$md5.ps";
|
||||
$convertformat = get_config('filter_tex', 'convertformat');
|
||||
$img = "$latex->temp_dir/$md5.{$convertformat}";
|
||||
|
||||
// Change directory to temp dir so that we can work with relative paths.
|
||||
chdir($latex->temp_dir);
|
||||
|
||||
// put the expression as a file into the temp area
|
||||
$expression = html_entity_decode($expression);
|
||||
$output .= "<p>Processing TeX expression:</p><pre>$expression</pre>\n";
|
||||
@@ -252,9 +255,6 @@
|
||||
fputs($fh, $doc);
|
||||
fclose($fh);
|
||||
|
||||
// cd to temp dir
|
||||
chdir($latex->temp_dir);
|
||||
|
||||
// step 1: latex command
|
||||
$pathlatex = escapeshellarg($pathlatex);
|
||||
$cmd = "$pathlatex --interaction=nonstopmode --halt-on-error $tex";
|
||||
|
||||
@@ -85,20 +85,16 @@ $string['pathsunsecuredataroot'] = 'La ubicación de dataroot no es segura';
|
||||
$string['pathswrongadmindir'] = 'El directorio admin no existe';
|
||||
$string['phpextension'] = 'Extensión PHP {$a}';
|
||||
$string['phpversion'] = 'Versión PHP';
|
||||
$string['phpversionhelp'] = '<p>Moodle requiere al menos una versión de PHP 4.3.0 o 5.1.0 ((5.0.x tiene una serie de problemas conocidos).</p>
|
||||
<p>En este momento está ejecutando la versión {$a}</p>
|
||||
<p>¡Debe actualizar PHP o trasladarse a otro servidor con una versión más reciente de PHP!<br />
|
||||
(En caso de 5.0.x podría también revertir a la versión 4.4.x)</p>';
|
||||
$string['phpversionhelp'] = '<p>Moodle requiere una versión de PHP que sea al menos la 5.6.5 o 7.1 (7.0.x tiene algunas limitaciones del motor).</p>
|
||||
<p>En este momento está ejecutando la versión {$a}.</p>
|
||||
<p>Debe actualizar PHP o trasladarse a otro servidor con una versión más reciente de PHP.</p>';
|
||||
$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
|
||||
$string['welcomep20'] = 'Si está viendo esta página es porque ha podido ejecutar el paquete <strong>{$a->packname} {$a->packversion}</strong> satisfactoriamente en su ordenador. ¡Enhorabuena!';
|
||||
$string['welcomep30'] = 'Esta versión de <strong>{$a->installername}</strong> incluye las
|
||||
aplicaciones necesarias para que <strong>Moodle</strong> funcione en su ordenador,
|
||||
principalmente:';
|
||||
$string['welcomep40'] = 'El paquete también incluye <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>.';
|
||||
$string['welcomep50'] = 'El uso de todas las aplicaciones del paquete está gobernado por sus respectivas
|
||||
licencias. El programa <strong>{$a->installername}</strong> es
|
||||
<a href="http://www.opensource.org/docs/definition_plain.html">código abierto</a> y se distribuye
|
||||
bajo licencia <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a>.';
|
||||
$string['welcomep50'] = 'El uso de todas las aplicaciones del paquete está supeditado a sus respectivas licencias. El programa <strong>{$a->installername}</strong> es <a href="http://www.opensource.org/docs/definition_plain.html">de código abierto</a> y se distribuye bajo licencia <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a>.';
|
||||
$string['welcomep60'] = 'Las siguientes páginas le guiarán a través de algunos sencillos pasos para configurar y ajustar <strong>Moodle</strong> en su ordenador. Puede utilizar los valores por defecto sugeridos o, de forma opcional, modificarlos para que se ajusten a sus necesidades.';
|
||||
$string['welcomep70'] = 'Pulse en el botón "Siguiente" para continuar con la configuración de <strong>Moodle</strong>.';
|
||||
$string['wwwroot'] = 'Dirección Web';
|
||||
|
||||
@@ -39,3 +39,4 @@ $string['cliunknowoption'] = 'Tunnistamaton valinta: {$a} Ole hyvä ja käytä -
|
||||
$string['cliyesnoprompt'] = 'syötä k (kyllä) tai e (ei)';
|
||||
$string['environmentrequireinstall'] = 'pitää olla asennettuna ja käytössä';
|
||||
$string['environmentrequireversion'] = 'versio {$a->needed} vaaditaan ja käytössä on versio {$a->current}';
|
||||
$string['upgradekeyset'] = 'Päivitysavain (jätä tyhjäksi, jos et halua asettaa sitä)';
|
||||
|
||||
@@ -70,7 +70,7 @@ $string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
|
||||
$string['welcomep20'] = 'Anda melihat halaman ini karena Anda telah berhasil memasang dan meluncurkan paket <strong> {$a->packname} {$a->packversion}</strong> di komputer Anda. Selamat!';
|
||||
$string['welcomep30'] = 'Rilis <strong> {$a->installername}</strong> ini mencakup aplikasi untuk menciptakan lingkungan tempat <strong> Moodle </strong> yang akan digunakan, yaitu:';
|
||||
$string['welcomep40'] = 'Paket juga termasuk <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>.';
|
||||
$string['welcomep50'] = 'Penggunaan semua aplikasi dalam paket ini diatur oleh lisensi masing-masing. Paket <strong>{$a->installername}</strong> lengkap adalah <a href="http://www.opensource.org/docs/definition_plain.html"> sumber terbuka </a> dan didistribusikan di bawah lisensi <a href="http://www.gnu.org/copyleft/gpl.html"> GPL </a>.';
|
||||
$string['welcomep50'] = 'Penggunaan semua aplikasi dalam paket ini diatur oleh lisensi masing-masing. Paket lengkap <strong>{$a->installername}</strong> adalah <a href="https://www.opensource.org/docs/definition_plain.html"> open source </a> dan didistribusikan di bawah lisensi <a href="https://www.gnu.org/copyleft/gpl.html">GPL</a>.';
|
||||
$string['welcomep60'] = 'Halaman berikut akan menuntun Anda melalui beberapa langkah yang mudah diikuti untuk mengonfigurasi dan menyiapkan <strong> Moodle </strong> di komputer Anda. Anda dapat menerima pengaturan bawaan atau, secara opsional, mengubahnya sesuai dengan kebutuhan Anda.';
|
||||
$string['welcomep70'] = 'Klik tombol "Selanjutnya" di bawah untuk melanjutkan instalasi <strong> Moodle </strong>.';
|
||||
$string['wwwroot'] = 'Alamat web';
|
||||
|
||||
@@ -37,6 +37,6 @@ $string['clitypevalue'] = 'introduza valor';
|
||||
$string['clitypevaluedefault'] = 'introduza valor, pressione a tecla \'Enter\' para usar o valor predefinido ({$a})';
|
||||
$string['cliunknowoption'] = 'Opções desconhecidas: {$a}. Por favor, use a opção --help';
|
||||
$string['cliyesnoprompt'] = 'digite s (para sim) ou n (para não)';
|
||||
$string['environmentrequireinstall'] = 'deve estar instalada e ativa';
|
||||
$string['environmentrequireversion'] = 'é requerida a versão {$a->needed} e está a correr a versão {$a->current}';
|
||||
$string['environmentrequireinstall'] = 'tem de estar instalada e ativa';
|
||||
$string['environmentrequireversion'] = 'é requerida a versão {$a->needed} e está a usar a versão {$a->current}';
|
||||
$string['upgradekeyset'] = 'Chave de atualização (deixe em branco caso não pretenda definir)';
|
||||
|
||||
@@ -54,7 +54,7 @@ $string['pathserrcreatedataroot'] = 'O programa de instalação não conseguiu c
|
||||
$string['pathshead'] = 'Confirmar caminhos';
|
||||
$string['pathsrodataroot'] = 'A pasta de dados não tem permissões de escrita.';
|
||||
$string['pathsroparentdataroot'] = 'A pasta ascendente <b>{$a->parent}</b> não tem permissões de escrita. O programa de instalação não conseguiu criar a pasta <b>{$a->dataroot}</b>.';
|
||||
$string['pathssubadmindir'] = 'Alguns servidores Web utilizam a pasta <strong>admin</strong> em URLs especiais de acesso a funcionalidades especiais, como é o caso de painéis de controlo. Algumas situações podem criar conflitos com a localização normal das páginas de administração do Moodle. Estes problemas podem ser resolvidos renomeando a pasta <strong>admin</strong> na instalação do Moodle e indicando aqui o novo nome a utilizar. Por exemplo:<br /><br /><b>moodleadmin</b><br /><br />Esta ação resolverá os problemas de acesso das hiperligações para as funcionalidades de administração do Moodle.';
|
||||
$string['pathssubadmindir'] = 'Alguns servidores Web utilizam a pasta <strong>admin</strong> em URLs especiais de acesso a funcionalidades especiais, como é o caso de painéis de controlo. Algumas situações podem criar conflitos com a localização normal das páginas de administração do Moodle. Estes problemas podem ser resolvidos renomeando a pasta <strong>admin</strong> na instalação do Moodle e indicando aqui o novo nome a utilizar. Exemplo:<br /><br /><b>moodleadmin</b><br /><br />Esta ação resolverá os problemas de acesso das hiperligações para as funcionalidades de administração do Moodle.';
|
||||
$string['pathssubdataroot'] = '<p>Pasta onde o Moodle irá armazenar todo o conteúdo de ficheiros enviados pelos utilizadores.</p>
|
||||
<p>Esta pasta deve ser legível e gravável pelo utilizador do servidor web (geralmente \'www-data\', \'nobody\', ou \'apache\').</p>
|
||||
<p>Não deve ser acessível diretamente através da web.</p>
|
||||
|
||||
@@ -33,6 +33,7 @@ $string['admindirname'] = 'Skrbniška mapa';
|
||||
$string['availablelangs'] = 'Jezikovni paketi na voljo';
|
||||
$string['chooselanguagehead'] = 'Izberite jezik';
|
||||
$string['chooselanguagesub'] = 'Izberite jezik za namestitev. Pozneje boste lahko izbrali tudi jezike strani in uporabniške jezike.';
|
||||
$string['clialreadyconfigured'] = 'Konfiguracijska datoteka config.php že obstaja. Uporabite admin/cli/install_database.php, če želite namestiti Moodle na to spletno mesto.';
|
||||
$string['clialreadyinstalled'] = 'Datoteka config.php že obstaja, prosimo uporabite admin/cli/install_database.php, če želite posodobiti vašo stran.';
|
||||
$string['cliinstallheader'] = 'Moodle {$a} namestitveni program z ukazno vrstico';
|
||||
$string['databasehost'] = 'Gostitelj podatkovne baze';
|
||||
|
||||
@@ -34,7 +34,7 @@ $string['availablelangs'] = 'Tillgängliga språkpaket';
|
||||
$string['chooselanguagehead'] = 'Välj ett språk';
|
||||
$string['chooselanguagesub'] = 'Vänligen välj ett språk för installationen. Du kommer att ha möjlighet att välja språk för webbplatsen och användarna på en senare skärm.';
|
||||
$string['clialreadyconfigured'] = 'Filen <em>config.php</em> finns redan. Använd <code>admin/cli/install_database.php</code> för att installera Moodle på denna server.';
|
||||
$string['clialreadyinstalled'] = 'Filen config.php finns redan. Vänligen använd admin/cli/upgrade.php om Du vill uppgradera Din webbplats.';
|
||||
$string['clialreadyinstalled'] = 'Filen <code>config.php</code> finns redan. Vänligen använd <code>admin/cli/upgrade.php</code> om du vill uppgradera Moodle på den här webbplatsen.';
|
||||
$string['cliinstallheader'] = 'Kommandoradsbaserat installationsprogram för Moodle {$a}';
|
||||
$string['databasehost'] = 'Databasserver';
|
||||
$string['databasename'] = 'Namn på databas';
|
||||
|
||||
@@ -659,6 +659,7 @@ $string['checkboxyes'] = 'Yes';
|
||||
$string['choosefiletoedit'] = 'Choose file to edit';
|
||||
$string['h5pgetcontenttypestask'] = 'Download available H5P content types from h5p.org';
|
||||
$string['iconvrequired'] = 'Installing ICONV extension is required.';
|
||||
$string['igbinary322buggyversion'] = 'The php-igbinary extension installed on the site can lead to problems when running with PHP 7.2. You are recommended to either upgrade to php-igbinary 3.2.5 or later, or alternatively to upgrade to PHP 7.3 or later.';
|
||||
$string['ignore'] = 'Ignore';
|
||||
$string['includemoduleuserdata'] = 'Include module user data';
|
||||
$string['incompatibleblocks'] = 'Incompatible blocks';
|
||||
|
||||
@@ -549,7 +549,7 @@ abstract class behat_generator_base {
|
||||
$cmtable = new \core\dml\table('course_modules', 'cm', 'cm');
|
||||
$cmfrom = $cmtable->get_from_sql();
|
||||
|
||||
$acttable = new \core\dml\table($activity, 'act', 'act');
|
||||
$acttable = new \core\dml\table($activity, 'a', 'a');
|
||||
$actselect = $acttable->get_field_select();
|
||||
$actfrom = $acttable->get_from_sql();
|
||||
|
||||
@@ -558,8 +558,8 @@ abstract class behat_generator_base {
|
||||
FROM {$cmfrom}
|
||||
INNER JOIN {$coursefrom} ON c.id = cm.course
|
||||
INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
INNER JOIN {$actfrom} ON cm.instance = act.id
|
||||
WHERE cm.idnumber = :idnumber OR act.name = :name
|
||||
INNER JOIN {$actfrom} ON cm.instance = a.id
|
||||
WHERE cm.idnumber = :idnumber OR a.name = :name
|
||||
EOF;
|
||||
|
||||
$result = $DB->get_record_sql($sql, [
|
||||
|
||||
@@ -1546,7 +1546,7 @@ EOF;
|
||||
$cmtable = new \core\dml\table('course_modules', 'cm', 'cm');
|
||||
$cmfrom = $cmtable->get_from_sql();
|
||||
|
||||
$acttable = new \core\dml\table($activity, 'act', 'act');
|
||||
$acttable = new \core\dml\table($activity, 'a', 'a');
|
||||
$actselect = $acttable->get_field_select();
|
||||
$actfrom = $acttable->get_from_sql();
|
||||
|
||||
@@ -1555,8 +1555,8 @@ EOF;
|
||||
FROM {$cmfrom}
|
||||
INNER JOIN {$coursefrom} ON c.id = cm.course
|
||||
INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
INNER JOIN {$actfrom} ON cm.instance = act.id
|
||||
WHERE cm.idnumber = :idnumber OR act.name = :name
|
||||
INNER JOIN {$actfrom} ON cm.instance = a.id
|
||||
WHERE cm.idnumber = :idnumber OR a.name = :name
|
||||
EOF;
|
||||
|
||||
$result = $DB->get_record_sql($sql, [
|
||||
|
||||
@@ -655,6 +655,7 @@ $capabilities = array(
|
||||
)
|
||||
),
|
||||
|
||||
// The ability to override the permissions for any capability.
|
||||
'moodle/role:override' => array(
|
||||
|
||||
'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS,
|
||||
@@ -666,6 +667,8 @@ $capabilities = array(
|
||||
)
|
||||
),
|
||||
|
||||
// The ability to override the permissions for 'safe' capabilities (those without risks).
|
||||
// If a user has moodle/role:override then you should not check this capability.
|
||||
'moodle/role:safeoverride' => array(
|
||||
|
||||
'riskbitmask' => RISK_SPAM,
|
||||
|
||||
@@ -1717,7 +1717,7 @@ EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Press a named key with an optional set of modifiers.
|
||||
* Press a named or character key with an optional set of modifiers.
|
||||
*
|
||||
* Supported named keys are:
|
||||
* - up
|
||||
@@ -1735,6 +1735,8 @@ EOF;
|
||||
* - enter
|
||||
* - tab
|
||||
*
|
||||
* You can also use a single character for the key name e.g. 'Ctrl C'.
|
||||
*
|
||||
* Supported moderators are:
|
||||
* - shift
|
||||
* - ctrl
|
||||
@@ -1830,7 +1832,12 @@ EOF;
|
||||
$keys[] = behat_keys::SPACE;
|
||||
break;
|
||||
default:
|
||||
throw new \coding_exception("Unknown key '$key'}");
|
||||
// You can enter a single ASCII character (e.g. a letter) to directly type that key.
|
||||
if (strlen($key) === 1) {
|
||||
$keys[] = strtolower($key);
|
||||
} else {
|
||||
throw new \coding_exception("Unknown key '$key'}");
|
||||
}
|
||||
}
|
||||
|
||||
behat_base::type_keys($this->getSession(), $keys);
|
||||
|
||||
@@ -2422,6 +2422,59 @@ function check_sixtyfour_bits(environment_results $result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the igbinary extension installed is buggy one
|
||||
*
|
||||
* There are a few php-igbinary versions that are buggy and
|
||||
* return any unserialised array with wrong index. This defeats
|
||||
* key() and next() operations on them.
|
||||
*
|
||||
* This library is used by MUC and also by memcached and redis
|
||||
* when available.
|
||||
*
|
||||
* Let's inform if there is some problem when:
|
||||
* - php 7.2 is being used (php 7.3 and up are immune).
|
||||
* - the igbinary extension is installed.
|
||||
* - the version of the extension is between 3.2.2 and 3.2.4.
|
||||
* - the buggy behaviour is reproduced.
|
||||
*
|
||||
* @param environment_results $result object to update, if relevant.
|
||||
* @return environment_results|null updated results or null.
|
||||
*/
|
||||
function check_igbinary322_version(environment_results $result) {
|
||||
|
||||
// No problem if using PHP version 7.3 and up.
|
||||
$phpversion = normalize_version(phpversion());
|
||||
if (version_compare($phpversion, '7.3', '>=')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// No problem if igbinary is not installed..
|
||||
if (!function_exists('igbinary_serialize')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// No problem if using igbinary < 3.2.2 or > 3.2.4.
|
||||
$igbinaryversion = normalize_version(phpversion('igbinary'));
|
||||
if (version_compare($igbinaryversion, '3.2.2', '<') or version_compare($igbinaryversion, '3.2.4', '>')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Let's verify the real behaviour to see if the bug is around.
|
||||
// Note that we need this extra check because they released 3.2.5 with 3.2.4 version number, so
|
||||
// over the paper, there are 3.2.4 working versions (3.2.5 ones with messed reflection version).
|
||||
$data = [1, 2, 3];
|
||||
$data = igbinary_unserialize(igbinary_serialize($data));
|
||||
if (key($data) === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Arrived here, we are using PHP 7.2 and a buggy verified igbinary version, let's inform and don't allow to continue.
|
||||
$result->setInfo('igbinary version problem');
|
||||
$result->setStatus(false);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the upgrade key is provided, if it is defined.
|
||||
*
|
||||
|
||||
@@ -110,10 +110,8 @@ Feature: Anonymous feedback
|
||||
And I log out
|
||||
|
||||
Scenario: Complete fully anonymous feedback on the front page as a guest
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
Given the following config values are set as admin:
|
||||
| feedback_allowfullanonymous | 1 |
|
||||
And I log out
|
||||
When I follow "Site feedback"
|
||||
And I follow "Preview"
|
||||
And I should see "Do you like our site?"
|
||||
@@ -128,9 +126,9 @@ Feature: Anonymous feedback
|
||||
|
||||
@javascript
|
||||
Scenario: Complete fully anonymous feedback and view analyze on the front page as a guest
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
Given the following config values are set as admin:
|
||||
| feedback_allowfullanonymous | 1 |
|
||||
And I log in as "admin"
|
||||
And I set the following system permissions of "Guest" role:
|
||||
| capability | permission |
|
||||
| mod/feedback:viewanalysepage | Allow |
|
||||
|
||||
@@ -14,10 +14,10 @@ Feature: View fill the blanks attempt report
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
And I log in as "admin"
|
||||
# No HTML should appear even with formatstringstriptags disabled.
|
||||
And I set the following administration settings values:
|
||||
And the following config values are set as admin:
|
||||
# No HTML should appear even with formatstringstriptags disabled.
|
||||
| formatstringstriptags | 0 |
|
||||
And I log in as "admin"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "H5P" to section "1"
|
||||
And I set the following fields to these values:
|
||||
|
||||
@@ -14,10 +14,10 @@ Feature: View essay attempt report
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
And I log in as "admin"
|
||||
# No HTML should appear even with formatstringstriptags disabled.
|
||||
And I set the following administration settings values:
|
||||
And the following config values are set as admin:
|
||||
# No HTML should appear even with formatstringstriptags disabled.
|
||||
| formatstringstriptags | 0 |
|
||||
And I log in as "admin"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "H5P" to section "1"
|
||||
And I set the following fields to these values:
|
||||
|
||||
@@ -413,10 +413,21 @@ class mod_quiz_external extends external_api {
|
||||
require_capability('mod/quiz:viewreports', $context);
|
||||
}
|
||||
|
||||
// Update quiz with override information.
|
||||
$quiz = quiz_update_effective_access($quiz, $params['userid']);
|
||||
$attempts = quiz_get_user_attempts($quiz->id, $user->id, $params['status'], $params['includepreviews']);
|
||||
|
||||
$attemptresponse = [];
|
||||
foreach ($attempts as $attempt) {
|
||||
$reviewoptions = quiz_get_review_options($quiz, $attempt, $context);
|
||||
if (!has_capability('mod/quiz:viewreports', $context) &&
|
||||
($reviewoptions->marks < question_display_options::MARK_AND_MAX || $attempt->state != quiz_attempt::FINISHED)) {
|
||||
// Blank the mark if the teacher does not allow it.
|
||||
$attempt->sumgrades = null;
|
||||
}
|
||||
$attemptresponse[] = $attempt;
|
||||
}
|
||||
$result = array();
|
||||
$result['attempts'] = $attempts;
|
||||
$result['attempts'] = $attemptresponse;
|
||||
$result['warnings'] = $warnings;
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -109,15 +109,19 @@ class mod_quiz_external_testcase extends externallib_advanced_testcase {
|
||||
* @param boolean $startattempt whether to start a new attempt
|
||||
* @param boolean $finishattempt whether to finish the new attempt
|
||||
* @param string $behaviour the quiz preferredbehaviour, defaults to 'deferredfeedback'.
|
||||
* @param boolean $includeqattachments whether to include a question that supports attachments, defaults to false.
|
||||
* @param array $extraoptions extra options for Quiz.
|
||||
* @return array array containing the quiz, context and the attempt
|
||||
*/
|
||||
private function create_quiz_with_questions($startattempt = false, $finishattempt = false, $behaviour = 'deferredfeedback') {
|
||||
private function create_quiz_with_questions($startattempt = false, $finishattempt = false, $behaviour = 'deferredfeedback',
|
||||
$includeqattachments = false, $extraoptions = []) {
|
||||
|
||||
// Create a new quiz with attempts.
|
||||
$quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
|
||||
$data = array('course' => $this->course->id,
|
||||
'sumgrades' => 2,
|
||||
'preferredbehaviour' => $behaviour);
|
||||
$data = array_merge($data, $extraoptions);
|
||||
$quiz = $quizgenerator->create_instance($data);
|
||||
$context = context_module::instance($quiz->cmid);
|
||||
|
||||
@@ -130,6 +134,12 @@ class mod_quiz_external_testcase extends externallib_advanced_testcase {
|
||||
$question = $questiongenerator->create_question('numerical', null, array('category' => $cat->id));
|
||||
quiz_add_quiz_question($question->id, $quiz);
|
||||
|
||||
if ($includeqattachments) {
|
||||
$question = $questiongenerator->create_question('essay', null, array('category' => $cat->id, 'attachments' => 1,
|
||||
'attachmentsrequired' => 1));
|
||||
quiz_add_quiz_question($question->id, $quiz);
|
||||
}
|
||||
|
||||
$quizobj = quiz::create($quiz->id, $this->student->id);
|
||||
|
||||
// Set grade to pass.
|
||||
@@ -387,6 +397,8 @@ class mod_quiz_external_testcase extends externallib_advanced_testcase {
|
||||
$this->assertEquals($quiz->id, $result['attempts'][0]['quiz']);
|
||||
$this->assertEquals($this->student->id, $result['attempts'][0]['userid']);
|
||||
$this->assertEquals(1, $result['attempts'][0]['attempt']);
|
||||
$this->assertArrayHasKey('sumgrades', $result['attempts'][0]);
|
||||
$this->assertEquals(1.0, $result['attempts'][0]['sumgrades']);
|
||||
|
||||
// Test filters. Only finished.
|
||||
$result = mod_quiz_external::get_user_attempts($quiz->id, 0, 'finished', false);
|
||||
@@ -452,6 +464,42 @@ class mod_quiz_external_testcase extends externallib_advanced_testcase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_user_attempts with marks hidden
|
||||
*/
|
||||
public function test_get_user_attempts_with_marks_hidden() {
|
||||
// Create quiz with one attempt finished and hide the mark.
|
||||
list($quiz, $context, $quizobj, $attempt, $attemptobj) = $this->create_quiz_with_questions(
|
||||
true, true, 'deferredfeedback', false,
|
||||
['marksduring' => 0, 'marksimmediately' => 0, 'marksopen' => 0, 'marksclosed' => 0]);
|
||||
|
||||
// Student cannot see the grades.
|
||||
$this->setUser($this->student);
|
||||
$result = mod_quiz_external::get_user_attempts($quiz->id);
|
||||
$result = external_api::clean_returnvalue(mod_quiz_external::get_user_attempts_returns(), $result);
|
||||
|
||||
$this->assertCount(1, $result['attempts']);
|
||||
$this->assertEquals($attempt->id, $result['attempts'][0]['id']);
|
||||
$this->assertEquals($quiz->id, $result['attempts'][0]['quiz']);
|
||||
$this->assertEquals($this->student->id, $result['attempts'][0]['userid']);
|
||||
$this->assertEquals(1, $result['attempts'][0]['attempt']);
|
||||
$this->assertArrayHasKey('sumgrades', $result['attempts'][0]);
|
||||
$this->assertEquals(null, $result['attempts'][0]['sumgrades']);
|
||||
|
||||
// Test manager can see user grades.
|
||||
$this->setUser($this->teacher);
|
||||
$result = mod_quiz_external::get_user_attempts($quiz->id, $this->student->id);
|
||||
$result = external_api::clean_returnvalue(mod_quiz_external::get_user_attempts_returns(), $result);
|
||||
|
||||
$this->assertCount(1, $result['attempts']);
|
||||
$this->assertEquals($attempt->id, $result['attempts'][0]['id']);
|
||||
$this->assertEquals($quiz->id, $result['attempts'][0]['quiz']);
|
||||
$this->assertEquals($this->student->id, $result['attempts'][0]['userid']);
|
||||
$this->assertEquals(1, $result['attempts'][0]['attempt']);
|
||||
$this->assertArrayHasKey('sumgrades', $result['attempts'][0]);
|
||||
$this->assertEquals(1.0, $result['attempts'][0]['sumgrades']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_user_best_grade
|
||||
*/
|
||||
|
||||
Generated
+801
-549
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -19,9 +19,9 @@
|
||||
"eslint-plugin-babel": "5.3.0",
|
||||
"eslint-plugin-promise": "4.2.1",
|
||||
"fb-watchman": "2.0.1",
|
||||
"gherkin-lint": "4.0.1",
|
||||
"gherkin-lint": "^4.2.2",
|
||||
"glob": "7.1.6",
|
||||
"grunt": "1.1.0",
|
||||
"grunt": "^1.4.1",
|
||||
"grunt-babel": "8.0.0",
|
||||
"grunt-contrib-uglify": "4.0.1",
|
||||
"grunt-contrib-watch": "1.1.0",
|
||||
@@ -35,7 +35,7 @@
|
||||
"stylelint": "13.3.3",
|
||||
"stylelint-checkstyle-formatter": "0.1.2",
|
||||
"stylelint-csstree-validator": "1.8.0",
|
||||
"xmldom": "0.3.0",
|
||||
"xmldom": "^0.6.0",
|
||||
"xpath": "0.0.27"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -7,23 +7,24 @@ Feature: Use the particiaption report to message groups of students
|
||||
Background:
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | category | groupmode |
|
||||
| Course 1 | C1 | 0 | 1 |
|
||||
| Course 1 | C1 | 0 | 1 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
| student2 | Student | 2 | student2@example.com |
|
||||
| student3 | Student | 3 | student3@example.com |
|
||||
| username | firstname | lastname |
|
||||
| teacher1 | Teacher | 1 |
|
||||
| student1 | Student | 1 |
|
||||
| student2 | Student | 2 |
|
||||
| student3 | Student | 3 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
| student3 | C1 | student |
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
| student3 | C1 | student |
|
||||
And the following "activity" exists:
|
||||
| course | C1 |
|
||||
| activity | book |
|
||||
| name | Test book name |
|
||||
| idnumber | Test book name |
|
||||
| description | Test book |
|
||||
| idnumber | book1 |
|
||||
And I am on the "Test book name" "book activity" page logged in as student1
|
||||
|
||||
@@ -6,13 +6,8 @@ $drawer-padding-x: 20px !default;
|
||||
$drawer-padding-y: 20px !default;
|
||||
$drawer-offscreen-gutter: 20px !default;
|
||||
|
||||
:target::before {
|
||||
content: " ";
|
||||
display: block;
|
||||
height: ($fixed-header-y + 10px); /* fixed header height*/
|
||||
margin-top: -($fixed-header-y + 10px); /* negative fixed header height */
|
||||
width: 1px;
|
||||
pointer-events: none;
|
||||
:target {
|
||||
scroll-margin-top: $fixed-header-y + 10px;
|
||||
}
|
||||
|
||||
.pagelayout-embedded :target {
|
||||
|
||||
@@ -14031,15 +14031,8 @@ span.editinstructions {
|
||||
white-space: pre-wrap; }
|
||||
|
||||
/* Anchor link offset fix. This makes hash links scroll 60px down to account for the fixed header. */
|
||||
:target::before {
|
||||
content: " ";
|
||||
display: block;
|
||||
height: 60px;
|
||||
/* fixed header height*/
|
||||
margin-top: -60px;
|
||||
/* negative fixed header height */
|
||||
width: 1px;
|
||||
pointer-events: none; }
|
||||
:target {
|
||||
scroll-margin-top: 60px; }
|
||||
|
||||
.pagelayout-embedded :target {
|
||||
padding-top: initial;
|
||||
|
||||
@@ -14247,15 +14247,8 @@ span.editinstructions {
|
||||
white-space: pre-wrap; }
|
||||
|
||||
/* Anchor link offset fix. This makes hash links scroll 60px down to account for the fixed header. */
|
||||
:target::before {
|
||||
content: " ";
|
||||
display: block;
|
||||
height: 60px;
|
||||
/* fixed header height*/
|
||||
margin-top: -60px;
|
||||
/* negative fixed header height */
|
||||
width: 1px;
|
||||
pointer-events: none; }
|
||||
:target {
|
||||
scroll-margin-top: 60px; }
|
||||
|
||||
.pagelayout-embedded :target {
|
||||
padding-top: initial;
|
||||
|
||||
+22
-4
@@ -101,15 +101,33 @@ if ($formaction == 'bulkchange.php') {
|
||||
$identityfieldsselect .= ', u.' . $field . ' ';
|
||||
}
|
||||
|
||||
if (!empty($userids)) {
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($userids);
|
||||
// Ensure users are enrolled in this course context, further limiting them by selected userids.
|
||||
[$enrolledsql, $enrolledparams] = get_enrolled_sql($context);
|
||||
[$useridsql, $useridparams] = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'userid');
|
||||
|
||||
$params = array_merge($enrolledparams, $useridparams);
|
||||
|
||||
// If user can only view their own groups then they can only export users from those groups too.
|
||||
$groupmode = groups_get_course_groupmode($course);
|
||||
if ($groupmode == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $context)) {
|
||||
$groups = groups_get_all_groups($course->id, $USER->id, 0, 'g.id');
|
||||
$groupids = array_column($groups, 'id');
|
||||
|
||||
[$groupmembersql, $groupmemberparams] = groups_get_members_ids_sql($groupids, $context);
|
||||
$params = array_merge($params, $groupmemberparams);
|
||||
|
||||
$groupmemberjoin = "JOIN ({$groupmembersql}) jg ON jg.id = u.id";
|
||||
} else {
|
||||
$groupmemberjoin = '';
|
||||
}
|
||||
|
||||
$sql = "SELECT u.firstname, u.lastname" . $identityfieldsselect . "
|
||||
FROM {user} u
|
||||
WHERE u.id $insql";
|
||||
JOIN ({$enrolledsql}) je ON je.id = u.id
|
||||
{$groupmemberjoin}
|
||||
WHERE u.id {$useridsql}";
|
||||
|
||||
$rs = $DB->get_recordset_sql($sql, $inparams);
|
||||
$rs = $DB->get_recordset_sql($sql, $params);
|
||||
|
||||
// Provide callback to pre-process all records ensuring user identity fields are escaped if HTML supported.
|
||||
\core\dataformat::download_data(
|
||||
|
||||
@@ -51,11 +51,9 @@ Feature: The student can navigate to their grades page and user grade report.
|
||||
| Test assignment three | 0.00 %( Empty ) | - | 0–150 | - | 0.00 % |
|
||||
|
||||
Scenario: Change Grades settings to go to a custom url.
|
||||
When I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
| grade_mygrades_report | External URL |
|
||||
| gradereport_mygradeurl | /badges/mybadges.php |
|
||||
And I log out
|
||||
Given the following config values are set as admin:
|
||||
| grade_mygrades_report | external |
|
||||
| gradereport_mygradeurl | /badges/mybadges.php |
|
||||
And I log in as "student1"
|
||||
And I follow "Student 1"
|
||||
And I follow "Grades" in the user menu
|
||||
|
||||
+2
-2
@@ -29,9 +29,9 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2020061509.00; // 20200615 = branching date YYYYMMDD - do not modify!
|
||||
$version = 2020061510.00; // 20200615 = branching date YYYYMMDD - do not modify!
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
$release = '3.9.9 (Build: 20210729)'; // Human-friendly version name
|
||||
$release = '3.9.10 (Build: 20210913)'; // Human-friendly version name
|
||||
$branch = '39'; // This version's branch.
|
||||
$maturity = MATURITY_STABLE; // This version's maturity level.
|
||||
|
||||
Reference in New Issue
Block a user