Merge branch 'MOODLE_311_STABLE' into install_311_STABLE

This commit is contained in:
AMOS bot
2020-11-28 00:07:28 +00:00
76 changed files with 2281 additions and 2089 deletions
+71
View File
@@ -0,0 +1,71 @@
<?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/>.
/**
* Template configuraton file for github actions CI/CD.
*
* @package core
* @copyright 2020 onwards Eloy Lafuente (stronk7) {@link https://stronk7.com}
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// This cannot be used out from a github actions workflow, so just exit.
getenv('GITHUB_WORKFLOW') || die; // phpcs:ignore moodle.Files.MoodleInternal.MoodleInternalGlobalState
unset($CFG);
global $CFG;
$CFG = new stdClass();
$CFG->dbtype = getenv('dbtype');
$CFG->dblibrary = 'native';
$CFG->dbhost = '127.0.0.1';
$CFG->dbname = 'test';
$CFG->dbuser = 'test';
$CFG->dbpass = 'test';
$CFG->prefix = 'm_';
$CFG->dboptions = ['dbcollation' => 'utf8mb4_bin'];
$host = 'localhost';
$CFG->wwwroot = "http://{$host}";
$CFG->dataroot = realpath(dirname(__DIR__)) . '/moodledata';
$CFG->admin = 'admin';
$CFG->directorypermissions = 0777;
// Debug options - possible to be controlled by flag in future.
$CFG->debug = (E_ALL | E_STRICT); // DEBUG_DEVELOPER.
$CFG->debugdisplay = 1;
$CFG->debugstringids = 1; // Add strings=1 to url to get string ids.
$CFG->perfdebug = 15;
$CFG->debugpageinfo = 1;
$CFG->allowthemechangeonurl = 1;
$CFG->passwordpolicy = 0;
$CFG->cronclionly = 0;
$CFG->pathtophp = getenv('pathtophp');
$CFG->phpunit_dataroot = realpath(dirname(__DIR__)) . '/phpunitdata';
$CFG->phpunit_prefix = 't_';
define('TEST_EXTERNAL_FILES_HTTP_URL', 'http://localhost:8080');
define('TEST_EXTERNAL_FILES_HTTPS_URL', 'http://localhost:8080');
define('TEST_SESSION_REDIS_HOST', 'localhost');
define('TEST_CACHESTORE_REDIS_TESTSERVERS', 'localhost');
// TODO: add others (solr, mongodb, memcached, ldap...).
// Too much for now: define('PHPUNIT_LONGTEST', true); // Only leaves a few tests out and they are run later by CI.
require_once(__DIR__ . '/lib/setup.php');
+103
View File
@@ -0,0 +1,103 @@
name: Core
on: [push]
env:
php: 7.4
jobs:
Grunt:
runs-on: ubuntu-18.04
steps:
- name: Checking out code
uses: actions/checkout@v2
- name: Configuring node & npm
shell: bash -l {0}
run: nvm install
- name: Installing node stuff
run: npm install
- name: Running grunt
run: npx grunt
- name: Looking for uncommitted changes
# Add all files to the git index and then run diff --cached to see all changes.
# This ensures that we get the status of all files, including new files.
# We ignore npm-shrinkwrap.json to make the tasks immune to npm changes.
run: |
git add .
git reset -- npm-shrinkwrap.json
git diff --cached --exit-code
PHPUnit:
runs-on: ${{ matrix.os }}
services:
exttests:
image: moodlehq/moodle-exttests
ports:
- 8080:80
redis:
image: redis
ports:
- 6379:6379
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-18.04
php: 7.2
db: mysqli
- os: ubuntu-18.04
php: 7.4
db: pgsql
steps:
- name: Setting up DB mysql
if: ${{ matrix.db == 'mysqli' }}
uses: johanmeiring/mysql-action@tmpfs-patch
with:
collation server: utf8mb4_bin
mysql version: 5.7
mysql database: test
mysql user: test
mysql password: test
use tmpfs: true
- name: Setting up DB pgsql
if: ${{ matrix.db == 'pgsql' }}
uses: m4nu56/postgresql-action@v1
with:
postgresql version: 9.6
postgresql db: test
postgresql user: test
postgresql password: test
- name: Configuring git vars
uses: rlespinasse/[email protected]
- name: Setting up PHP ${{ matrix.php }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
- name: Checking out code from ${{ env.GITHUB_REF_SLUG }}
uses: actions/checkout@v2
- name: Setting up PHPUnit
env:
dbtype: ${{ matrix.db }}
run: |
echo "pathtophp=$(which php)" >> $GITHUB_ENV # Inject installed pathtophp to env. The template config needs it.
cp .github/workflows/config-template.php config.php
mkdir ../moodledata
sudo locale-gen en_AU.UTF-8
php admin/tool/phpunit/cli/init.php --no-composer-self-update
- name: Running PHPUnit tests
env:
dbtype: ${{ matrix.db }}
run: vendor/bin/phpunit -v
+29 -71
View File
@@ -16,31 +16,9 @@ services:
- mysql
- docker
php:
# We only run the highest and lowest supported versions to reduce the load on travis-ci.org.
- 7.4
- 7.2
addons:
postgresql: "9.6"
env:
# Although we want to run these jobs and see failures as quickly as possible, we also want to get the slowest job to
# start first so that the total run time is not too high.
#
# We only run MySQL on PHP 7.2, so run that first.
# CI Tests should be second-highest in priority as these only take <= 60 seconds to run under normal circumstances.
# Postgres is significantly is pretty reasonable in its run-time.
# Run CI Tests without running PHPUnit.
- DB=none TASK=CITEST
# Run unit tests on Postgres
- DB=pgsql TASK=PHPUNIT
# Perform an upgrade test too.
- DB=pgsql TASK=UPGRADE
jobs:
# Enable fast finish.
# This will fail the build if a single job fails (except those in allow_failures).
@@ -48,12 +26,35 @@ jobs:
fast_finish: true
include:
# Run mysql only on highest - it's just too slow
- php: 7.4
# First all the lowest php ones (7.2)
- php: 7.2
env: DB=none TASK=CITEST
- php: 7.2
env: DB=none TASK=GRUNT NVM_VERSION='lts/carbon'
- if: env(MOODLE_DATABASE) = "pgsql" OR env(MOODLE_DATABASE) = "all" OR env(MOODLE_DATABASE) IS NOT present
php: 7.2
env: DB=pgsql TASK=PHPUNIT
- if: env(MOODLE_DATABASE) = "mysqli" OR env(MOODLE_DATABASE) = "all"
php: 7.2
env: DB=mysqli TASK=PHPUNIT
# Then, conditionally, all the highest php ones (7.4)
- if: env(MOODLE_PHP) = "all"
php: 7.4
env: DB=none TASK=CITEST
- if: env(MOODLE_PHP) = "all"
php: 7.4
env: DB=none TASK=GRUNT NVM_VERSION='lts/carbon'
- if: env(MOODLE_PHP) = "all" AND (env(MOODLE_DATABASE) = "pgsql" OR env(MOODLE_DATABASE) = "all" OR env(MOODLE_DATABASE) IS NOT present)
php: 7.4
env: DB=pgsql TASK=PHPUNIT
- if: env(MOODLE_PHP) = "all" AND (env(MOODLE_DATABASE) = "mysqli" OR env(MOODLE_DATABASE) = "all")
php: 7.4
env: DB=mysqli TASK=PHPUNIT
# Run grunt/npm install on highest version too ('node' is an alias for the latest node.js version.)
- php: 7.4
env: DB=none TASK=GRUNT NVM_VERSION='lts/carbon'
cache:
directories:
@@ -113,7 +114,7 @@ install:
before_script:
- phpenv config-rm xdebug.ini
- >
if [ "$TASK" = 'PHPUNIT' -o "$TASK" = 'UPGRADE' ];
if [ "$TASK" = 'PHPUNIT' ];
then
# Copy generic configuration in place.
cp config-dist.php config.php ;
@@ -224,32 +225,6 @@ before_script:
export phpcmd=`which php`;
fi
########################################################################
# Upgrade test
########################################################################
- >
if [ "$TASK" = 'UPGRADE' ];
then
# We need the official upstream.
git remote add upstream https://github.com/moodle/moodle.git;
# Checkout 30 STABLE branch (the first version compatible with PHP 7.x)
git fetch upstream MOODLE_30_STABLE;
git checkout MOODLE_30_STABLE;
# Perform the upgrade
php admin/cli/install_database.php --agree-license --adminpass=Password [email protected] --fullname="Upgrade test" --shortname=Upgrade;
# Return to the previous commit
git checkout -;
# Perform the upgrade
php admin/cli/upgrade.php --non-interactive --allow-unstable ;
# The local_ci repository can be used to check upgrade savepoints.
git clone https://github.com/moodlehq/moodle-local_ci.git local/ci ;
fi
script:
- >
if [ "$TASK" = 'PHPUNIT' ];
@@ -275,23 +250,6 @@ script:
git diff --cached --exit-code ;
fi
########################################################################
# Upgrade test
########################################################################
- >
if [ "$TASK" = 'UPGRADE' ];
then
cp local/ci/check_upgrade_savepoints/check_upgrade_savepoints.php ./check_upgrade_savepoints.php
result=`php check_upgrade_savepoints.php`;
# Check if there are problems
count=`echo "$result" | grep -P "ERROR|WARN" | wc -l` ;
if (($count > 0));
then
echo "$result"
exit 1 ;
fi
fi
after_script:
- >
if [ "$TASK" = 'PHPUNIT' ];
@@ -0,0 +1,30 @@
@tool_behat
Feature: Verify that the inplace editable field works as expected
In order to use behat step definitions
As a test write
I need to ensure that the inplace editable works in forms
Background:
Given the following "course" exists:
| fullname | Course 1 |
| shortname | C1 |
And the following "activities" exist:
| activity | course | name | idnumber |
| forum | C1 | My first forum | forum1 |
| assign | C1 | My first assignment | assign1 |
| quiz | C1 | My first quiz | quiz1 |
And I log in as "admin"
And I am on "Course 1" course homepage with editing mode on
@javascript
Scenario: Using an inplace editable updates the name of an activity
When I set the field "Edit title" in the "My first assignment" "activity" to "Coursework submission"
Then I should see "Coursework submission"
And I should not see "My first assignment"
But I should see "My first forum"
And I should see "My first quiz"
And I set the field "Edit title" in the "Coursework submission" "activity" to "My first assignment"
And I should not see "Coursework submission"
But I should see "My first assignment"
And I should see "My first forum"
And I should see "My first quiz"
+3
View File
@@ -27,6 +27,9 @@ require_once('../../config.php');
$issuerid = required_param('id', PARAM_INT);
$wantsurl = new moodle_url(optional_param('wantsurl', '', PARAM_URL));
$PAGE->set_context(context_system::instance());
$PAGE->set_url(new moodle_url('/auth/oauth2/login.php', ['id' => $issuerid]));
require_sesskey();
if (!\auth_oauth2\api::is_enabled()) {
+8 -1
View File
@@ -105,6 +105,9 @@ class block_section_links extends block_base {
}
}
// Whether or not section name should be displayed.
$showsectionname = !empty($config->showsectionname) ? true : false;
// Prepare an array of sections to create links for.
$sections = array();
$canviewhidden = has_capability('moodle/course:update', $context);
@@ -126,13 +129,17 @@ class block_section_links extends block_base {
$sections[$i]->highlight = true;
$sectiontojumpto = $section->section;
}
if ($showsectionname) {
$sections[$i]->name = $courseformat->get_section_name($i);
}
}
}
if (!empty($sections)) {
// Render the sections.
$renderer = $this->page->get_renderer('block_section_links');
$this->content->text = $renderer->render_section_links($this->page->course, $sections, $sectiontojumpto);
$this->content->text = $renderer->render_section_links($this->page->course, $sections,
$sectiontojumpto, $showsectionname);
}
return $this->content;
+3
View File
@@ -82,5 +82,8 @@ class block_section_links_edit_form extends block_edit_form {
$mform->addHelpButton('config_incby'.$i, 'incby'.$i, 'block_section_links');
}
$mform->addElement('selectyesno', 'config_showsectionname', get_string('showsectionname', 'block_section_links'));
$mform->setDefault('config_showsectionname', !empty($config->showsectionname) ? 1 : 0);
$mform->addHelpButton('config_showsectionname', 'showsectionname', 'block_section_links');
}
}
@@ -34,6 +34,8 @@ $string['numsections2'] = 'Alternative number of sections';
$string['numsections2_help'] = 'Once the number of sections in the course reaches this number then the Alternative increment by value is used.';
$string['pluginname'] = 'Section links';
$string['section_links:addinstance'] = 'Add a new section links block';
$string['showsectionname'] = 'Display section name';
$string['showsectionname_help'] = 'Display section name in addition to section number';
$string['topics'] = 'Topics';
$string['weeks'] = 'Weeks';
$string['privacy:metadata'] = 'The Section links block only shows data stored in other locations.';
+7 -2
View File
@@ -38,10 +38,12 @@ class block_section_links_renderer extends plugin_renderer_base {
* @param stdClass $course The course we are rendering for.
* @param array $sections An array of section objects to render.
* @param bool|int The section to provide a jump to link for.
* @param bool $showsectionname Whether or not section name should be displayed.
* @return string The HTML to display.
*/
public function render_section_links(stdClass $course, array $sections, $jumptosection = false) {
$html = html_writer::start_tag('ol', array('class' => 'inline-list'));
public function render_section_links(stdClass $course, array $sections, $jumptosection = false, $showsectionname = false) {
$olparams = $showsectionname ? ['class' => 'unlist'] : ['class' => 'inline-list'];
$html = html_writer::start_tag('ol', $olparams);
foreach ($sections as $section) {
$attributes = array();
if (!$section->visible) {
@@ -49,6 +51,9 @@ class block_section_links_renderer extends plugin_renderer_base {
}
$html .= html_writer::start_tag('li');
$sectiontext = $section->section;
if ($showsectionname) {
$sectiontext .= ': ' . $section->name;
}
if ($section->highlight) {
$sectiontext = html_writer::tag('strong', $sectiontext);
}
+5
View File
@@ -48,4 +48,9 @@ if ($ADMIN->fulltree) {
get_string('incby'.$i.'_help', 'block_section_links'),
$selected[$i][1], $increments));
}
$settings->add(new admin_setting_configcheckbox('block_section_links/showsectionname',
get_string('showsectionname', 'block_section_links'),
get_string('showsectionname_help', 'block_section_links'),
0));
}
@@ -0,0 +1,43 @@
@block @block_section_links
Feature: The Section links block can be configured to display section name in addition to section number
Background:
Given the following "courses" exist:
| fullname | shortname | category | numsections | coursedisplay |
| Course 1 | C1 | 0 | 10 | 1 |
And the following "activities" exist:
| activity | name | course | idnumber | section |
| assign | First assignment | C1 | assign1 | 7 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And I log in as "admin"
And I set the following administration settings values:
| showsectionname | 1 |
And I am on "Course 1" course homepage with editing mode on
And I add the "Section links" block
And I log out
Scenario: Student can see section name under the Section links block
Given I log in as "student1"
When I am on "Course 1" course homepage
Then I should see "7: Topic 7" in the "Section links" "block"
And I follow "7: Topic 7"
And I should see "First assignment"
Scenario: Teacher can configure existing Section links block to display section number or section name
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
When I configure the "Section links" block
And I set the following fields to these values:
| Display section name | No |
And I click on "Save changes" "button"
Then I should not see "7: Topic 7" in the "Section links" "block"
And I should see "7" in the "Section links" "block"
And I follow "7"
And I should see "First assignment"
+6
View File
@@ -0,0 +1,6 @@
This file describes API changes in the section_links block code.
=== 3.11 ===
* New optional parameter $showsectionname has been added to render_section_links(). Setting this to true will display
section name in addition to section number.
+1 -1
View File
@@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2020110900; // The current plugin version (Date: YYYYMMDDXX)
$plugin->version = 2020110901; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2020110300; // Requires this Moodle version
$plugin->component = 'block_section_links'; // Full name of the plugin (used for diagnostics)
@@ -150,4 +150,17 @@ class behat_block_site_main_menu extends behat_base {
$xpath = "//*[contains(concat(' ',normalize-space(@class),' '),' block_site_main_menu ')]//li[contains(., $activityname)]";
$this->execute('behat_action_menu::i_open_the_action_menu_in', [$xpath, 'xpath_element']);
}
/**
* Return the list of partial named selectors.
*
* @return array
*/
public static function get_partial_named_selectors(): array {
return [
new behat_component_named_selector('Activity', [
"//*[contains(concat(' ',normalize-space(@class),' '),' block_site_main_menu ')]//li[contains(., %locator%)]"
]),
];
}
}
@@ -6,15 +6,16 @@ Feature: Edit activities in main menu block
@javascript
Scenario: Edit name of acitivity in-place in site main menu block
Given I log in as "admin"
Given the following "activity" exists:
| activity | forum |
| course | Acceptance test site |
| name | My forum name |
| idnumber | forum |
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
When I add a "Forum" to section "0" and I fill the form with:
| Forum name | My forum name |
And I click on "Edit title" "link" in the "My forum name" activity in site main menu block
And I set the field "New name for activity My forum name" to "New forum name"
And I press the enter key
When I set the field "Edit title" in the "My forum name" "block_site_main_menu > Activity" to "New forum name"
Then I should not see "My forum name"
And I should see "New forum name"
And I follow "New forum name"
@@ -158,4 +158,17 @@ class behat_block_social_activities extends behat_base {
$xpath = "//*[contains(concat(' ',normalize-space(@class),' '),' block_social_activities ')]//li[contains(., $activityname)]";
$this->execute('behat_action_menu::i_open_the_action_menu_in', [$xpath, 'xpath_element']);
}
/**
* Return the list of partial named selectors.
*
* @return array
*/
public static function get_partial_named_selectors(): array {
return [
new behat_component_named_selector('Activity', [
"//*[contains(concat(' ',normalize-space(@class),' '),' block_social_activities ')]//li[contains(., %locator%)]",
]),
];
}
}
@@ -25,9 +25,7 @@ Feature: Edit activities in social activities block
And I click on "Add a new Forum" "link" in the "Add an activity or resource" "dialogue"
And I set the field "Forum name" to "My forum name"
And I press "Save and return to course"
And I click on "Edit title" "link" in the "My forum name" activity in social activities block
And I set the field "New name for activity My forum name" to "New forum name"
And I press the enter key
When I set the field "Edit title" in the "My forum name" "block_social_activities > Activity" to "New forum name"
Then I should not see "My forum name" in the "Social activities" "block"
And I should see "New forum name"
And I follow "New forum name"
@@ -84,4 +82,3 @@ Feature: Edit activities in social activities block
And I should not see "My forum name" in the "Social activities" "block"
And I click on "My forum name" "link" in the "Recent activity" "block"
And I should see "My forum name" in the ".breadcrumb" "css_element"
And I log out
+1 -3
View File
@@ -62,9 +62,7 @@ Feature: Add cohorts of users
@javascript
Scenario: Edit cohort name in-place
When I follow "Cohorts"
And I click on "Edit cohort name" "link" in the "Test cohort name" "table_row"
And I set the field "New name for cohort Test cohort name" to "Students cohort"
And I press the enter key
And I set the field "Edit cohort name" to "Students cohort"
Then I should not see "Test cohort name"
And I should see "Students cohort"
And I follow "Cohorts"
+1 -1
View File
@@ -12,7 +12,7 @@
],
"require-dev": {
"phpunit/phpunit": "8.5.*",
"moodlehq/behat-extension": "3.310.0",
"moodlehq/behat-extension": "3.311.0",
"mikey179/vfsstream": "^1.6",
"instaclick/php-webdriver": "dev-local as 1.x-dev"
}
Generated
+151 -339
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -324,6 +324,9 @@ $CFG->admin = 'admin';
// Use the igbinary serializer instead of the php default one. Note that phpredis must be compiled with
// igbinary support to make the setting to work. Also, if you change the serializer you have to flush the database!
// $CFG->session_redis_serializer_use_igbinary = false; // Optional, default is PHP builtin serializer.
// $CFG->session_redis_compressor = 'none'; // Optional, possible values are:
// // 'gzip' - PHP GZip compression
// // 'zstd' - PHP Zstandard compression
//
// Please be aware that when selecting Memcached for sessions that it is advised to use a dedicated
// memcache server. The memcached extension does not provide isolated environments for individual uses.
@@ -53,9 +53,7 @@ Feature: Sections can be edited and deleted in topics format
@javascript
Scenario: Inline edit section name in topics format
When I click on "Edit topic name" "link" in the "li#section-1" "css_element"
And I set the field "New name for topic Topic 1" to "Midterm evaluation"
And I press the enter key
When I set the field "Edit topic name" in the "li#section-1" "css_element" to "Midterm evaluation"
Then I should not see "Topic 1" in the "region-main" "region"
And "New name for topic" "field" should not exist
And I should see "Midterm evaluation" in the "li#section-1" "css_element"
@@ -54,9 +54,7 @@ Feature: Sections can be edited and deleted in weeks format
@javascript
Scenario: Inline edit section name in weeks format
When I click on "Edit week name" "link" in the "li#section-1" "css_element"
And I set the field "New name for week 1 May - 7 May" to "Midterm evaluation"
And I press the enter key
When I set the field "Edit week name" in the "li#section-1" "css_element" to "Midterm evaluation"
Then I should not see "1 May - 7 May" in the "region-main" "region"
And "New name for week" "field" should not exist
And I should see "Midterm evaluation" in the "li#section-1" "css_element"
@@ -15,15 +15,16 @@ Feature: Edit activity name in-place
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
And the following "activity" exists:
| course | C1 |
| activity | forum |
| name | Test forum name |
| description | Test forum description |
| idnumber | forum1 |
When I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I add a "Forum" to section "1" and I fill the form with:
| Forum name | Test forum name |
| Description | Test forum description |
# Rename activity
And I click on "Edit title" "link" in the "//div[contains(@class,'activityinstance') and contains(.,'Test forum name')]" "xpath_element"
And I set the field "New name for activity Test forum name" to "Good news"
And I press the enter key
And I set the field "Edit title" in the "Test forum name" "activity" to "Good news"
Then I should not see "Test forum name" in the ".course-content" "css_element"
And "New name for activity Test forum name" "field" should not exist
And I should see "Good news"
@@ -32,7 +33,7 @@ Feature: Edit activity name in-place
And I should not see "Test forum name"
# Cancel renaming
And I click on "Edit title" "link" in the "//div[contains(@class,'activityinstance') and contains(.,'Good news')]" "xpath_element"
And I set the field "New name for activity Good news" to "Terrible news"
And I type "Terrible news"
And I press the escape key
And "New name for activity Good news" "field" should not exist
And I should see "Good news"
+6 -16
View File
@@ -852,22 +852,12 @@ class behat_course extends behat_base {
* @param string $newactivityname
*/
public function i_change_activity_name_to($activityname, $newactivityname) {
if (!$this->running_javascript()) {
throw new DriverException('Change activity name step is not available with Javascript disabled');
}
$activity = $this->escape($activityname);
$this->execute('behat_course::i_click_on_in_the_activity',
array(get_string('edittitle'), "link", $activity)
);
// Adding chr(10) to save changes.
$this->execute('behat_forms::i_set_the_field_to',
array('title', $this->escape($newactivityname) . chr(10))
);
$this->execute('behat_forms::i_set_the_field_in_container_to', [
get_string('edittitle'),
$activityname,
'activity',
$newactivityname
]);
}
/**
@@ -12,7 +12,6 @@ Feature: Managers can manage categories for course custom fields
Then I should see "Other fields" in the "#customfield_catlist" "css_element"
And I navigate to "Reports > Logs" in site administration
And I press "Get these logs"
And I log out
Scenario: Edit a category name for custom course fields
Given the following "custom field categories" exist:
@@ -20,15 +19,12 @@ Feature: Managers can manage categories for course custom fields
| Category for test | core_course | course | 0 |
And I log in as "admin"
And I navigate to "Courses > Course custom fields" in site administration
And I click on "Edit category name" "link" in the "//div[contains(@class,'categoryinstance') and contains(.,'Category for test')]" "xpath_element"
And I set the field "New value for Category for test" to "Good fields"
And I press the enter key
And I set the field "Edit category name" in the "//div[contains(@class,'categoryinstance') and contains(.,'Category for test')]" "xpath_element" to "Good fields"
Then I should not see "Category for test" in the "#customfield_catlist" "css_element"
And "New value for Category for test" "field" should not exist
And I should see "Good fields" in the "#customfield_catlist" "css_element"
And I navigate to "Reports > Logs" in site administration
And I press "Get these logs"
And I log out
Scenario: Delete a category for custom course fields
Given the following "custom field categories" exist:
@@ -46,7 +42,6 @@ Feature: Managers can manage categories for course custom fields
Then I should not see "Test category" in the "#customfield_catlist" "css_element"
And I navigate to "Reports > Logs" in site administration
And I press "Get these logs"
And I log out
Scenario: Move field in the course custom fields to another category
Given the following "custom field categories" exist:
@@ -78,7 +73,6 @@ Feature: Managers can manage categories for course custom fields
And I press "Move \"Field1\""
And I follow "After field Field2"
And "Field1" "text" should appear after "Field2" "text"
And I log out
Scenario: Reorder course custom field categories
Given the following "custom field categories" exist:
@@ -108,4 +102,3 @@ Feature: Managers can manage categories for course custom fields
And "Field1" "text" should appear after "Category1" "text"
And "Category2" "text" should appear after "Field1" "text"
And "Category3" "text" should appear after "Category2" "text"
And I log out
-95
View File
@@ -1,95 +0,0 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* CLI sync for full external database synchronisation.
*
* Sample cron entry:
* # 5 minutes past 4am
* 5 4 * * * $sudo -u www-data /usr/bin/php /var/www/moodle/enrol/database/cli/sync.php
*
* Notes:
* - it is required to use the web server account when executing PHP CLI scripts
* - you need to change the "www-data" to match the apache user account
* - use "su" if "sudo" not available
*
* @deprecated since Moodle 3.7 MDL-59986 - please do not use this CLI script any more, use scheduled task instead.
* @todo MDL-63266 This will be deleted in Moodle 3.11.
* @package enrol_database
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require(__DIR__.'/../../../config.php');
require_once("$CFG->libdir/clilib.php");
// Now get cli options.
list($options, $unrecognized) = cli_get_params(array('verbose'=>false, 'help'=>false), array('v'=>'verbose', 'h'=>'help'));
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
if ($options['help']) {
$help =
"Execute enrol sync with external database.
The enrol_database plugin must be enabled and properly configured.
Options:
-v, --verbose Print verbose progress information
-h, --help Print out this help
Example:
\$ sudo -u www-data /usr/bin/php enrol/database/cli/sync.php
Sample cron entry:
# 5 minutes past 4am
5 4 * * * sudo -u www-data /usr/bin/php /var/www/moodle/enrol/database/cli/sync.php
";
echo $help;
die;
}
cli_problem('[ENROL DATABASE] The sync enrolments cron script has been deprecated. Please use the scheduled task instead.');
// Abort execution of the CLI script if the enrol_database\task\sync_enrolments is enabled.
$task = \core\task\manager::get_scheduled_task('enrol_database\task\sync_enrolments');
if (!$task->get_disabled()) {
cli_error('[ENROL DATABASE] The scheduled task sync_enrolments is enabled, the cron execution has been aborted.');
}
if (!enrol_is_enabled('database')) {
cli_error('enrol_database plugin is disabled, synchronisation stopped', 2);
}
if (empty($options['verbose'])) {
$trace = new null_progress_trace();
} else {
$trace = new text_progress_trace();
}
/** @var enrol_database_plugin $enrol */
$enrol = enrol_get_plugin('database');
$result = 0;
$result = $result | $enrol->sync_courses($trace);
$result = $result | $enrol->sync_enrolments($trace);
exit($result);
+3
View File
@@ -1,5 +1,8 @@
This files describes API changes in the enrol_database code.
=== 3.11 ===
* Final deprecation enrol/database/cli/sync.php. Refer below for substitute.
=== 3.9 ===
* Class enrol_database_admin_setting_category has been removed. This class was only used by the database
enrolment plugin settings and it was replaced by admin_settings_coursecat_select.
+1
View File
@@ -80,6 +80,7 @@ $string['profilelabel'] = '{$a->label}: {$a->profile} {$a->operator} {$a->value}
$string['profilelabelnovalue'] = '{$a->label}: {$a->profile} {$a->operator}';
$string['removeall'] = 'Remove all filters';
$string['removeselected'] = 'Remove selected';
$string['replacefilters'] = 'Replace filters';
$string['selectlabel'] = '{$a->label} {$a->operator} {$a->value}';
$string['startswith'] = 'starts with';
$string['tablenosave'] = 'Changes in table above are saved automatically.';
+5 -1303
View File
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -48,7 +48,6 @@ class behat_field_manager {
* @return behat_form_field
*/
public static function get_form_field_from_label($label, RawMinkContext $context) {
// There are moodle form elements that are not directly related with
// a basic HTML form field, we should also take care of them.
// The DOM node.
@@ -172,6 +171,10 @@ class behat_field_manager {
} else if ($tagname == 'select') {
// Select tag.
return 'select';
} else if ($tagname == 'span') {
if ($fieldnode->hasAttribute('data-inplaceeditable') && $fieldnode->getAttribute('data-inplaceeditable')) {
return 'inplaceeditable';
}
}
// We can not provide a closer field type.
@@ -0,0 +1,87 @@
<?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/>.
/**
* The Interface for a behat root context.
*
* @package core
* @category test
* @copyright 2020 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* The Interface for a behat root context.
*
* This interface should be implemented by the behat_base context, and behat form fields, and it should be paired with
* the behat_session_trait.
*
* It should not be necessary to implement this interface, and the behat_session_trait trait in normal circumstances.
*
* @package core
* @category test
* @copyright 2020 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface behat_session_interface {
/**
* Small timeout.
*
* A reduced timeout for cases where self::TIMEOUT is too much
* and a simple $this->getSession()->getPage()->find() could not
* be enough.
*
* @deprecated since Moodle 3.7 MDL-64979 - please use get_reduced_timeout() instead
* @todo MDL-64982 This will be deleted in Moodle 3.11
* @see behat_base::get_reduced_timeout()
*/
const REDUCED_TIMEOUT = 2;
/**
* The timeout for each Behat step (load page, wait for an element to load...).
*
* @deprecated since Moodle 3.7 MDL-64979 - please use get_timeout() instead
* @todo MDL-64982 This will be deleted in Moodle 3.11
* @see behat_base::get_timeout()
*/
const TIMEOUT = 6;
/**
* And extended timeout for specific cases.
*
* @deprecated since Moodle 3.7 MDL-64979 - please use get_extended_timeout() instead
* @todo MDL-64982 This will be deleted in Moodle 3.11
* @see behat_base::get_extended_timeout()
*/
const EXTENDED_TIMEOUT = 10;
/**
* The JS code to check that the page is ready.
*
* The document must be complete and either M.util.pending_js must be empty, or it must not be defined at all.
*/
const PAGE_READY_JS = "document.readyState === 'complete' && " .
"(typeof M !== 'object' || typeof M.util !== 'object' || " .
"typeof M.util.pending_js === 'undefined' || M.util.pending_js.length === 0)";
/**
* Returns the Mink session.
*
* @param string|null $name name of the session OR active session will be used
* @return \Behat\Mink\Session
*/
public function getSession($name = null);
}
File diff suppressed because it is too large Load Diff
+6 -1
View File
@@ -135,7 +135,7 @@ class behat_partial_named_selector extends \Behat\Mink\Selector\PartialNamedSele
*/
protected static $moodleselectors = array(
'activity' => <<<XPATH
.//li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')][normalize-space(.) = %locator% ]
.//li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')][descendant::*[contains(normalize-space(.), %locator%)]]
XPATH
, 'block' => <<<XPATH
.//*[@data-block][contains(concat(' ', normalize-space(@class), ' '), concat(' ', %locator%, ' ')) or
@@ -262,6 +262,11 @@ XPATH
.//*[@data-passwordunmask='wrapper']
/descendant::input[@id = %locator% or @id = //label[contains(normalize-space(string(.)), %locator%)]/@for]
XPATH
,
'inplaceeditable' => <<<XPATH
.//descendant::span[@data-inplaceeditable][descendant::a[%titleMatch%]]
XPATH
,
],
];
+16 -3
View File
@@ -25,8 +25,8 @@
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
use Behat\Mink\Session as Session,
Behat\Mink\Element\NodeElement as NodeElement;
use Behat\Mink\Element\NodeElement;
use Behat\Mink\Session;
/**
* Representation of a form field.
@@ -38,7 +38,10 @@ use Behat\Mink\Session as Session,
* @copyright 2012 David Monllaó
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_form_field {
class behat_form_field implements behat_session_interface {
// All of the functionality of behat_base is shared with form fields via the behat_session_trait trait.
use behat_session_trait;
/**
* @var Session Behat session.
@@ -55,6 +58,16 @@ class behat_form_field {
*/
protected $fieldlocator = false;
/**
* Returns the Mink session.
*
* @param string|null $name name of the session OR active session will be used
* @return \Behat\Mink\Session
*/
public function getSession($name = null) {
return $this->session;
}
/**
* General constructor with the node and the session to interact with.
@@ -0,0 +1,74 @@
<?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/>.
/**
* Custom interaction with inplace editable elements.
*
* @package core_form
* @category test
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
require_once(__DIR__ . '/behat_form_text.php');
/**
* Custom interaction with inplace editable elements.
*
* @package core_form
* @category test
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_form_inplaceeditable extends behat_form_text {
/**
* Sets the value to a field.
*
* @param string $value
* @return void
*/
public function set_value($value) {
// Require JS to run this step.
self::require_javascript();
// Click to enable editing.
self::execute(
'behat_general::i_click_on_in_the',
[
'[data-inplaceeditablelink]',
'css_element',
$this->field,
'NodeElement',
]
);
// Note: It is not possible to use the NodeElement->keyDown() and related functions because
// this can trigger a focusOnElement call each time.
// Instead use the behat_base::type_keys() function.
// The inplace editable selects all existing content on focus.
// Clear the existing value.
self::type_keys($this->session, [behat_keys::BACKSPACE]);
// Type in the new value, followed by ENTER to save the value.
self::type_keys($this->session, array_merge(
str_split($value),
[behat_keys::ENTER]
));
}
}
+70 -1
View File
@@ -40,6 +40,19 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class redis extends handler {
/**
* Compressor: none.
*/
const COMPRESSION_NONE = 'none';
/**
* Compressor: PHP GZip.
*/
const COMPRESSION_GZIP = 'gzip';
/**
* Compressor: PHP Zstandard.
*/
const COMPRESSION_ZSTD = 'zstd';
/** @var string $host save_path string */
protected $host = '';
/** @var int $port The port to connect to */
@@ -56,6 +69,8 @@ class redis extends handler {
protected $lockretry = 100;
/** @var int $serializer The serializer to use */
protected $serializer = \Redis::SERIALIZER_PHP;
/** @var int $compressor The compressor to use */
protected $compressor = self::COMPRESSION_NONE;
/** @var string $lasthash hash of the session data content */
protected $lasthash = null;
@@ -122,6 +137,10 @@ class redis extends handler {
if (isset($CFG->session_redis_lock_expire)) {
$this->lockexpire = (int)$CFG->session_redis_lock_expire;
}
if (isset($CFG->session_redis_compressor)) {
$this->compressor = $CFG->session_redis_compressor;
}
}
/**
@@ -268,7 +287,8 @@ class redis extends handler {
if ($this->requires_write_lock()) {
$this->lock_session($id);
}
$sessiondata = $this->connection->get($id);
$sessiondata = $this->uncompress($this->connection->get($id));
if ($sessiondata === false) {
if ($this->requires_write_lock()) {
$this->unlock_session($id);
@@ -285,6 +305,53 @@ class redis extends handler {
return $sessiondata;
}
/**
* Compresses session data.
*
* @param mixed $value
* @return string
*/
private function compress($value) {
switch ($this->compressor) {
case self::COMPRESSION_NONE:
return $value;
case self::COMPRESSION_GZIP:
return gzencode($value);
case self::COMPRESSION_ZSTD:
return zstd_compress($value);
default:
debugging("Invalid compressor: {$this->compressor}");
return $value;
}
}
/**
* Uncompresses session data.
*
* @param string $value
* @return mixed
*/
private function uncompress($value) {
if ($value === false) {
return false;
}
switch ($this->compressor) {
case self::COMPRESSION_NONE:
break;
case self::COMPRESSION_GZIP:
$value = gzdecode($value);
break;
case self::COMPRESSION_ZSTD:
$value = zstd_uncompress($value);
break;
default:
debugging("Invalid compressor: {$this->compressor}");
}
return $value;
}
/**
* Write the serialized session data to our session store.
*
@@ -312,6 +379,8 @@ class redis extends handler {
// There can be race conditions on new sessions racing each other but we can
// address that in the future.
try {
$data = $this->compress($data);
$this->connection->setex($id, $this->timeout, $data);
} catch (RedisException $e) {
error_log('Failed talking to redis: '.$e->getMessage());
+26 -7
View File
@@ -7801,7 +7801,14 @@ function get_plugin_list_with_function($plugintype, $function, $file = 'lib.php'
$filepath = $allplugins[$pluginname] . DIRECTORY_SEPARATOR . $file;
if (file_exists($filepath)) {
include_once($filepath);
$pluginfunctions[$plugintype . '_' . $pluginname] = $functionname;
// Now that the file is loaded, we must verify the function still exists.
if (function_exists($functionname)) {
$pluginfunctions[$plugintype . '_' . $pluginname] = $functionname;
} else {
// Invalidate the cache for next run.
\cache_helper::invalidate_by_definition('core', 'plugin_functions');
}
}
}
}
@@ -7834,6 +7841,7 @@ function get_plugins_with_function($function, $file = 'lib.php', $include = true
// Clearning the filename as cache_helper::hash_key only allows a-zA-Z0-9_.
$key = $function . '_' . clean_param($file, PARAM_ALPHA);
$pluginfunctions = $cache->get($key);
$dirty = false;
// Use the plugin manager to check that plugins are currently installed.
$pluginmanager = \core_plugin_manager::instance();
@@ -7848,14 +7856,14 @@ function get_plugins_with_function($function, $file = 'lib.php', $include = true
foreach ($plugins as $plugin => $function) {
if (!isset($installedplugins[$plugin])) {
// Plugin code is still present on disk but it is not installed.
unset($pluginfunctions[$plugintype][$plugin]);
continue;
$dirty = true;
break 2;
}
// Cache might be out of sync with the codebase, skip the plugin if it is not available.
if (empty($allplugins[$plugin])) {
unset($pluginfunctions[$plugintype][$plugin]);
continue;
$dirty = true;
break 2;
}
$fileexists = file_exists($allplugins[$plugin] . DIRECTORY_SEPARATOR . $file);
@@ -7864,11 +7872,22 @@ function get_plugins_with_function($function, $file = 'lib.php', $include = true
include_once($allplugins[$plugin] . DIRECTORY_SEPARATOR . $file);
} else if (!$fileexists) {
// If the file is not available any more it should not be returned.
unset($pluginfunctions[$plugintype][$plugin]);
$dirty = true;
break 2;
}
// Check if the function still exists in the file.
if ($include && !function_exists($function)) {
$dirty = true;
break 2;
}
}
}
return $pluginfunctions;
// If the cache is dirty, we should fall through and let it rebuild.
if (!$dirty) {
return $pluginfunctions;
}
}
$pluginfunctions = array();
-20
View File
@@ -41,26 +41,6 @@ use Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException,
*/
class behat_deprecated extends behat_base {
/**
* Docks a block. Editing mode should be previously enabled.
* @throws ExpectationException
* @param string $blockname
* @return void
* @deprecated since Moodle 3.7 MDL-64506 - please do not use this definition step any more.
* @todo MDL-65215 This will be deleted in Moodle 3.11.
*/
public function i_dock_block($blockname) {
$message = "Block docking is no longer used as of MDL-64506. Please update your tests.";
$this->deprecated_message($message);
// Looking for both title and alt.
$xpath = "//input[@type='image'][@title='" . get_string('dockblock', 'block', $blockname) . "' or @alt='" . get_string('addtodock', 'block') . "']";
$this->execute('behat_general::i_click_on_in_the',
array($xpath, "xpath_element", $this->escape($blockname), "block")
);
}
/**
* Throws an exception if $CFG->behat_usedeprecated is not allowed.
*
+24
View File
@@ -116,6 +116,30 @@ class core_session_redis_testcase extends advanced_testcase {
$this->assertSessionNoLocks();
}
public function test_compression_read_and_write_works() {
global $CFG;
$CFG->session_redis_compressor = \core\session\redis::COMPRESSION_GZIP;
$sess = new \core\session\redis();
$sess->init();
$this->assertTrue($sess->handler_write('sess1', 'DATA'));
$this->assertSame('DATA', $sess->handler_read('sess1'));
$this->assertTrue($sess->handler_close());
if (extension_loaded('zstd')) {
$CFG->session_redis_compressor = \core\session\redis::COMPRESSION_ZSTD;
$sess = new \core\session\redis();
$sess->init();
$this->assertTrue($sess->handler_write('sess2', 'DATA'));
$this->assertSame('DATA', $sess->handler_read('sess2'));
$this->assertTrue($sess->handler_close());
}
$CFG->session_redis_compressor = \core\session\redis::COMPRESSION_NONE;
}
public function test_session_blocks_with_existing_session() {
$sess = new \core\session\redis();
$sess->init();
+1
View File
@@ -4,6 +4,7 @@ information provided here is intended especially for developers.
=== 3.11 ===
* New optional parameter $extracontent for print_collapsible_region_start(). This allows developers to add interactive HTML elements
(e.g. a help icon) after the collapsible region's toggle link.
* Final deprecation i_dock_block() in behat_deprecated.php
=== 3.10 ===
* PHPUnit has been upgraded to 8.5. That comes with a few changes:
+1 -1
View File
@@ -1,2 +1,2 @@
define ("core_message/message_drawer",["jquery","core/custom_interaction_events","core/pubsub","core_message/message_drawer_view_contact","core_message/message_drawer_view_contacts","core_message/message_drawer_view_conversation","core_message/message_drawer_view_group_info","core_message/message_drawer_view_overview","core_message/message_drawer_view_search","core_message/message_drawer_view_settings","core_message/message_drawer_router","core_message/message_drawer_routes","core_message/message_drawer_events","core/pending","core/drawer"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){var p={DRAWER:"[data-region=\"right-hand-drawer\"]",JUMPTO:".popover-region [data-region=\"jumpto\"]",PANEL_BODY_CONTAINER:"[data-region=\"panel-body-container\"]",PANEL_HEADER_CONTAINER:"[data-region=\"panel-header-container\"]",VIEW_CONTACT:"[data-region=\"view-contact\"]",VIEW_CONTACTS:"[data-region=\"view-contacts\"]",VIEW_CONVERSATION:"[data-region=\"view-conversation\"]",VIEW_GROUP_INFO:"[data-region=\"view-group-info\"]",VIEW_OVERVIEW:"[data-region=\"view-overview\"]",VIEW_SEARCH:"[data-region=\"view-search\"]",VIEW_SETTINGS:"[data-region=\"view-settings\"]",ROUTES:"[data-route]",ROUTES_BACK:"[data-route-back]",HEADER_CONTAINER:"[data-region=\"header-container\"]",BODY_CONTAINER:"[data-region=\"body-container\"]",FOOTER_CONTAINER:"[data-region=\"footer-container\"]",CLOSE_BUTTON:"[data-action=\"closedrawer\"]"},q=function(a,b,c){var d=b.find(p.HEADER_CONTAINER).find(c);if(!d.length){d=b.find(p.PANEL_HEADER_CONTAINER).find(c)}var e=b.find(p.BODY_CONTAINER).find(c);if(!e.length){e=b.find(p.PANEL_BODY_CONTAINER).find(c)}var f=b.find(p.FOOTER_CONTAINER).find(c);return[a,d.length?d:null,e.length?e:null,f.length?f:null]},r=[[l.VIEW_CONTACT,p.VIEW_CONTACT,d.show,d.description],[l.VIEW_CONTACTS,p.VIEW_CONTACTS,e.show,e.description],[l.VIEW_CONVERSATION,p.VIEW_CONVERSATION,f.show,f.description],[l.VIEW_GROUP_INFO,p.VIEW_GROUP_INFO,g.show,g.description],[l.VIEW_OVERVIEW,p.VIEW_OVERVIEW,h.show,h.description],[l.VIEW_SEARCH,p.VIEW_SEARCH,i.show,i.description],[l.VIEW_SETTINGS,p.VIEW_SETTINGS,j.show,j.description]],s=function(a,b){r.forEach(function(c){k.add(a,c[0],q(a,b,c[1]),c[2],c[3])})},t=function(a,b){if(!b.attr("data-shown")){k.go(a,l.VIEW_OVERVIEW);b.attr("data-shown",!0)}var c=o.getDrawerRoot(b);if(c.length){o.show(c)}},u=function(a){var b=o.getDrawerRoot(a);if(b.length){o.hide(b)}},v=function(a){var b=o.getDrawerRoot(a);if(b.length){return o.isVisible(b)}return!0},w=function(b){a(p.DRAWER).attr("data-origin",b)},x=function(d,e,f){b.define(e,[b.events.activate]);var g=/^data-route-param-?(\d*)$/;e.on(b.events.activate,p.ROUTES,function(b,c){for(var e=a(b.target).closest(p.ROUTES),f=e.attr("data-route"),h=[],j=0;j<e[0].attributes.length;j++){h.push(e[0].attributes[j])}var l=h.filter(function(a){var b=a.nodeName,c=g.test(b);return c});l.sort(function(c,a){var b=g.exec(c.nodeName),d=g.exec(a.nodeName),e=1<b.length?b[1]:0,f=1<d.length?d[1]:0;if(e<f){return-1}else if(f<e){return 1}else{return 0}});var m=l.map(function(a){return a.nodeValue}),n=[d,f].concat(m);k.go.apply(null,n);c.originalEvent.preventDefault()});e.on(b.events.activate,p.ROUTES_BACK,function(a,b){k.back(d);b.originalEvent.preventDefault()});e.on("hide.bs.collapse",".collapse",function(b){var c=new n;a(b.target).one("hidden.bs.collapse",function(){c.resolve()})});e.on("show.bs.collapse",".collapse",function(b){var c=new n;a(b.target).one("shown.bs.collapse",function(){c.resolve()})});a(p.JUMPTO).focus(function(){var b=a(p.HEADER_CONTAINER).find("input:visible");if(b.length){b.focus()}else{a(p.HEADER_CONTAINER).find(p.ROUTES_BACK).focus()}});a(p.DRAWER).focus(function(){var b=a(this).attr("data-origin");if(b){a("#"+b).focus()}});if(!f){c.subscribe(m.SHOW,function(){t(d,e)});c.subscribe(m.HIDE,function(){u(e)});c.subscribe(m.TOGGLE_VISIBILITY,function(b){if(v(e)){u(e);a(p.JUMPTO).attr("tabindex",-1)}else{t(d,e);w(b);a(p.JUMPTO).attr("tabindex",0)}})}c.subscribe(m.SHOW_CONVERSATION,function(a){w(a.buttonid);t(d,e);k.go(d,l.VIEW_CONVERSATION,a.conversationid)});var h=e.find(p.CLOSE_BUTTON);h.on(b.events.activate,function(){c.publish(m.TOGGLE_VISIBILITY)});c.subscribe(m.CREATE_CONVERSATION_WITH_USER,function(a){w(a.buttonid);t(d,e);k.go(d,l.VIEW_CONVERSATION,null,"create",a.userid)});c.subscribe(m.SHOW_SETTINGS,function(){t(d,e);k.go(d,l.VIEW_SETTINGS)});c.subscribe(m.PREFERENCES_UPDATED,function(a){var b=a.filter(function(a){return"message_entertosend"==a.type}),c=b.length?b[0]:null;if(c){var d=e.find(p.FOOTER_CONTAINER).find(p.VIEW_CONVERSATION);d.attr("data-enter-to-send",c.value)}})};return{init:function init(b,c,d,e){b=a(b);s(c,b);x(c,b,d);if(d){t(c,b);if(e){var f=e.params||[];f=[c,e.path].concat(f);k.go.apply(null,f)}}}}});
define ("core_message/message_drawer",["jquery","core/custom_interaction_events","core/pubsub","core_message/message_drawer_view_contact","core_message/message_drawer_view_contacts","core_message/message_drawer_view_conversation","core_message/message_drawer_view_group_info","core_message/message_drawer_view_overview","core_message/message_drawer_view_search","core_message/message_drawer_view_settings","core_message/message_drawer_router","core_message/message_drawer_routes","core_message/message_drawer_events","core/pending","core/drawer"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){var p={DRAWER:"[data-region=\"right-hand-drawer\"]",JUMPTO:".popover-region [data-region=\"jumpto\"]",PANEL_BODY_CONTAINER:"[data-region=\"panel-body-container\"]",PANEL_HEADER_CONTAINER:"[data-region=\"panel-header-container\"]",VIEW_CONTACT:"[data-region=\"view-contact\"]",VIEW_CONTACTS:"[data-region=\"view-contacts\"]",VIEW_CONVERSATION:"[data-region=\"view-conversation\"]",VIEW_GROUP_INFO:"[data-region=\"view-group-info\"]",VIEW_OVERVIEW:"[data-region=\"view-overview\"]",VIEW_SEARCH:"[data-region=\"view-search\"]",VIEW_SETTINGS:"[data-region=\"view-settings\"]",ROUTES:"[data-route]",ROUTES_BACK:"[data-route-back]",HEADER_CONTAINER:"[data-region=\"header-container\"]",BODY_CONTAINER:"[data-region=\"body-container\"]",FOOTER_CONTAINER:"[data-region=\"footer-container\"]",CLOSE_BUTTON:"[data-action=\"closedrawer\"]"},q=function(a,b,c){var d=b.find(p.HEADER_CONTAINER).find(c);if(!d.length){d=b.find(p.PANEL_HEADER_CONTAINER).find(c)}var e=b.find(p.BODY_CONTAINER).find(c);if(!e.length){e=b.find(p.PANEL_BODY_CONTAINER).find(c)}var f=b.find(p.FOOTER_CONTAINER).find(c);return[a,d.length?d:null,e.length?e:null,f.length?f:null]},r=[[l.VIEW_CONTACT,p.VIEW_CONTACT,d.show,d.description],[l.VIEW_CONTACTS,p.VIEW_CONTACTS,e.show,e.description],[l.VIEW_CONVERSATION,p.VIEW_CONVERSATION,f.show,f.description],[l.VIEW_GROUP_INFO,p.VIEW_GROUP_INFO,g.show,g.description],[l.VIEW_OVERVIEW,p.VIEW_OVERVIEW,h.show,h.description],[l.VIEW_SEARCH,p.VIEW_SEARCH,i.show,i.description],[l.VIEW_SETTINGS,p.VIEW_SETTINGS,j.show,j.description]],s=function(a,b){r.forEach(function(c){k.add(a,c[0],q(a,b,c[1]),c[2],c[3])})},t=function(a,b){if(!b.attr("data-shown")){k.go(a,l.VIEW_OVERVIEW);b.attr("data-shown",!0)}var c=o.getDrawerRoot(b);if(c.length){o.show(c)}},u=function(a){var b=o.getDrawerRoot(a);if(b.length){o.hide(b)}},v=function(a){var b=o.getDrawerRoot(a);if(b.length){return o.isVisible(b)}return!0},w=function(b){a(p.DRAWER).attr("data-origin",b)},x=function(d,e,f){b.define(e,[b.events.activate]);var g=/^data-route-param-?(\d*)$/;e.on(b.events.activate,p.ROUTES,function(b,c){for(var e=a(b.target).closest(p.ROUTES),f=e.attr("data-route"),h=[],j=0;j<e[0].attributes.length;j++){h.push(e[0].attributes[j])}var l=h.filter(function(a){var b=a.nodeName,c=g.test(b);return c});l.sort(function(c,a){var b=g.exec(c.nodeName),d=g.exec(a.nodeName),e=1<b.length?b[1]:0,f=1<d.length?d[1]:0;if(e<f){return-1}else if(f<e){return 1}else{return 0}});var m=l.map(function(a){return a.nodeValue}),n=[d,f].concat(m);k.go.apply(null,n);c.originalEvent.preventDefault()});e.on(b.events.activate,p.ROUTES_BACK,function(a,b){k.back(d);b.originalEvent.preventDefault()});e.on("hide.bs.collapse",".collapse",function(b){var c=new n;a(b.target).one("hidden.bs.collapse",function(){c.resolve()})});e.on("show.bs.collapse",".collapse",function(b){var c=new n;a(b.target).one("shown.bs.collapse",function(){c.resolve()})});a(p.JUMPTO).focus(function(){var b=e.find(p.CLOSE_BUTTON);if(b.length){b.focus()}else{a(p.HEADER_CONTAINER).find(p.ROUTES_BACK).focus()}});a(p.DRAWER).focus(function(){var b=a(this).attr("data-origin");if(b){a("#"+b).focus()}});if(!f){c.subscribe(m.SHOW,function(){t(d,e)});c.subscribe(m.HIDE,function(){u(e)});c.subscribe(m.TOGGLE_VISIBILITY,function(b){if(v(e)){u(e);a(p.JUMPTO).attr("tabindex",-1)}else{t(d,e);w(b);a(p.JUMPTO).attr("tabindex",0)}})}c.subscribe(m.SHOW_CONVERSATION,function(a){w(a.buttonid);t(d,e);k.go(d,l.VIEW_CONVERSATION,a.conversationid)});var h=e.find(p.CLOSE_BUTTON);h.on(b.events.activate,function(){var b=a(p.DRAWER).attr("data-origin");if(b){a("#"+b).focus()}c.publish(m.TOGGLE_VISIBILITY)});c.subscribe(m.CREATE_CONVERSATION_WITH_USER,function(a){w(a.buttonid);t(d,e);k.go(d,l.VIEW_CONVERSATION,null,"create",a.userid)});c.subscribe(m.SHOW_SETTINGS,function(){t(d,e);k.go(d,l.VIEW_SETTINGS)});c.subscribe(m.PREFERENCES_UPDATED,function(a){var b=a.filter(function(a){return"message_entertosend"==a.type}),c=b.length?b[0]:null;if(c){var d=e.find(p.FOOTER_CONTAINER).find(p.VIEW_CONVERSATION);d.attr("data-enter-to-send",c.value)}})};return{init:function init(b,c,d,e){b=a(b);s(c,b);x(c,b,d);if(d){t(c,b);if(e){var f=e.params||[];f=[c,e.path].concat(f);k.go.apply(null,f)}}}}});
//# sourceMappingURL=message_drawer.min.js.map
File diff suppressed because one or more lines are too long
+5 -1
View File
@@ -254,7 +254,7 @@ function(
});
$(SELECTORS.JUMPTO).focus(function() {
var firstInput = $(SELECTORS.HEADER_CONTAINER).find('input:visible');
var firstInput = root.find(SELECTORS.CLOSE_BUTTON);
if (firstInput.length) {
firstInput.focus();
} else {
@@ -298,6 +298,10 @@ function(
var closebutton = root.find(SELECTORS.CLOSE_BUTTON);
closebutton.on(CustomEvents.events.activate, function() {
var button = $(SELECTORS.DRAWER).attr('data-origin');
if (button) {
$('#' + button).focus();
}
PubSub.publish(Events.TOGGLE_VISIBILITY);
});
+2 -2
View File
@@ -36,8 +36,8 @@
{{< core/drawer}}
{{$drawercontent}}
<div id="message-drawer-{{uniqid}}" class="message-app" data-region="message-drawer" role="region">
<div class="closewidget bg-light border-bottom text-right">
<a class="text-dark" data-action="closedrawer" href="#">
<div class="closewidget text-right pr-2">
<a class="text-dark btn-link" data-action="closedrawer" href="#">
{{#pix}} i/window_close, core, {{#str}} closebuttontitle {{/str}} {{/pix}}
</a>
</div>
@@ -34,7 +34,7 @@
{}
}}
<div class="hidden border-bottom px-2 py-3" aria-hidden="true" data-region="view-contacts">
<div class="hidden border-bottom p-1 px-sm-2" aria-hidden="true" data-region="view-contacts">
<div class="d-flex align-items-center">
{{#isdrawer}}
<div class="align-self-stretch">
@@ -35,7 +35,7 @@
}}
<div
class="hidden bg-white position-relative border-bottom p-1 p-sm-2"
class="hidden bg-white position-relative border-bottom p-1 px-sm-2"
aria-hidden="true"
data-region="view-conversation"
>
@@ -33,7 +33,7 @@
{}
}}
<div class="border-bottom p-1 px-sm-2 py-sm-3" aria-hidden="false" {{^isdrawer}}data-in-panel="true"{{/isdrawer}} data-region="view-overview">
<div class="border-bottom p-1 px-sm-2" aria-hidden="false" {{^isdrawer}}data-in-panel="true"{{/isdrawer}} data-region="view-overview">
<div class="d-flex align-items-center">
<div class="input-group simplesearchform">
<input
@@ -34,7 +34,7 @@
}}
<div class="hidden border-bottom px-2 py-3 view-search" {{^isdrawer}}data-in-panel="true"{{/isdrawer}} aria-hidden="true" data-region="view-search">
<div class="hidden border-bottom p-1 px-sm-2 view-search" {{^isdrawer}}data-in-panel="true"{{/isdrawer}} aria-hidden="true" data-region="view-search">
<div class="d-flex align-items-center">
<a
class="mr-2 align-self-stretch d-flex align-items-center"
@@ -34,7 +34,7 @@
}}
<div class="hidden border-bottom px-2 py-3" aria-hidden="true" data-region="view-settings">
<div class="hidden border-bottom p-1 px-sm-2 pb-sm-3" aria-hidden="true" data-region="view-settings">
<div class="d-flex align-items-center">
{{#isdrawer}}
<div class="align-self-stretch" >
+6 -6
View File
@@ -12,16 +12,16 @@ Feature: Edited book chapters handle tags correctly
And the following "courses" exist:
| fullname | shortname | format |
| Course 1 | C1 | topics |
And the following "activity" exists:
| activity | book |
| course | C1 |
| idnumber | book1 |
| name | Test book |
| description | A book about dreams |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I add a "Book" to section "1" and I fill the form with:
| Name | Test book |
| Description | A book about dreams! |
And I log out
Scenario: Book chapter edition of custom tags works as expected
Given I log in as "teacher1"
+3 -79
View File
@@ -28,85 +28,9 @@ require_once(__DIR__.'/lib.php');
require_once($CFG->dirroot.'/mod/book/locallib.php');
/**
* Generate toc structure and titles
*
* @deprecated since Moodle 3.7
* @param array $chapters
* @param stdClass $book
* @param stdClass $cm
* @return array
*/
function booktool_print_get_toc($chapters, $book, $cm) {
debugging('booktool_print_get_toc() is deprecated. Please use booktool_print renderer
function render_print_book_toc().', DEBUG_DEVELOPER);
$first = true;
$titles = array();
$context = context_module::instance($cm->id);
$toc = ''; // Representation of toc (HTML).
switch ($book->numbering) {
case BOOK_NUM_NONE:
$toc .= html_writer::start_tag('div', array('class' => 'book_toc_none'));
break;
case BOOK_NUM_NUMBERS:
$toc .= html_writer::start_tag('div', array('class' => 'book_toc_numbered'));
break;
case BOOK_NUM_BULLETS:
$toc .= html_writer::start_tag('div', array('class' => 'book_toc_bullets'));
break;
case BOOK_NUM_INDENTED:
$toc .= html_writer::start_tag('div', array('class' => 'book_toc_indented'));
break;
}
$toc .= html_writer::tag('a', '', array('name' => 'toc')); // Representation of toc (HTML).
$toc .= html_writer::tag('h2', get_string('toc', 'mod_book'));
$toc .= html_writer::start_tag('ul');
foreach ($chapters as $ch) {
if (!$ch->hidden) {
$title = book_get_chapter_title($ch->id, $chapters, $book, $context);
if (!$ch->subchapter) {
if ($first) {
$toc .= html_writer::start_tag('li');
} else {
$toc .= html_writer::end_tag('ul');
$toc .= html_writer::end_tag('li');
$toc .= html_writer::start_tag('li');
}
} else {
if ($first) {
$toc .= html_writer::start_tag('li');
$toc .= html_writer::start_tag('ul');
$toc .= html_writer::start_tag('li');
} else {
$toc .= html_writer::start_tag('li');
}
}
$titles[$ch->id] = $title;
$toc .= html_writer::link(new moodle_url('#ch'.$ch->id), $title, array('title' => s($title)));
if (!$ch->subchapter) {
$toc .= html_writer::start_tag('ul');
} else {
$toc .= html_writer::end_tag('li');
}
$first = false;
}
}
$toc .= html_writer::end_tag('ul');
$toc .= html_writer::end_tag('li');
$toc .= html_writer::end_tag('ul');
$toc .= html_writer::end_tag('div');
$toc = str_replace('<ul></ul>', '', $toc); // Cleanup of invalid structures.
return array($toc, $titles);
function booktool_print_get_toc() {
throw new coding_exception(__FUNCTION__ . ' can not be used any more. Please use booktool_print renderer
function render_print_book_toc().');
}
+3
View File
@@ -1,5 +1,8 @@
This files describes API changes in the book code.
=== 3.11 ===
* Final deprecation - booktool_print_get_toc(). Please use render_print_book_toc() instead.
=== 3.8 ===
* The following functions have been finally deprecated and can not be used anymore:
@@ -130,8 +130,7 @@ Feature: The forum search allows users to perform advanced searches for forum po
And I press "Search forums"
And I should see "Advanced search"
And I set the field "Is tagged with" to "SearchedTag"
And I click on "[data-value='SearchedTag']" "css_element"
And I press the escape key
And I press the enter key
When I press "Search forums"
Then I should see "My subject"
And I should not see "Your subjective"
+14 -1
View File
@@ -2074,6 +2074,18 @@ function lti_calculate_custom_parameter($value) {
return implode(",", groups_get_user_groups($COURSE->id, $USER->id)[0]);
case 'Context.id.history':
return implode(",", get_course_history($COURSE));
case 'CourseSection.timeFrame.begin':
if (empty($COURSE->startdate)) {
return "";
}
$dt = new DateTime("@$COURSE->startdate", new DateTimeZone('UTC'));
return $dt->format(DateTime::ATOM);
case 'CourseSection.timeFrame.end':
if (empty($COURSE->enddate)) {
return "";
}
$dt = new DateTime("@$COURSE->enddate", new DateTimeZone('UTC'));
return $dt->format(DateTime::ATOM);
}
return null;
}
@@ -3739,7 +3751,8 @@ function lti_get_capabilities() {
'CourseSection.label' => 'context_label',
'CourseSection.sourcedId' => 'lis_course_section_sourcedid',
'CourseSection.longDescription' => '$COURSE->summary',
'CourseSection.timeFrame.begin' => '$COURSE->startdate',
'CourseSection.timeFrame.begin' => null,
'CourseSection.timeFrame.end' => null,
'ResourceLink.id' => 'resource_link_id',
'ResourceLink.title' => 'resource_link_title',
'ResourceLink.description' => 'resource_link_description',
+1 -3
View File
@@ -21,9 +21,7 @@ Feature: Rename external tools via inline editing
And I am on "Course 1" course homepage with editing mode on
And I add a "External tool" to section "1" and I fill the form with:
| Activity name | Test tool activity 1 |
And I click on "Edit title" "link" in the "li#section-1" "css_element"
And I set the field "New name for activity Test tool activity 1" to "Test tool activity renamed"
And I press the enter key
And I set the field "Edit title" in the "li#section-1" "css_element" to "Test tool activity renamed"
And I navigate to "Setup > Gradebook setup" in the course gradebook
Then I should not see "Test tool activity 1"
And I should see "Test tool activity renamed"
+5 -2
View File
@@ -397,7 +397,8 @@ class behat_mod_quiz extends behat_question_base {
$this->execute('behat_general::assert_page_contains_text', $this->escape(get_string('edittitleinstructions')));
$this->execute('behat_forms::i_set_the_field_to', array('maxmark', $this->escape($newmark) . chr(10)));
$this->execute('behat_general::i_type', [$newmark]);
$this->execute('behat_general::i_press_named_key', ['', 'enter']);
}
/**
@@ -653,7 +654,9 @@ class behat_mod_quiz extends behat_question_base {
$this->execute('behat_general::assert_page_contains_text', $this->escape(get_string('edittitleinstructions')));
$this->execute('behat_forms::i_set_the_field_to', array('section', $this->escape($sectionheading) . chr(10)));
$this->execute('behat_general::i_press_named_key', ['', 'backspace']);
$this->execute('behat_general::i_type', [$sectionheading]);
$this->execute('behat_general::i_press_named_key', ['', 'enter']);
}
/**
@@ -17,16 +17,19 @@ Feature: Edit quiz marks with no attempts
And the following "activities" exist:
| activity | name | course | idnumber | grade | decimalpoints | questiondecimalpoints |
| quiz | Quiz 1 | C1 | quiz1 | 20 | 2 | -1 |
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I add a "True/False" question to the "Quiz 1" quiz with:
| Question name | First question |
| Question text | Answer me |
| Default mark | 2.0 |
And I add a "True/False" question to the "Quiz 1" quiz with:
| Question name | Second question |
| Question text | Answer again |
| Default mark | 3.0 |
And the following "question categories" exist:
| contextlevel | reference | name |
| Course | C1 | Test questions |
And the following "questions" exist:
| questioncategory | qtype | name | questiontext |
| Test questions | truefalse | First question | Answer me |
| Test questions | truefalse | Second question | Answer again |
And quiz "Quiz 1" contains the following questions:
| question | page | maxmark |
| First question | 1 | 2.0 |
| Second question | 1 | 3.0 |
And I am on the "Quiz 1" "mod_quiz > Edit" page logged in as "teacher1"
@javascript
Scenario: Set the max mark for a question.
+1 -1
View File
@@ -96,7 +96,7 @@ class get_available_gateways extends external_api {
new external_single_structure([
'shortname' => new external_value(PARAM_PLUGIN, 'Name of the plugin'),
'name' => new external_value(PARAM_TEXT, 'Human readable name of the gateway'),
'description' => new external_value(PARAM_TEXT, 'description of the gateway'),
'description' => new external_value(PARAM_RAW, 'description of the gateway'),
'surcharge' => new external_value(PARAM_INT, 'percentage of surcharge when using the gateway'),
'cost' => new external_value(PARAM_TEXT,
'Cost in human-readable form (amount plus surcharge with currency sign)'),
+2
View File
@@ -23,6 +23,8 @@
*/
function xmldb_paygw_paypal_install() {
global $CFG;
// Enable the Paypal payment gateway on installation. It still needs to be configured and enabled for accounts.
$order = (!empty($CFG->paygw_plugins_sortorder)) ? explode(',', $CFG->paygw_plugins_sortorder) : [];
set_config('paygw_plugins_sortorder', join(',', array_merge($order, ['paypal'])));
+1 -1
View File
@@ -45,7 +45,7 @@
<input class="custom-control-input" type="radio" name="payby" id="id-payby-{{uniqid}}-{{shortname}}" data-cost="{{cost}}" data-surcharge="{{surcharge}}" value="{{shortname}}" {{#checked}} checked="checked" {{/checked}} />
<label class="custom-control-label bg-light border p-3 my-3" for="id-payby-{{uniqid}}-{{shortname}}">
<p class="h3">{{name}}</p>
<p class="content mb-2">{{description}}</p>
<p class="content mb-2">{{{description}}}</p>
{{#pix}} img, paygw_{{shortname}} {{/pix}}
</label>
</div>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3 -4
View File
@@ -211,7 +211,7 @@ define(['jquery', 'core/dragdrop'], function($, dragDrop) {
// Resize them to the same size.
$('.dropzones .droppreview').css('padding', '0');
var numGroups = $('select.draggroup').first().find('option').length;
var numGroups = $('.draggroup select').first().find('option').length;
for (var group = 1; group <= numGroups; group++) {
dragDropToImageForm.resizeAllDragsAndDropsInGroup(group);
}
@@ -398,9 +398,8 @@ define(['jquery', 'core/dragdrop'], function($, dragDrop) {
top = Math.round(dropPosition.top - backgroundPosition.top);
// Constrain coordinates to be inside the background.
// The -10 here matches the +10 in resizeAllDragsAndDropsInGroup().
left = Math.max(0, Math.min(left, backgroundImage.width() - drop.width() - 10));
top = Math.max(0, Math.min(top, backgroundImage.height() - drop.height() - 10));
left = Math.round(Math.max(0, Math.min(left, backgroundImage.outerWidth() - drop.outerWidth())));
top = Math.round(Math.max(0, Math.min(top, backgroundImage.outerHeight() - drop.outerHeight())));
// Update the form.
dragDropToImageForm.form.setFormValue('drops', [dropNo, 'xleft'], left);
+1
View File
@@ -99,6 +99,7 @@ form.mform fieldset#id_previewareaheader .dragitems {
form.mform fieldset#id_previewareaheader .droppreview {
position: absolute;
cursor: move;
white-space: nowrap;
}
.que.ddimageortext .dragitems.readonly .drag {
+7 -2
View File
@@ -106,7 +106,7 @@ class qtype_essay_renderer extends qtype_renderer {
*/
public function files_input(question_attempt $qa, $numallowed,
question_display_options $options) {
global $CFG;
global $CFG, $COURSE;
require_once($CFG->dirroot . '/lib/form/filemanager.php');
$pickeroptions = new stdClass();
@@ -122,7 +122,12 @@ class qtype_essay_renderer extends qtype_renderer {
$pickeroptions->accepted_types = $qa->get_question()->filetypeslist;
$fm = new form_filemanager($pickeroptions);
$fm->options->maxbytes = $qa->get_question()->maxbytes;;
$fm->options->maxbytes = get_user_max_upload_file_size(
$this->page->context,
$CFG->maxbytes,
$COURSE->maxbytes,
$qa->get_question()->maxbytes
);
$filesrenderer = $this->page->get_renderer('core', 'files');
$text = '';
@@ -8,8 +8,8 @@ I need to choose the appropriate maxbytes for attachments
| username | firstname | lastname | email |
| teacher1 | T1 | Teacher1 | teacher1@moodle.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
| fullname | shortname | category | maxbytes |
| Course 1 | C1 | 0 | 1048576 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
@@ -18,15 +18,24 @@ I need to choose the appropriate maxbytes for attachments
| Course | C1 | Test questions |
And the following "questions" exist:
| questioncategory | qtype | name | template | attachments | maxbytes |
| Test questions | essay | essay-1-20MB | editor | 1 | 20971520 |
Given I log in as "teacher1"
| Test questions | essay | essay-1-512KB | editor | 1 | 524288 |
| Test questions | essay | essay-1-max | editor | 1 | 0 |
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I navigate to "Question bank" in current page administration
@javascript @_switch_window
Scenario: Preview an Essay question and see the allowed maximum file sizes and number of attachments.
When I choose "Preview" action for "essay-1-20MB" in the question bank
When I choose "Preview" action for "essay-1-512KB" in the question bank
And I switch to "questionpreview" window
And I should see "Please write a story about a frog."
And I should see "Maximum file size: 20MB, maximum number of files: 1"
Then I should see "Please write a story about a frog."
And I should see "Maximum file size: 512KB, maximum number of files: 1"
And I switch to the main window
@javascript @_switch_window
Scenario: Preview an Essay question with Course upload limit and see the allowed maximum file size.
When I choose "Preview" action for "essay-1-max" in the question bank
And I switch to "questionpreview" window
Then I should see "Please write a story about a frog."
And I should see "Maximum file size: 1MB, maximum number of files: 1"
And I switch to the main window
+4 -18
View File
@@ -28,15 +28,11 @@ Feature: Managers can create and manage tag collections
Scenario: Adding tag collections
When I follow "Hobbies"
Then I should see "Nothing to display"
And I log out
Scenario: Editing tag collections
When I click on "Edit tag collection name" "link" in the "//table[contains(@class,'tag-collections-table')]//tr[contains(.,'Hobbies')]" "xpath_element"
And I set the field "New name for tag collection Hobbies" to "Newname"
And I press the enter key
When I set the field "Edit tag collection name" in the "//table[contains(@class,'tag-collections-table')]//tr[contains(.,'Hobbies')]" "xpath_element" to "Newname"
Then I should not see "Hobbies"
And I should see "Newname"
And I log out
Scenario: Resorting tag collections
When I follow "Add tag collection"
@@ -48,41 +44,34 @@ Feature: Managers can create and manage tag collections
And "Blogging" "link" should appear before "Hobbies" "link"
And I click on "Move down" "link" in the "Blogging" "table_row"
And "Blogging" "link" should appear after "Hobbies" "link"
And I log out
Scenario: Deleting tag collections
When I click on "Delete" "link" in the "Hobbies" "table_row"
Then I should see "Are you sure you want to delete tag collection \"Hobbies\"?"
And I press "Yes"
And I should not see "Hobbies"
And I log out
Scenario: Assigning tag area to tag collection
And I should see "User interests" in the "//table[contains(@class,'tag-collections-table')]//tr[contains(.,'Default collection')]" "xpath_element"
And I should not see "User interests" in the "//table[contains(@class,'tag-collections-table')]//tr[contains(.,'Hobbies')]" "xpath_element"
When I click on "Change tag collection" "link" in the "//table[contains(@class,'tag-areas-table')]//tr[contains(.,'User interests')]" "xpath_element"
And I set the field "Change tag collection of area User interests" to "Hobbies"
When I set the field "Change tag collection" in the "//table[contains(@class,'tag-areas-table')]//tr[contains(.,'User interests')]" "xpath_element" to "Hobbies"
Then I should not see "User interests" in the "//table[contains(@class,'tag-collections-table')]//tr[contains(.,'Default collection')]" "xpath_element"
And I should see "User interests" in the "//table[contains(@class,'tag-collections-table')]//tr[contains(.,'Hobbies')]" "xpath_element"
And I should see "Hobbies" in the "//table[contains(@class,'tag-areas-table')]//tr[contains(.,'User interests')]" "xpath_element"
And I log out
Scenario: Disabling tag areas
When I click on "Disable" "link" in the "//table[contains(@class,'tag-areas-table')]//tr[contains(.,'User interests')]" "xpath_element"
And I should not see "User interests" in the "table.tag-collections-table" "css_element"
And I click on "Enable" "link" in the "//table[contains(@class,'tag-areas-table')]//tr[contains(.,'User interests')]" "xpath_element"
And I should see "User interests" in the "//table[contains(@class,'tag-collections-table')]//tr[contains(.,'Default collection')]" "xpath_element"
And I log out
Scenario: Deleting non-empty tag collections
When I click on "Change tag collection" "link" in the "//table[contains(@class,'tag-areas-table')]//tr[contains(.,'User interests')]" "xpath_element"
And I set the field "Change tag collection of area User interests" to "Hobbies"
When I set the field "Change tag collection" in the "//table[contains(@class,'tag-areas-table')]//tr[contains(.,'User interests')]" "xpath_element" to "Hobbies"
And I click on "Delete" "link" in the "Hobbies" "table_row"
Then I should see "Are you sure you want to delete tag collection \"Hobbies\"?"
And I press "Yes"
And I should not see "Hobbies"
And I should see "User interests" in the "//table[contains(@class,'tag-collections-table')]//tr[contains(.,'Default collection')]" "xpath_element"
And I log out
Scenario: Moving tags when changing tag collections
And I open my profile in edit mode
@@ -90,8 +79,7 @@ Feature: Managers can create and manage tag collections
And I set the field "List of interests" to "Swimming, Tag0, Tag3"
And I press "Update profile"
And I navigate to "Appearance > Manage tags" in site administration
When I click on "Change tag collection" "link" in the "//table[contains(@class,'tag-areas-table')]//tr[contains(.,'User interests')]" "xpath_element"
And I set the field "Change tag collection of area User interests" to "Hobbies"
When I set the field "Change tag collection" in the "//table[contains(@class,'tag-areas-table')]//tr[contains(.,'User interests')]" "xpath_element" to "Hobbies"
And I follow "Hobbies"
Then I should see "Swimming"
And I should see "Tag0"
@@ -107,7 +95,6 @@ Feature: Managers can create and manage tag collections
And I should see "Tag3"
And I should see "Tag1"
And I should see "Tag2"
And I log out
Scenario: Creating searchable and non-searchable tag collections
And I follow "Add tag collection"
@@ -129,4 +116,3 @@ Feature: Managers can create and manage tag collections
And I click on "Site pages" "list_item" in the "Navigation" "block"
And I click on "Tags" "link" in the "Navigation" "block"
And "Select tag collection" "select" should not exist
And I log out
+4 -11
View File
@@ -158,19 +158,14 @@ Feature: Users can edit tags to add description or rename
And I navigate to "Appearance > Manage tags" in site administration
And I follow "Default collection"
# Renaming tag to a valid name
And I click on "Edit tag name" "link" in the "Cat" "table_row"
And I set the field "New name for tag Cat" to "Kitten"
And I press the enter key
And I set the field "Edit tag name" in the "Cat" "table_row" to "Kitten"
Then I should not see "Cat"
And "New name for tag" "field" should not exist
And I wait until "Kitten" "link" exists
And I follow "Default collection"
And I should see "Kitten"
And I should not see "Cat"
# Renaming tag to an invalid name
And I click on "Edit tag name" "link" in the "Turtle" "table_row"
And I set the field "New name for tag Turtle" to "DOG"
And I press the enter key
And I set the field "Edit tag name" in the "Turtle" "table_row" to "DOG"
And I should see "The tag name is already in use. Do you want to combine these tags?"
And I click on "Cancel" "button" in the "Confirm" "dialogue"
And "New name for tag" "field" should not exist
@@ -183,7 +178,7 @@ Feature: Users can edit tags to add description or rename
And I should not see "DOG"
# Cancel tag renaming
And I click on "Edit tag name" "link" in the "Dog" "table_row"
And I set the field "New name for tag Dog" to "Penguin"
And I type "Penguin"
And I press the escape key
And "New name for tag" "field" should not exist
And I should see "Turtle"
@@ -197,9 +192,7 @@ Feature: Users can edit tags to add description or rename
When I log in as "manager1"
And I navigate to "Appearance > Manage tags" in site administration
And I follow "Default collection"
And I click on "Edit tag name" "link" in the "Turtle" "table_row"
And I set the field "New name for tag Turtle" to "DOG"
And I press the enter key
And I set the field "Edit tag name" in the "Turtle" "table_row" to "DOG"
And I should see "The tag name is already in use. Do you want to combine these tags?"
And I press "Yes"
Then I should not see "Turtle"
-8
View File
@@ -124,10 +124,6 @@ $right-drawer-width: 320px;
opacity: 1;
}
.closewidget {
display: none;
}
&.hidden {
display: block;
right: $right-drawer-width * -1;
@@ -147,10 +143,6 @@ $right-drawer-width: 320px;
height: 100%;
z-index: $zindex-fixed;
}
.closewidget {
display: block;
padding: 0 0.2rem;
}
}
body.drawer-open-left,
body.drawer-open-right {
-5
View File
@@ -14174,8 +14174,6 @@ body.drawer-ease {
padding: 0;
visibility: visible;
opacity: 1; }
[data-region=right-hand-drawer] .closewidget {
display: none; }
[data-region=right-hand-drawer].hidden {
display: block;
right: -320px;
@@ -14191,9 +14189,6 @@ body.drawer-ease {
top: 0;
height: 100%;
z-index: 1030; }
[data-region=right-hand-drawer] .closewidget {
display: block;
padding: 0 0.2rem; }
body.drawer-open-left,
body.drawer-open-right {
overflow: hidden; } }
-5
View File
@@ -14389,8 +14389,6 @@ body.drawer-ease {
padding: 0;
visibility: visible;
opacity: 1; }
[data-region=right-hand-drawer] .closewidget {
display: none; }
[data-region=right-hand-drawer].hidden {
display: block;
right: -320px;
@@ -14406,9 +14404,6 @@ body.drawer-ease {
top: 0;
height: 100%;
z-index: 1030; }
[data-region=right-hand-drawer] .closewidget {
display: block;
padding: 0 0.2rem; }
body.drawer-open-left,
body.drawer-open-right {
overflow: hidden; } }
+3 -1
View File
@@ -23,6 +23,7 @@
*/
require_once("../config.php");
require_once($CFG->dirroot . '/course/lib.php');
$formaction = required_param('formaction', PARAM_LOCALURL);
$id = required_param('id', PARAM_INT);
@@ -78,7 +79,8 @@ if ($formaction == 'bulkchange.php') {
if (empty($plugin) AND $operationname == 'download_participants') {
// Check permissions.
if (has_capability('moodle/course:manageactivities', $context)) {
$pagecontext = ($course->id == SITEID) ? context_system::instance() : $context;
if (course_can_view_participants($pagecontext)) {
$plugins = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
if (isset($plugins[$dataformat])) {
if ($plugins[$dataformat]->is_enabled()) {
+17 -11
View File
@@ -101,6 +101,12 @@ class user_filtering {
// Fist the new filter form.
$this->_addform = new user_add_filter_form($baseurl, array('fields' => $this->_fields, 'extraparams' => $extraparams));
if ($adddata = $this->_addform->get_data()) {
// Clear previous filters.
if (!empty($adddata->replacefilters)) {
$SESSION->user_filtering = [];
}
// Add new filters.
foreach ($this->_fields as $fname => $field) {
$data = $field->check_data($adddata);
if ($data === false) {
@@ -111,19 +117,16 @@ class user_filtering {
}
$SESSION->user_filtering[$fname][] = $data;
}
// Clear the form.
$_POST = array();
$this->_addform = new user_add_filter_form($baseurl, array('fields' => $this->_fields, 'extraparams' => $extraparams));
}
// Now the active filters.
$this->_activeform = new user_active_filter_form($baseurl, array('fields' => $this->_fields, 'extraparams' => $extraparams));
if ($adddata = $this->_activeform->get_data()) {
if (!empty($adddata->removeall)) {
if ($activedata = $this->_activeform->get_data()) {
if (!empty($activedata->removeall)) {
$SESSION->user_filtering = array();
} else if (!empty($adddata->removeselected) and !empty($adddata->filter)) {
foreach ($adddata->filter as $fname => $instances) {
} else if (!empty($activedata->removeselected) and !empty($activedata->filter)) {
foreach ($activedata->filter as $fname => $instances) {
foreach ($instances as $i => $val) {
if (empty($val)) {
continue;
@@ -135,11 +138,14 @@ class user_filtering {
}
}
}
// Clear+reload the form.
$_POST = array();
$this->_activeform = new user_active_filter_form($baseurl, array('fields' => $this->_fields, 'extraparams' => $extraparams));
}
// Now the active filters.
// Rebuild the forms if filters data was processed.
if ($adddata || $activedata) {
$_POST = []; // Reset submitted data.
$this->_addform = new user_add_filter_form($baseurl, ['fields' => $this->_fields, 'extraparams' => $extraparams]);
$this->_activeform = new user_active_filter_form($baseurl, ['fields' => $this->_fields, 'extraparams' => $extraparams]);
}
}
/**
+11 -2
View File
@@ -36,6 +36,8 @@ class user_add_filter_form extends moodleform {
* Form definition.
*/
public function definition() {
global $SESSION;
$mform =& $this->_form;
$fields = $this->_customdata['fields'];
$extraparams = $this->_customdata['extraparams'];
@@ -54,8 +56,15 @@ class user_add_filter_form extends moodleform {
}
}
// Add button.
$mform->addElement('submit', 'addfilter', get_string('addfilter', 'filters'));
// Add buttons.
$replacefiltersbutton = $mform->createElement('submit', 'replacefilters', get_string('replacefilters', 'filters'));
$addfilterbutton = $mform->createElement('submit', 'addfilter', get_string('addfilter', 'filters'));
$buttons = array_filter([
empty($SESSION->user_filtering) ? null : $replacefiltersbutton,
$addfilterbutton,
]);
$mform->addGroup($buttons);
}
}
+2 -2
View File
@@ -29,9 +29,9 @@
defined('MOODLE_INTERNAL') || die();
$version = 2020112100.00; // 20201109 = branching date YYYYMMDD - do not modify!
$version = 2020112700.00; // 20201109 = branching date YYYYMMDD - do not modify!
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
$release = '3.11dev (Build: 20201121)';// Human-friendly version name
$release = '3.11dev (Build: 20201127)';// Human-friendly version name
$branch = '311'; // This version's branch.
$maturity = MATURITY_ALPHA; // This version's maturity level.