Compare commits
138 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 54b614ca7e | |||
| 4125c3c470 | |||
| 258cdfaa6a | |||
| 8c2792cb5d | |||
| 25d4bc7ffc | |||
| d98b39a597 | |||
| 191badf5ad | |||
| c4ba598a0b | |||
| f77ea5bffa | |||
| 3d11b25cce | |||
| 8a55673463 | |||
| 230f7a2d1b | |||
| 87344d03e1 | |||
| aa392ce73a | |||
| 6220f712ab | |||
| 714b88a1b8 | |||
| bbadf7a02c | |||
| 57b97b29b7 | |||
| 6fe4eb2a86 | |||
| 8a0274423d | |||
| ddd80a5d2c | |||
| 50bf83fede | |||
| cafbfc6076 | |||
| 2b22716dc2 | |||
| c59d4f9612 | |||
| e3ce532831 | |||
| 87e0d39e19 | |||
| 90aceb5e6c | |||
| 7d1996fb3a | |||
| cb9583c21c | |||
| 633c6d7e80 | |||
| 76ed3885a9 | |||
| f4456f6afe | |||
| 21384101e1 | |||
| 61cedbd56e | |||
| 376e57d267 | |||
| 9de11ea641 | |||
| d793ac8e5e | |||
| 3828f4b484 | |||
| 4c5288b32c | |||
| 284b201a28 | |||
| 31c078e183 | |||
| 6b8961b7ae | |||
| 7ef79076fe | |||
| 1bf4ac0c11 | |||
| 2c4cc3caf6 | |||
| 3fd34dd999 | |||
| 4dcb3b244c | |||
| 6272227934 | |||
| 36e0a49c00 | |||
| 1ee3d6323c | |||
| 4ed613fbc2 | |||
| 123fc91eea | |||
| 83c9f41a47 | |||
| 3bec78df9b | |||
| 6793732790 | |||
| a9ce394b63 | |||
| a8c9e82653 | |||
| 9ceb538216 | |||
| 5073d3542b | |||
| 34f352ded2 | |||
| 1125231af0 | |||
| 8a147fee4b | |||
| 1d4a1bc258 | |||
| 4119bd99a9 | |||
| a287546036 | |||
| 6102f8553a | |||
| 982dda9417 | |||
| e5744719c9 | |||
| 1b513e8c42 | |||
| 1aa9a5b11f | |||
| a313e53b54 | |||
| 983a79347b | |||
| f6a3cbe339 | |||
| cccef66f0c | |||
| ad58a4c3c3 | |||
| 2eb859a839 | |||
| 3583300da5 | |||
| 2d9ee56211 | |||
| 5ba22c14a1 | |||
| b3fab1d80e | |||
| d5cd3c95b0 | |||
| 0448a7b267 | |||
| 7b841a0f39 | |||
| 25519bc4d8 | |||
| 5f6923adf1 | |||
| a08fcb8d13 | |||
| 9fbbdc8a0a | |||
| 56781dec40 | |||
| 15b31bfb39 | |||
| fc65a2e4a2 | |||
| 3436523f17 | |||
| 8062e43941 | |||
| a28e929a74 | |||
| 838f1bfa7b | |||
| 88a18df37f | |||
| cad3403e71 | |||
| 34b551b4e7 | |||
| a62e36a9f6 | |||
| 7dd9098338 | |||
| a69d81beaa | |||
| 4434756fd6 | |||
| 08e64ba626 | |||
| caa944907a | |||
| 19815c2c2c | |||
| 8de4b14be7 | |||
| 2016d64b83 | |||
| 3eedb8a9c9 | |||
| 7f8d209ba1 | |||
| 3f2ffcce08 | |||
| 18f46b32a2 | |||
| ffb762aae0 | |||
| 9b6a36114c | |||
| 8ef1574c47 | |||
| 09a93cb6eb | |||
| b1fa2a9976 | |||
| 310da5eee7 | |||
| f9e7aa5820 | |||
| 91c1bba220 | |||
| 8984ffef4f | |||
| 419e169fa5 | |||
| 73003c7c96 | |||
| 4619b5128d | |||
| 9a5389830e | |||
| 44cd084562 | |||
| ca6f1479ad | |||
| 2e592749aa | |||
| e261c7ed25 | |||
| 57c492a6a5 | |||
| 442e1f33c9 | |||
| 9cde5a75bb | |||
| 191351d2a3 | |||
| fec4afdbd3 | |||
| 02daea22d5 | |||
| df61870abc | |||
| cb42a0ad6a | |||
| 9b3be75879 | |||
| 1b9df59e3c |
@@ -3539,6 +3539,8 @@
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_max_input_vars" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_xmlrpc_usage" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="4.0" requires="3.6">
|
||||
@@ -3729,6 +3731,200 @@
|
||||
<ON_CHECK message="iscustomadminwarnings" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_xmlrpc_usage" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="4.1" requires="3.9">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="10.4" />
|
||||
<VENDOR name="mysql" version="5.7" />
|
||||
<VENDOR name="postgres" version="12" />
|
||||
<VENDOR name="mssql" version="14.0" />
|
||||
<VENDOR name="oracle" version="19" />
|
||||
</DATABASE>
|
||||
<PHP version="7.4.0" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="mbstringrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
<PHP_EXTENSION name="sodium" level="optional"/>
|
||||
<PHP_EXTENSION name="exif" level="optional"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_max_input_vars" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_admin_dir_usage" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="iscustomadminwarnings" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_xmlrpc_usage" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
@@ -7,8 +7,8 @@ Feature: Behat steps for interacting with form work
|
||||
@javascript
|
||||
Scenario: Test fields in containers
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname |
|
||||
| Course 1 | C1 |
|
||||
| fullname | shortname | summary | summaryformat |
|
||||
| Course 1 | C1 | Red | 1 |
|
||||
When I log in as "admin"
|
||||
And I am on "Course 1" course homepage
|
||||
# Just get to any form.
|
||||
@@ -17,11 +17,15 @@ Feature: Behat steps for interacting with form work
|
||||
And I set the following fields in the "Appearance" "fieldset" to these values:
|
||||
| Show activity reports | Yes |
|
||||
| Number of announcements | 1 |
|
||||
And I set the following fields in the "Description" "fieldset" to these values:
|
||||
| Course summary | Green |
|
||||
Then the field "Show activity reports" in the "Appearance" "fieldset" matches value "Yes"
|
||||
And the field "Show activity reports" in the "Appearance" "fieldset" does not match value "No"
|
||||
And the following fields in the "region-main" "region" match these values:
|
||||
| Course full name | Frog |
|
||||
| Number of announcements | 1 |
|
||||
| Course summary | Green |
|
||||
And the following fields in the "region-main" "region" do not match these values:
|
||||
| Course full name | Course 1 |
|
||||
| Number of announcements | 5 |
|
||||
| Course summary | Red |
|
||||
|
||||
@@ -323,7 +323,7 @@ class accessibility {
|
||||
/**
|
||||
* This function runs one specified check on the html item
|
||||
*
|
||||
* @param string $html The html string to be analysed; might be NULL.
|
||||
* @param string|null $html The html string to be analysed; might be NULL.
|
||||
* @param int $contentid The content area ID
|
||||
* @param int $errid The error ID
|
||||
* @param string $check The check name to run
|
||||
@@ -333,7 +333,7 @@ class accessibility {
|
||||
* @throws \dml_exception
|
||||
*/
|
||||
public static function run_one_check(
|
||||
string $html = null,
|
||||
?string $html,
|
||||
int $contentid,
|
||||
int $errid,
|
||||
string $check,
|
||||
|
||||
@@ -63,4 +63,67 @@ class behat_tool_lp extends behat_base {
|
||||
$this->execute('behat_general::i_click_on', [$xpathtarget, 'xpath_element']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert page names to URLs for steps like 'When I am on the "[identifier]" "[page type]" page'.
|
||||
*
|
||||
* Recognised page names are:
|
||||
* | pagetype | name meaning | description |
|
||||
* | Course competencies | Course name | The course competencies page |
|
||||
*
|
||||
* @param string $page identifies which type of page this is, e.g. 'Course competencies'.
|
||||
* @param string $identifier identifies the particular page, e.g. 'C1'.
|
||||
* @return moodle_url the corresponding URL.
|
||||
* @throws Exception with a meaningful error message if the specified page cannot be found.
|
||||
*/
|
||||
protected function resolve_page_instance_url(string $page, string $identifier): moodle_url {
|
||||
switch (strtolower($page)) {
|
||||
case 'course competencies':
|
||||
$courseid = $this->get_course_id($identifier);
|
||||
return new moodle_url('/admin/tool/lp/coursecompetencies.php', [
|
||||
'courseid' => $courseid,
|
||||
]);
|
||||
default:
|
||||
throw new Exception("Unrecognised page type '{$page}'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of the exact named selectors for the component.
|
||||
*
|
||||
* @return behat_component_named_selector[]
|
||||
*/
|
||||
public static function get_exact_named_selectors(): array {
|
||||
return [
|
||||
new behat_component_named_selector('competency', [
|
||||
"//*[@data-region='coursecompetencies']//table[contains(@class,'managecompetencies')]".
|
||||
"//tr[contains(., //a[@title='View details'][contains(., %locator%)])]",
|
||||
]),
|
||||
new behat_component_named_selector('learning plan', [
|
||||
"//*[@data-region='plan-competencies']//table[contains(@class,'managecompetencies')]".
|
||||
"//tr[@data-node='user-competency'][contains(., //a[@data-usercompetency='true'][contains(., %locator%)])]",
|
||||
]),
|
||||
new behat_component_named_selector('competency description', [
|
||||
"//td/p[contains(., %locator%)]",
|
||||
]),
|
||||
new behat_component_named_selector('competency grade', [
|
||||
"//span[contains(concat(' ', normalize-space(@class), ' '), ' badge ')][contains(., %locator%)]",
|
||||
]),
|
||||
new behat_component_named_selector('learning plan rating', [
|
||||
"//td[position()=2][contains(., %locator%)]",
|
||||
]),
|
||||
new behat_component_named_selector('learning plan proficiency', [
|
||||
"//td[position()=3][contains(., %locator%)]",
|
||||
]),
|
||||
new behat_component_named_selector('competency page proficiency', [
|
||||
"//dt[contains(., 'Proficient')]/following-sibling::dd[1][contains(., %locator%)]",
|
||||
]),
|
||||
new behat_component_named_selector('competency page rating', [
|
||||
"//dt[contains(., 'Rating')]/following-sibling::dd[1][contains(., %locator%)]",
|
||||
]),
|
||||
new behat_component_named_selector('competency page related competency', [
|
||||
"//*[@data-region='relatedcompetencies']//a[contains(., %locator%)]",
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
@tool @tool_lp @javascript
|
||||
Feature: View competencies
|
||||
In order to access competency information
|
||||
As a user
|
||||
I need to view user competencies
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname |
|
||||
| student1 | Student | first |
|
||||
| teacher1 | Teacher | first |
|
||||
And the following "system role assigns" exist:
|
||||
| user | role | contextlevel |
|
||||
| teacher1 | editingteacher | System |
|
||||
And the following "permission overrides" exist:
|
||||
| capability | permission | role | contextlevel | reference |
|
||||
| moodle/competency:planview | Allow | editingteacher | System | |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname |
|
||||
| Course 1 | C1 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And the following "scales" exist:
|
||||
| name | scale |
|
||||
| Test Scale | Bad, Good, Great |
|
||||
And the following "core_competency > frameworks" exist:
|
||||
| shortname | idnumber | scale |
|
||||
| Cookery | cookery | Test Scale |
|
||||
| Literacy | literacy | Test Scale |
|
||||
And the following "core_competency > competencies" exist:
|
||||
| shortname | idnumber | description | competencyframework |
|
||||
| Salads | salads | Salads are important | cookery |
|
||||
| Desserts | desserts | Desserts are important | cookery |
|
||||
| Cakes | cakes | Cakes are important | cookery |
|
||||
| Reading | reading | Reading is important | literacy |
|
||||
| Writing | writing | Writing is important | literacy |
|
||||
And the following "core_competency > related_competencies" exist:
|
||||
| competency | relatedcompetency |
|
||||
| desserts | cakes |
|
||||
And the following "core_competency > plans" exist:
|
||||
| name | description | competencies | user |
|
||||
| Cookery | Cookery is important | salads, desserts, cakes | student1 |
|
||||
| Literacy | Literacy is important | reading, writing | student1 |
|
||||
And the following "core_competency > course_competencies" exist:
|
||||
| course | competency |
|
||||
| C1 | salads |
|
||||
| C1 | desserts |
|
||||
| C1 | cakes |
|
||||
| C1 | reading |
|
||||
| C1 | writing |
|
||||
And the following "core_competency > user_competency" exist:
|
||||
| competency | user | grade |
|
||||
| salads | student1 | Good |
|
||||
| desserts | student1 | Great |
|
||||
| cakes | student1 | Great |
|
||||
And the following "core_competency > user_competency_courses" exist:
|
||||
| course | competency | user | grade |
|
||||
| C1 | salads | student1 | Good |
|
||||
| C1 | desserts | student1 | Great |
|
||||
| C1 | cakes | student1 | Great |
|
||||
|
||||
Scenario: Student view
|
||||
# Course competencies
|
||||
Given I am on the "C1" "tool_lp > course competencies" page logged in as "student1"
|
||||
Then I should see "You are proficient in 3 out of 5 competencies in this course"
|
||||
|
||||
And "Salads are important" "tool_lp > competency description" should exist in the "Salads" "tool_lp > competency"
|
||||
And "Good" "tool_lp > competency grade" should exist in the "Salads" "tool_lp > competency"
|
||||
|
||||
And "Desserts are important" "tool_lp > competency description" should exist in the "Desserts" "tool_lp > competency"
|
||||
And "Great" "tool_lp > competency grade" should exist in the "Desserts" "tool_lp > competency"
|
||||
|
||||
And "Cakes are important" "tool_lp > competency description" should exist in the "Cakes" "tool_lp > competency"
|
||||
And "Great" "tool_lp > competency grade" should exist in the "Cakes" "tool_lp > competency"
|
||||
|
||||
And "Reading is important" "tool_lp > competency description" should exist in the "Reading" "tool_lp > competency"
|
||||
And "Good" "tool_lp > competency grade" should not exist in the "Reading" "tool_lp > competency"
|
||||
And "Great" "tool_lp > competency grade" should not exist in the "Reading" "tool_lp > competency"
|
||||
And "Bad" "tool_lp > competency grade" should not exist in the "Reading" "tool_lp > competency"
|
||||
|
||||
And "Writing is important" "tool_lp > competency description" should exist in the "Writing" "tool_lp > competency"
|
||||
And "Good" "tool_lp > competency grade" should not exist in the "Writing" "tool_lp > competency"
|
||||
And "Great" "tool_lp > competency grade" should not exist in the "Writing" "tool_lp > competency"
|
||||
And "Bad" "tool_lp > competency grade" should not exist in the "Writing" "tool_lp > competency"
|
||||
|
||||
# Course competencies details
|
||||
And I click on "Desserts" "link" in the "Desserts" "tool_lp > competency"
|
||||
And I should see "Desserts are important"
|
||||
And "Yes" "tool_lp > competency page proficiency" should exist
|
||||
And "Great" "tool_lp > competency page rating" should exist
|
||||
|
||||
# Course competencies summary
|
||||
And I click on "Cakes" "link"
|
||||
And I should see "Cakes are important"
|
||||
|
||||
# Learning plans
|
||||
And I click on "Close" "button" in the "Cakes" "dialogue"
|
||||
And I follow "Profile" in the user menu
|
||||
And I click on "Learning plans" "link"
|
||||
And I should see "Cookery"
|
||||
And I should see "Literacy"
|
||||
|
||||
# Learning plans details
|
||||
And I click on "Cookery" "link"
|
||||
And I should see "Cookery is important"
|
||||
And I should see "3 out of 3 competencies are proficient"
|
||||
|
||||
And "Good" "tool_lp > learning plan rating" should exist in the "Salads" "tool_lp > learning plan"
|
||||
And "Yes" "tool_lp > learning plan proficiency" should exist in the "Salads" "tool_lp > learning plan"
|
||||
|
||||
And "Great" "tool_lp > learning plan rating" should exist in the "Desserts" "tool_lp > learning plan"
|
||||
And "Yes" "tool_lp > learning plan proficiency" should exist in the "Desserts" "tool_lp > learning plan"
|
||||
|
||||
And "Great" "tool_lp > learning plan rating" should exist in the "Cakes" "tool_lp > learning plan"
|
||||
And "Yes" "tool_lp > learning plan proficiency" should exist in the "Cakes" "tool_lp > learning plan"
|
||||
|
||||
And I should not see "Literacy"
|
||||
And I should not see "Reading"
|
||||
And I should not see "Writing"
|
||||
|
||||
# Learning plans competency details
|
||||
And I click on "Desserts" "link" in the "Desserts" "tool_lp > learning plan"
|
||||
And I should see "Desserts are important"
|
||||
And "Yes" "tool_lp > competency page proficiency" should exist
|
||||
And "Great" "tool_lp > competency page rating" should exist
|
||||
|
||||
# Learning plans competency summary
|
||||
And I click on "Cakes cakes" "link"
|
||||
And I should see "Cakes are important"
|
||||
|
||||
Scenario: Teacher view
|
||||
# Participant competencies
|
||||
Given I am on the "C1" "report_competency > breakdown" page logged in as "teacher1"
|
||||
Then I should see "Student first"
|
||||
And "Good" "report_competency > breakdown rating" should exist in the "Salads" "report_competency > breakdown"
|
||||
And "Great" "report_competency > breakdown rating" should exist in the "Desserts" "report_competency > breakdown"
|
||||
And "Great" "report_competency > breakdown rating" should exist in the "Cakes" "report_competency > breakdown"
|
||||
And "Not rated" "report_competency > breakdown rating" should exist in the "Reading" "report_competency > breakdown"
|
||||
And "Not rated" "report_competency > breakdown rating" should exist in the "Writing" "report_competency > breakdown"
|
||||
|
||||
# Participant competencies details
|
||||
And I click on "Great" "report_competency > breakdown rating" in the "Desserts" "report_competency > breakdown"
|
||||
And "Yes" "tool_lp > competency page proficiency" should exist
|
||||
And "Great" "tool_lp > competency page rating" should exist
|
||||
|
||||
# Participant competencies summary
|
||||
And I click on "Cakes" "tool_lp > competency page related competency"
|
||||
And I should see "Cakes are important"
|
||||
|
||||
# Participant learning plans
|
||||
And I click on "Close" "button" in the "Cakes" "dialogue"
|
||||
And I click on "Close" "button" in the "User competency summary" "dialogue"
|
||||
And I navigate to course participants
|
||||
And I click on "Student first" "link"
|
||||
And I click on "Learning plans" "link"
|
||||
And I should see "Cookery"
|
||||
And I should see "Literacy"
|
||||
|
||||
# Participant learning plans details
|
||||
And I click on "Cookery" "link"
|
||||
And I should see "Cookery is important"
|
||||
And I should see "3 out of 3 competencies are proficient"
|
||||
|
||||
And "Good" "tool_lp > learning plan rating" should exist in the "Salads" "tool_lp > learning plan"
|
||||
And "Yes" "tool_lp > learning plan proficiency" should exist in the "Salads" "tool_lp > learning plan"
|
||||
|
||||
And "Great" "tool_lp > learning plan rating" should exist in the "Desserts" "tool_lp > learning plan"
|
||||
And "Yes" "tool_lp > learning plan proficiency" should exist in the "Desserts" "tool_lp > learning plan"
|
||||
|
||||
And "Great" "tool_lp > learning plan rating" should exist in the "Cakes" "tool_lp > learning plan"
|
||||
And "Yes" "tool_lp > learning plan proficiency" should exist in the "Cakes" "tool_lp > learning plan"
|
||||
|
||||
And I should not see "Literacy"
|
||||
And I should not see "Reading"
|
||||
And I should not see "Writing"
|
||||
|
||||
# Learning plans competency details
|
||||
And I click on "Desserts" "link"
|
||||
And I should see "Desserts are important"
|
||||
And "Yes" "tool_lp > competency page proficiency" should exist
|
||||
And "Great" "tool_lp > competency page rating" should exist
|
||||
|
||||
# Learning plans competency summary
|
||||
And I click on "Cakes" "tool_lp > competency page related competency"
|
||||
And I should see "Cakes are important"
|
||||
@@ -14,17 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Unit tests for the analysis class.
|
||||
*
|
||||
* @package core_analytics
|
||||
* @copyright 2019 David Monllaó {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once(__DIR__ . '/fixtures/test_timesplitting_upcoming_seconds.php');
|
||||
namespace core_analytics;
|
||||
|
||||
/**
|
||||
* Unit tests for the analysis class.
|
||||
@@ -33,18 +23,19 @@ require_once(__DIR__ . '/fixtures/test_timesplitting_upcoming_seconds.php');
|
||||
* @copyright 2019 David Monllaó {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class analytics_analysis_testcase extends advanced_testcase {
|
||||
class analysis_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test fill_firstanalyses_cache.
|
||||
* @return null
|
||||
*/
|
||||
public function test_fill_firstanalyses_cache() {
|
||||
require_once(__DIR__ . '/fixtures/test_timesplitting_upcoming_seconds.php');
|
||||
$this->resetAfterTest();
|
||||
|
||||
$modelid = 1;
|
||||
|
||||
$params = ['startdate' => (new DateTimeImmutable('-5 seconds'))->getTimestamp()];
|
||||
$params = ['startdate' => (new \DateTimeImmutable('-5 seconds'))->getTimestamp()];
|
||||
$course1 = $this->getDataGenerator()->create_course($params);
|
||||
$course2 = $this->getDataGenerator()->create_course($params);
|
||||
$analysable1 = new \core_analytics\course($course1);
|
||||
@@ -72,7 +63,7 @@ class analytics_analysis_testcase extends advanced_testcase {
|
||||
$this->assertEquals($earliest, $firstanalyses[$modelid . '_' . $course1->id]);
|
||||
|
||||
// Upcoming periodic time-splitting methods can read and process the cached data.
|
||||
$seconds = new test_timesplitting_upcoming_seconds();
|
||||
$seconds = new \test_timesplitting_upcoming_seconds();
|
||||
$seconds->set_modelid($modelid);
|
||||
$seconds->set_analysable($analysable1);
|
||||
|
||||
|
||||
@@ -14,18 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Unit tests for the calculation info cache.
|
||||
*
|
||||
* @package core_analytics
|
||||
* @copyright 2019 David Monllaó {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once(__DIR__ . '/fixtures/test_indicator_max.php');
|
||||
require_once(__DIR__ . '/fixtures/test_indicator_min.php');
|
||||
namespace core_analytics;
|
||||
|
||||
/**
|
||||
* Unit tests for the calculation info cache.
|
||||
@@ -34,7 +23,7 @@ require_once(__DIR__ . '/fixtures/test_indicator_min.php');
|
||||
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class analytics_calculation_info_testcase extends advanced_testcase {
|
||||
class calculation_info_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* test_calculation_info description
|
||||
@@ -47,12 +36,14 @@ class analytics_calculation_info_testcase extends advanced_testcase {
|
||||
* @return null
|
||||
*/
|
||||
public function test_calculation_info_add_pull($info1, $info2, $info3, $info4) {
|
||||
require_once(__DIR__ . '/fixtures/test_indicator_max.php');
|
||||
require_once(__DIR__ . '/fixtures/test_indicator_min.php');
|
||||
$this->resetAfterTest();
|
||||
|
||||
$atimesplitting = new \core\analytics\time_splitting\quarters();
|
||||
|
||||
$indicator1 = new test_indicator_min();
|
||||
$indicator2 = new test_indicator_max();
|
||||
$indicator1 = new \test_indicator_min();
|
||||
$indicator2 = new \test_indicator_max();
|
||||
|
||||
$calculationinfo = new \core_analytics\calculation_info();
|
||||
$calculationinfo->add_shared(111, [111 => $info1]);
|
||||
|
||||
@@ -14,15 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Unit tests for the dataset manager.
|
||||
*
|
||||
* @package core_analytics
|
||||
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
namespace core_analytics;
|
||||
|
||||
/**
|
||||
* Unit tests for the dataset manager.
|
||||
@@ -31,7 +23,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class dataset_manager_testcase extends advanced_testcase {
|
||||
class dataset_manager_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* setUp
|
||||
|
||||
@@ -14,13 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Unit tests for the indicator API.
|
||||
*
|
||||
* @package core_analytics
|
||||
* @copyright 2019 David Monllaó {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core_analytics;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
@@ -35,7 +29,7 @@ require_once(__DIR__ . '/fixtures/test_indicator_min.php');
|
||||
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class analytics_indicator_testcase extends advanced_testcase {
|
||||
class indicator_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* test_validate_calculated_value
|
||||
@@ -79,7 +73,7 @@ class analytics_indicator_testcase extends advanced_testcase {
|
||||
->onlyMethods(['calculate_sample'])
|
||||
->getMock();
|
||||
$indicatormock->method('calculate_sample')->willReturn($willreturn);
|
||||
$this->expectException(coding_exception::class);
|
||||
$this->expectException(\coding_exception::class);
|
||||
list($values, $unused) = $indicatormock->calculate([1], 'notrelevanthere');
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_analytics;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once(__DIR__ . '/fixtures/test_indicator_max.php');
|
||||
@@ -39,7 +41,7 @@ require_once(__DIR__ . '/fixtures/test_analysis.php');
|
||||
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class analytics_model_testcase extends advanced_testcase {
|
||||
class model_test extends \advanced_testcase {
|
||||
|
||||
public function setUp(): void {
|
||||
|
||||
@@ -183,7 +185,7 @@ class analytics_model_testcase extends advanced_testcase {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$statictarget = new test_static_target_shortname();
|
||||
$statictarget = new \test_static_target_shortname();
|
||||
$indicators['test_indicator_max'] = \core_analytics\manager::get_indicator('test_indicator_max');
|
||||
$model = \core_analytics\model::create($statictarget, $indicators, '\core\analytics\time_splitting\quarters');
|
||||
$modelobj = $model->get_model_obj();
|
||||
@@ -301,11 +303,11 @@ class analytics_model_testcase extends advanced_testcase {
|
||||
$courses[$analysable->get_id()] = $course;
|
||||
}
|
||||
|
||||
$target = new test_target_course_level_shortname();
|
||||
$target = new \test_target_course_level_shortname();
|
||||
$analyser = new \core\analytics\analyser\courses(1, $target, [], [], []);
|
||||
|
||||
$result = new \core_analytics\local\analysis\result_array(1, false, []);
|
||||
$analysis = new test_analysis($analyser, false, $result);
|
||||
$analysis = new \test_analysis($analyser, false, $result);
|
||||
|
||||
// Each analysable element takes 0.5 secs minimum (test_analysis), so the max (and likely) number of analysable
|
||||
// elements that will be processed is 2.
|
||||
@@ -342,7 +344,7 @@ class analytics_model_testcase extends advanced_testcase {
|
||||
|
||||
// Training and prediction data do not get mixed.
|
||||
$result = new \core_analytics\local\analysis\result_array(1, false, []);
|
||||
$analysis = new test_analysis($analyser, false, $result);
|
||||
$analysis = new \test_analysis($analyser, false, $result);
|
||||
$analysis->run();
|
||||
$params = array('modelid' => 1, 'action' => 'training');
|
||||
$this->assertLessThanOrEqual(2, $DB->count_records('analytics_used_analysables', $params));
|
||||
@@ -402,7 +404,7 @@ class analytics_model_testcase extends advanced_testcase {
|
||||
$this->model->update(true, [], false);
|
||||
$this->assertFalse($this->model->can_export_configuration());
|
||||
|
||||
$statictarget = new test_static_target_shortname();
|
||||
$statictarget = new \test_static_target_shortname();
|
||||
$indicators['test_indicator_max'] = \core_analytics\manager::get_indicator('test_indicator_max');
|
||||
$model = \core_analytics\model::create($statictarget, $indicators, '\\core\\analytics\\time_splitting\\quarters');
|
||||
$this->assertFalse($model->can_export_configuration());
|
||||
@@ -418,7 +420,7 @@ class analytics_model_testcase extends advanced_testcase {
|
||||
|
||||
$modelconfig = new \core_analytics\model_config($this->model);
|
||||
|
||||
$method = new ReflectionMethod('\\core_analytics\\model_config', 'export_model_data');
|
||||
$method = new \ReflectionMethod('\\core_analytics\\model_config', 'export_model_data');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$modeldata = $method->invoke($modelconfig);
|
||||
@@ -566,7 +568,7 @@ class analytics_model_testcase extends advanced_testcase {
|
||||
private function add_fake_log() {
|
||||
global $DB, $USER;
|
||||
|
||||
$log = new stdClass();
|
||||
$log = new \stdClass();
|
||||
$log->modelid = $this->modelobj->id;
|
||||
$log->version = $this->modelobj->version;
|
||||
$log->target = $this->modelobj->target;
|
||||
|
||||
@@ -14,13 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Unit tests for prediction actions.
|
||||
*
|
||||
* @package core_analytics
|
||||
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core_analytics;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
@@ -34,7 +28,7 @@ require_once(__DIR__ . '/fixtures/test_target_shortname.php');
|
||||
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class analytics_prediction_actions_testcase extends advanced_testcase {
|
||||
class prediction_actions_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Common startup tasks
|
||||
|
||||
@@ -14,21 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Unit tests for evaluation, training and prediction.
|
||||
*
|
||||
* NOTE: in order to execute this test using a separate server for the
|
||||
* python ML backend you need to define these variables in your config.php file:
|
||||
*
|
||||
* define('TEST_MLBACKEND_PYTHON_HOST', '127.0.0.1');
|
||||
* define('TEST_MLBACKEND_PYTHON_PORT', 5000);
|
||||
* define('TEST_MLBACKEND_PYTHON_USERNAME', 'default');
|
||||
* define('TEST_MLBACKEND_PYTHON_PASSWORD', 'sshhhh');
|
||||
*
|
||||
* @package core_analytics
|
||||
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core_analytics;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
@@ -48,11 +34,19 @@ require_once(__DIR__ . '/../../course/lib.php');
|
||||
/**
|
||||
* Unit tests for evaluation, training and prediction.
|
||||
*
|
||||
* NOTE: in order to execute this test using a separate server for the
|
||||
* python ML backend you need to define these variables in your config.php file:
|
||||
*
|
||||
* define('TEST_MLBACKEND_PYTHON_HOST', '127.0.0.1');
|
||||
* define('TEST_MLBACKEND_PYTHON_PORT', 5000);
|
||||
* define('TEST_MLBACKEND_PYTHON_USERNAME', 'default');
|
||||
* define('TEST_MLBACKEND_PYTHON_PASSWORD', 'sshhhh');
|
||||
*
|
||||
* @package core_analytics
|
||||
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_analytics_prediction_testcase extends advanced_testcase {
|
||||
class prediction_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Purge all the mlbackend outputs.
|
||||
@@ -746,7 +740,7 @@ class core_analytics_prediction_testcase extends advanced_testcase {
|
||||
$samples,
|
||||
$ranges
|
||||
);
|
||||
$dataset = phpunit_util::call_internal_method($analysis, 'calculate_indicators', $params,
|
||||
$dataset = \phpunit_util::call_internal_method($analysis, 'calculate_indicators', $params,
|
||||
'\core_analytics\analysis');
|
||||
$this->assertArrayHasKey('123-0', $dataset);
|
||||
$this->assertArrayHasKey('123-1', $dataset);
|
||||
@@ -770,7 +764,7 @@ class core_analytics_prediction_testcase extends advanced_testcase {
|
||||
$samples,
|
||||
$ranges
|
||||
);
|
||||
$dataset = phpunit_util::call_internal_method($analysis, 'calculate_indicators', $params,
|
||||
$dataset = \phpunit_util::call_internal_method($analysis, 'calculate_indicators', $params,
|
||||
'\core_analytics\analysis');
|
||||
$this->assertArrayNotHasKey('123-0', $dataset);
|
||||
$this->assertArrayNotHasKey('123-1', $dataset);
|
||||
|
||||
@@ -14,14 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Provides the {@link analytics_stats_testcase} class.
|
||||
*
|
||||
* @package core_analytics
|
||||
* @category test
|
||||
* @copyright 2019 David Mudrák <david@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core_analytics;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
@@ -31,10 +24,12 @@ require_once(__DIR__ . '/fixtures/test_target_shortname.php');
|
||||
/**
|
||||
* Unit tests for the analytics stats.
|
||||
*
|
||||
* @package core_analytics
|
||||
* @category test
|
||||
* @copyright 2019 David Mudrák <david@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class analytics_stats_testcase extends advanced_testcase {
|
||||
class stats_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Set up the test environment.
|
||||
|
||||
@@ -14,30 +14,26 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Unit tests for core time splitting methods.
|
||||
*
|
||||
* @package core
|
||||
* @category analytics
|
||||
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core_analytics;
|
||||
|
||||
use test_timesplitting_seconds;
|
||||
use test_timesplitting_upcoming_seconds;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once(__DIR__ . '/../../analytics/tests/fixtures/test_timesplitting_seconds.php');
|
||||
require_once(__DIR__ . '/../../analytics/tests/fixtures/test_timesplitting_upcoming_seconds.php');
|
||||
require_once(__DIR__ . '/fixtures/test_timesplitting_seconds.php');
|
||||
require_once(__DIR__ . '/fixtures/test_timesplitting_upcoming_seconds.php');
|
||||
require_once(__DIR__ . '/../../lib/enrollib.php');
|
||||
|
||||
/**
|
||||
* Unit tests for core time splitting methods.
|
||||
*
|
||||
* @package core
|
||||
* @category analytics
|
||||
* @category test
|
||||
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_analytics_time_splittings_testcase extends advanced_testcase {
|
||||
class time_splittings_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* setUp
|
||||
@@ -214,7 +210,7 @@ class core_analytics_time_splittings_testcase extends advanced_testcase {
|
||||
|
||||
// We now use an ongoing course not yet ready to generate predictions.
|
||||
|
||||
$threedaysago = new DateTime('-3 days');
|
||||
$threedaysago = new \DateTime('-3 days');
|
||||
$params = array(
|
||||
'startdate' => $threedaysago->getTimestamp(),
|
||||
);
|
||||
@@ -229,7 +225,7 @@ class core_analytics_time_splittings_testcase extends advanced_testcase {
|
||||
|
||||
// We now use a ready-to-predict ongoing course.
|
||||
|
||||
$onemonthago = new DateTime('-30 days');
|
||||
$onemonthago = new \DateTime('-30 days');
|
||||
$params = array(
|
||||
'startdate' => $onemonthago->getTimestamp(),
|
||||
);
|
||||
@@ -284,10 +280,10 @@ class core_analytics_time_splittings_testcase extends advanced_testcase {
|
||||
|
||||
// We now check how new ranges get added as time passes.
|
||||
|
||||
$fewsecsago = new DateTime('-5 seconds');
|
||||
$fewsecsago = new \DateTime('-5 seconds');
|
||||
$params = array(
|
||||
'startdate' => $fewsecsago->getTimestamp(),
|
||||
'enddate' => (new DateTimeImmutable('+1 year'))->getTimestamp(),
|
||||
'enddate' => (new \DateTimeImmutable('+1 year'))->getTimestamp(),
|
||||
);
|
||||
$course = $this->getDataGenerator()->create_course($params);
|
||||
$analysable = new \core_analytics\course($course);
|
||||
+4
-9
@@ -866,11 +866,8 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
if (!empty($users)) {
|
||||
print_string('userentriestoupdate', 'auth_ldap', count($users));
|
||||
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
$xcount = 0;
|
||||
$maxxcount = 100;
|
||||
|
||||
foreach ($users as $user) {
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
echo "\t"; print_string('auth_dbupdatinguser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id));
|
||||
$userinfo = $this->get_userinfo($user->username);
|
||||
if (!$this->update_user_record($user->username, $updatekeys, true,
|
||||
@@ -878,12 +875,11 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
echo ' - '.get_string('skipped');
|
||||
}
|
||||
echo "\n";
|
||||
$xcount++;
|
||||
|
||||
// Update system roles, if needed.
|
||||
$this->sync_roles($user);
|
||||
$transaction->allow_commit();
|
||||
}
|
||||
$transaction->allow_commit();
|
||||
unset($users); // free mem
|
||||
}
|
||||
} else { // end do updates
|
||||
@@ -903,8 +899,8 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
if (!empty($add_users)) {
|
||||
print_string('userentriestoadd', 'auth_ldap', count($add_users));
|
||||
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
foreach ($add_users as $user) {
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
$user = $this->get_userinfo_asobj($user->username);
|
||||
|
||||
// Prep a few params
|
||||
@@ -939,9 +935,8 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
|
||||
// Add roles if needed.
|
||||
$this->sync_roles($euser);
|
||||
|
||||
$transaction->allow_commit();
|
||||
}
|
||||
$transaction->allow_commit();
|
||||
unset($add_users); // free mem
|
||||
} else {
|
||||
print_string('nouserstobeadded', 'auth_ldap');
|
||||
|
||||
@@ -482,13 +482,13 @@ class tree extends tree_node {
|
||||
*
|
||||
* @param bool $not True if there is a NOT in effect
|
||||
* @param info $info Information about location of condition tree
|
||||
* @param result $result Result object if this is a student display, else null
|
||||
* @param result|null $result Result object if this is a student display, else null
|
||||
* @param bool $root True if this is the root item
|
||||
* @param bool $hidden Staff display; true if this tree has show=false (from parent)
|
||||
* @return string|renderable Information to render
|
||||
*/
|
||||
protected function get_full_information_recursive(
|
||||
$not, info $info, result $result = null, $root, $hidden = false) {
|
||||
$not, info $info, ?result $result, $root, $hidden = false) {
|
||||
// Get list of children - either full list, or those which are shown.
|
||||
$children = $this->children;
|
||||
$staff = true;
|
||||
|
||||
@@ -12,9 +12,10 @@ Feature: Restore Moodle 2 course backups
|
||||
| Course 3 | C3 | 0 | topics | 2 | 0 |
|
||||
| Course 4 | C4 | 0 | topics | 20 | 0 |
|
||||
And the following "activities" exist:
|
||||
| activity | course | idnumber | name | intro | section |
|
||||
| assign | C3 | assign1 | Test assign name | Assign description | 1 |
|
||||
| data | C3 | data1 | Test database name | Database description | 2 |
|
||||
| activity | course | idnumber | name | intro | section | externalurl |
|
||||
| assign | C3 | assign1 | Test assign name | Assign description | 1 | |
|
||||
| data | C3 | data1 | Test database name | Database description | 2 | |
|
||||
| url | C1 | url1 | Test URL name | Test URL description | 3 | http://www.moodle.org |
|
||||
And I log in as "admin"
|
||||
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:
|
||||
@@ -108,11 +109,7 @@ Feature: Restore Moodle 2 course backups
|
||||
|
||||
@javascript
|
||||
Scenario: Restore a backup in an existing course retaining the backup course settings
|
||||
Given I add a "URL" to section "3" and I fill the form with:
|
||||
| Name | Test URL name |
|
||||
| Description | Test URL description |
|
||||
| id_externalurl | http://www.moodle.org |
|
||||
And I hide section "3"
|
||||
Given I hide section "3"
|
||||
And I hide section "7"
|
||||
When I backup "Course 1" course using this options:
|
||||
| Confirmation | Filename | test_backup.mbz |
|
||||
@@ -134,11 +131,7 @@ Feature: Restore Moodle 2 course backups
|
||||
|
||||
@javascript
|
||||
Scenario: Restore a backup in an existing course keeping the target course settings
|
||||
Given I add a "URL" to section "3" and I fill the form with:
|
||||
| Name | Test URL name |
|
||||
| Description | Test URL description |
|
||||
| id_externalurl | http://www.moodle.org |
|
||||
And I hide section "3"
|
||||
Given I hide section "3"
|
||||
And I hide section "7"
|
||||
When I backup "Course 1" course using this options:
|
||||
| Confirmation | Filename | test_backup.mbz |
|
||||
@@ -160,11 +153,7 @@ Feature: Restore Moodle 2 course backups
|
||||
|
||||
@javascript
|
||||
Scenario: Restore a backup in an existing course deleting contents and retaining the backup course settings
|
||||
Given I add a "URL" to section "3" and I fill the form with:
|
||||
| Name | Test URL name |
|
||||
| Description | Test URL description |
|
||||
| id_externalurl | http://www.moodle.org |
|
||||
And I hide section "3"
|
||||
Given I hide section "3"
|
||||
And I hide section "7"
|
||||
When I backup "Course 1" course using this options:
|
||||
| Initial | Include enrolled users | 0 |
|
||||
@@ -189,11 +178,7 @@ Feature: Restore Moodle 2 course backups
|
||||
|
||||
@javascript
|
||||
Scenario: Restore a backup in an existing course deleting contents and keeping the current course settings
|
||||
Given I add a "URL" to section "3" and I fill the form with:
|
||||
| Name | Test URL name |
|
||||
| Description | Test URL description |
|
||||
| id_externalurl | http://www.moodle.org |
|
||||
And I hide section "3"
|
||||
Given I hide section "3"
|
||||
And I hide section "7"
|
||||
When I backup "Course 1" course using this options:
|
||||
| Initial | Include enrolled users | 0 |
|
||||
@@ -218,11 +203,7 @@ Feature: Restore Moodle 2 course backups
|
||||
|
||||
@javascript
|
||||
Scenario: Restore a backup in an existing course deleting contents decreasing the number of sections
|
||||
Given I add a "URL" to section "3" and I fill the form with:
|
||||
| Name | Test URL name |
|
||||
| Description | Test URL description |
|
||||
| id_externalurl | http://www.moodle.org |
|
||||
And I hide section "3"
|
||||
Given I hide section "3"
|
||||
And I hide section "7"
|
||||
When I backup "Course 1" course using this options:
|
||||
| Initial | Include enrolled users | 0 |
|
||||
|
||||
@@ -21,7 +21,7 @@ Feature: Restore Moodle 2 course backups with different user data settings
|
||||
| data | Test database name | n | C1 | data1 |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I add a "Text input" field to "Test database name" database and I fill the form with:
|
||||
And I add a "Short text" field to "Test database name" database and I fill the form with:
|
||||
| Field name | Test field name |
|
||||
| Field description | Test field description |
|
||||
And I follow "Templates"
|
||||
|
||||
@@ -207,7 +207,7 @@ class issued_badge implements renderable {
|
||||
$data->hasrelatedbadges = true;
|
||||
$data->relatedbadges = [];
|
||||
foreach ($relatedbadges as $related) {
|
||||
if (isloggedin() && !is_guest($context)) {
|
||||
if (isloggedin() && ($context instanceof context_course && !is_guest($context))) {
|
||||
$related->url = (new moodle_url('/badges/overview.php', ['id' => $related->id]))->out(false);
|
||||
}
|
||||
$data->relatedbadges[] = (array)$related;
|
||||
|
||||
@@ -30,9 +30,7 @@ Feature: Award badges with separate groups
|
||||
| teacher1 | CB |
|
||||
| student2 | CA |
|
||||
| teacher2 | CA |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Course 1" "course editing" page logged in as "teacher1"
|
||||
And I expand all fieldsets
|
||||
And I set the field "Group mode" to "Separate groups"
|
||||
And I press "Save and display"
|
||||
|
||||
@@ -36,7 +36,7 @@ class moodle1_block_html_handler extends moodle1_block_handler {
|
||||
$contextid = $this->converter->get_contextid(CONTEXT_BLOCK, $olddata['id']);
|
||||
$decodeddata = base64_decode($olddata['configdata']);
|
||||
list($updated, $configdata) = upgrade_fix_serialized_objects($decodeddata);
|
||||
$configdata = unserialize($configdata);
|
||||
$configdata = unserialize_object($configdata);
|
||||
|
||||
// get a fresh new file manager for this instance
|
||||
$this->fileman = $this->converter->get_file_manager($contextid, 'block_html');
|
||||
@@ -44,7 +44,7 @@ class moodle1_block_html_handler extends moodle1_block_handler {
|
||||
// convert course files embedded in the block content
|
||||
$this->fileman->filearea = 'content';
|
||||
$this->fileman->itemid = 0;
|
||||
$configdata->text = moodle1_converter::migrate_referenced_files($configdata->text, $this->fileman);
|
||||
$configdata->text = moodle1_converter::migrate_referenced_files($configdata->text ?? '', $this->fileman);
|
||||
$configdata->format = FORMAT_HTML;
|
||||
|
||||
return base64_encode(serialize($configdata));
|
||||
|
||||
@@ -119,10 +119,19 @@ class block_news_items extends block_base {
|
||||
if (!empty($CFG->forum_enabletimedposts) && ($discussion->timestart > $posttime)) {
|
||||
$posttime = $discussion->timestart;
|
||||
}
|
||||
|
||||
// If the user who created the discussion post has been deleted, indicate so.
|
||||
if ($discussion->userdeleted) {
|
||||
$userfullname = get_string('deleteduser', 'mod_forum');
|
||||
} else {
|
||||
$userfullname = fullname($discussion, has_capability('moodle/site:viewfullnames', $context));
|
||||
}
|
||||
|
||||
$text .= '<li class="post">'.
|
||||
'<div class="head clearfix">'.
|
||||
'<div class="date">'.userdate($posttime, $strftimerecent).'</div>'.
|
||||
'<div class="name">'.fullname($discussion).'</div></div>'.
|
||||
'<div class="name">'.$userfullname.'</div>'.
|
||||
'</div>'.
|
||||
'<div class="info"><a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->discussion.'">'.$discussion->subject.'</a></div>'.
|
||||
"</li>\n";
|
||||
}
|
||||
|
||||
@@ -10,11 +10,18 @@ Feature: Add URL to main menu block
|
||||
And I am on site homepage
|
||||
And I turn editing mode on
|
||||
And I add the "Main menu" block
|
||||
When I add a "URL" to section "0" and I fill the form with:
|
||||
| Name | reference link |
|
||||
| Description | mooooooooodle |
|
||||
| External URL | http://www.moodle.com |
|
||||
| id_display | In pop-up |
|
||||
And the following "activity" exists:
|
||||
| activity | url |
|
||||
| course | Acceptance test site |
|
||||
| name | reference link |
|
||||
| intro | mooooooooodle |
|
||||
| externalurl | http://www.moodle.com |
|
||||
| section | 0 |
|
||||
When I am on the "reference link" "url activity editing" page
|
||||
And I expand all fieldsets
|
||||
And I set the following fields to these values:
|
||||
| id_display | In pop-up |
|
||||
And I press "Save and return to course"
|
||||
Then "reference link" "link" should exist in the "Main menu" "block"
|
||||
And "Add an activity or resource" "button" should exist in the "Main menu" "block"
|
||||
|
||||
@@ -33,12 +40,18 @@ Feature: Add URL to main menu block
|
||||
And I set the following fields to these values:
|
||||
| Page contexts | Display throughout the entire site |
|
||||
And I press "Save changes"
|
||||
When I am on the "C1" Course page
|
||||
And I add a "URL" to section "0" and I fill the form with:
|
||||
| Name | reference link |
|
||||
| Description | mooooooooodle |
|
||||
| External URL | http://www.moodle.com |
|
||||
| Display description on course page | 1 |
|
||||
| id_display | In pop-up |
|
||||
When the following "activity" exists:
|
||||
| activity | url |
|
||||
| course | C1 |
|
||||
| name | reference link |
|
||||
| intro | mooooooooodle |
|
||||
| externalurl | http://www.moodle.com |
|
||||
| section | 0 |
|
||||
| showdescription | 1 |
|
||||
And I am on the "reference link" "url activity editing" page
|
||||
And I expand all fieldsets
|
||||
And I set the following fields to these values:
|
||||
| id_display | In pop-up |
|
||||
And I press "Save and return to course"
|
||||
Then "reference link" "link" should not exist in the "Main menu" "block"
|
||||
And I should see "mooooooooodle" in the "region-main" "region"
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ class cachestore_file_addinstance_form extends cachestore_addinstance_form {
|
||||
$form = $this->_form;
|
||||
|
||||
$form->addElement('text', 'path', get_string('path', 'cachestore_file'));
|
||||
$form->setType('path', PARAM_SAFEPATH);
|
||||
$form->setType('path', PARAM_RAW);
|
||||
$form->addHelpButton('path', 'path', 'cachestore_file');
|
||||
|
||||
$form->addElement('checkbox', 'autocreate', get_string('autocreate', 'cachestore_file'));
|
||||
|
||||
Vendored
+1
-1
@@ -5,6 +5,6 @@
|
||||
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define("core_calendar/crud",["jquery","core/str","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","core/modal_factory","core/modal_events","core_calendar/modal_event_form","core_calendar/repository","core_calendar/events","core_calendar/modal_delete","core_calendar/selectors","core/pending"],(function($,Str,Notification,CustomEvents,Modal,ModalRegistry,ModalFactory,ModalEvents,ModalEventForm,CalendarRepository,CalendarEvents,ModalDelete,CalendarSelectors,Pending){return{registerRemove:function(root){root.on("click",CalendarSelectors.actions.remove,(function(e){var eventSource=$(this).closest(CalendarSelectors.eventItem);!function(eventId,eventTitle,eventCount){var deletePromise,pendingPromise=new Pending("core_calendar/crud:confirmDeletion"),deleteStrings=[{key:"deleteevent",component:"calendar"}],isRepeatedEvent=(eventCount=parseInt(eventCount,10))>1;isRepeatedEvent?(deleteStrings.push({key:"confirmeventseriesdelete",component:"calendar",param:{name:eventTitle,count:eventCount}}),deletePromise=ModalFactory.create({type:ModalDelete.TYPE})):(deleteStrings.push({key:"confirmeventdelete",component:"calendar",param:eventTitle}),deletePromise=ModalFactory.create({type:ModalFactory.types.SAVE_CANCEL}));var stringsPromise=Str.get_strings(deleteStrings);$.when(stringsPromise,deletePromise).then((function(strings,deleteModal){return deleteModal.setRemoveOnClose(!0),deleteModal.setTitle(strings[0]),deleteModal.setBody(strings[1]),isRepeatedEvent||deleteModal.setSaveButtonText(strings[0]),deleteModal.show(),deleteModal.getRoot().on(ModalEvents.save,(function(){var pendingPromise=new Pending("calendar/crud:initModal:deletedevent");CalendarRepository.deleteEvent(eventId,!1).then((function(){$("body").trigger(CalendarEvents.deleted,[eventId,!1])})).then(pendingPromise.resolve).catch(Notification.exception)})),deleteModal.getRoot().on(CalendarEvents.deleteAll,(function(){var pendingPromise=new Pending("calendar/crud:initModal:deletedallevent");CalendarRepository.deleteEvent(eventId,!0).then((function(){$("body").trigger(CalendarEvents.deleted,[eventId,!0])})).then(pendingPromise.resolve).catch(Notification.exception)})),deleteModal})).then((function(modal){return pendingPromise.resolve(),modal})).catch(Notification.exception)}(eventSource.data("eventId"),eventSource.data("eventTitle"),eventSource.data("eventCount")),e.preventDefault()}))},registerEditListeners:function(root,eventFormModalPromise){var pendingPromise=new Pending("core_calendar/crud:registerEditListeners");return eventFormModalPromise.then((function(modal){return $("body").on(CalendarEvents.editEvent,(function(e,eventId){var calendarWrapper=root.find(CalendarSelectors.wrapper);modal.setEventId(eventId),modal.setContextId(calendarWrapper.data("contextId")),modal.show(),e.stopImmediatePropagation()})),modal})).then((function(modal){return pendingPromise.resolve(),modal})).catch(Notification.exception)},registerEventFormModal:function(root){var eventFormPromise=ModalFactory.create({type:ModalEventForm.TYPE,large:!0});return root.on("click",CalendarSelectors.actions.create,(function(e){eventFormPromise.then((function(modal){var wrapper=root.find(CalendarSelectors.wrapper),categoryId=wrapper.data("categoryid");void 0!==categoryId&&modal.setCategoryId(categoryId);var today=root.find(CalendarSelectors.today),firstDay=root.find(CalendarSelectors.day);!today.length&&firstDay.length&&modal.setStartTime(firstDay.data("newEventTimestamp")),modal.setContextId(wrapper.data("contextId")),modal.setCourseId(wrapper.data("courseid")),modal.show()})).fail(Notification.exception),e.preventDefault()})),root.on("click",CalendarSelectors.actions.edit,(function(e){e.preventDefault();var target=$(e.currentTarget),calendarWrapper=target.closest(CalendarSelectors.wrapper),eventWrapper=target.closest(CalendarSelectors.eventItem);eventFormPromise.then((function(modal){modal.setEventId(eventWrapper.data("eventId")),modal.setContextId(calendarWrapper.data("contextId")),modal.show(),e.stopImmediatePropagation()})).fail(Notification.exception)})),eventFormPromise}}}));
|
||||
define("core_calendar/crud",["jquery","core/str","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","core/modal_factory","core/modal_events","core_calendar/modal_event_form","core_calendar/repository","core_calendar/events","core_calendar/modal_delete","core_calendar/selectors","core/pending"],(function($,Str,Notification,CustomEvents,Modal,ModalRegistry,ModalFactory,ModalEvents,ModalEventForm,CalendarRepository,CalendarEvents,ModalDelete,CalendarSelectors,Pending){return{registerRemove:function(root){root.on("click",CalendarSelectors.actions.remove,(function(e){var eventSource=$(this).closest(CalendarSelectors.eventItem);!function(eventId,eventTitle,eventCount){var deletePromise,pendingPromise=new Pending("core_calendar/crud:confirmDeletion"),deleteStrings=[{key:"deleteevent",component:"calendar"}],isRepeatedEvent=(eventCount=parseInt(eventCount,10))>1;isRepeatedEvent?(deleteStrings.push({key:"confirmeventseriesdelete",component:"calendar",param:{name:eventTitle,count:eventCount}}),deletePromise=ModalFactory.create({type:ModalDelete.TYPE})):(deleteStrings.push({key:"confirmeventdelete",component:"calendar",param:eventTitle}),deletePromise=ModalFactory.create({type:ModalFactory.types.SAVE_CANCEL}));var stringsPromise=Str.get_strings(deleteStrings);$.when(stringsPromise,deletePromise).then((function(strings,deleteModal){return deleteModal.setRemoveOnClose(!0),deleteModal.setTitle(strings[0]),deleteModal.setBody(strings[1]),isRepeatedEvent||deleteModal.setSaveButtonText(strings[0]),deleteModal.show(),deleteModal.getRoot().on(ModalEvents.save,(function(){var pendingPromise=new Pending("calendar/crud:initModal:deletedevent");CalendarRepository.deleteEvent(eventId,!1).then((function(){$("body").trigger(CalendarEvents.deleted,[eventId,!1])})).then(pendingPromise.resolve).catch(Notification.exception)})),deleteModal.getRoot().on(CalendarEvents.deleteAll,(function(){var pendingPromise=new Pending("calendar/crud:initModal:deletedallevent");CalendarRepository.deleteEvent(eventId,!0).then((function(){$("body").trigger(CalendarEvents.deleted,[eventId,!0])})).then(pendingPromise.resolve).catch(Notification.exception)})),deleteModal})).then((function(modal){return pendingPromise.resolve(),modal})).catch(Notification.exception)}(eventSource.data("eventId"),eventSource.data("eventTitle"),eventSource.data("eventCount")),e.preventDefault()}))},registerEditListeners:function(root,eventFormModalPromise){var pendingPromise=new Pending("core_calendar/crud:registerEditListeners");return eventFormModalPromise.then((function(modal){return $("body").on(CalendarEvents.editEvent,(function(e,eventId){var calendarWrapper=root.find(CalendarSelectors.wrapper);modal.setEventId(eventId),modal.setContextId(calendarWrapper.data("contextId")),modal.show(),e.stopImmediatePropagation()})),modal})).then((function(modal){return pendingPromise.resolve(),modal})).catch(Notification.exception)},registerEventFormModal:function(root){var eventFormPromise=ModalFactory.create({type:ModalEventForm.TYPE,large:!0});return root.on("click",CalendarSelectors.actions.create,(function(e){eventFormPromise.then((function(modal){var wrapper=root.find(CalendarSelectors.wrapper),categoryId=wrapper.data("categoryid");void 0!==categoryId&&modal.setCategoryId(categoryId);var today=root.find(CalendarSelectors.today),firstDay=root.find(CalendarSelectors.day);!today.length&&firstDay.length&&modal.setStartTime(firstDay.data("newEventTimestamp")),modal.setContextId(wrapper.data("contextId")),modal.setCourseId(wrapper.data("courseid")),modal.show()})).fail(Notification.exception),e.preventDefault()})),root.on("click",CalendarSelectors.actions.edit,(function(e){e.preventDefault();var target=$(e.currentTarget),calendarWrapper=target.closest(CalendarSelectors.wrapper),eventWrapper=target.closest(CalendarSelectors.eventItem);eventFormPromise.then((function(modal){modal.setEventId(eventWrapper.data("eventId")),modal.setContextId(calendarWrapper.data("contextId")),modal.setCourseId(eventWrapper.data("courseId")),modal.show(),e.stopImmediatePropagation()})).fail(Notification.exception)})),eventFormPromise}}}));
|
||||
|
||||
//# sourceMappingURL=crud.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -202,6 +202,7 @@ function(
|
||||
modal.setEventId(eventWrapper.data('eventId'));
|
||||
|
||||
modal.setContextId(calendarWrapper.data('contextId'));
|
||||
modal.setCourseId(eventWrapper.data('courseId'));
|
||||
modal.show();
|
||||
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
@@ -180,8 +180,13 @@ class container {
|
||||
$course = new \stdClass();
|
||||
$course->id = $dbrow->courseid;
|
||||
$completion = new \completion_info($course);
|
||||
|
||||
return (bool) !$completion->is_enabled($cm);
|
||||
if ($completion->is_enabled($cm)) {
|
||||
// Check if the event is completed, then in this case we do not need to complete it.
|
||||
// Make sure we're using a cm_info object.
|
||||
$completiondata = $completion->get_data($cm);
|
||||
return intval($completiondata->completionstate) === COMPLETION_COMPLETE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -118,16 +118,16 @@ class event implements event_interface {
|
||||
* @param int $id The event's ID in the database.
|
||||
* @param string $name The event's name.
|
||||
* @param description_interface $description The event's description.
|
||||
* @param proxy_interface $category The category associated with the event.
|
||||
* @param proxy_interface $course The course associated with the event.
|
||||
* @param proxy_interface $group The group associated with the event.
|
||||
* @param proxy_interface $user The user associated with the event.
|
||||
* @param event_collection_interface $repeats Collection of repeat events.
|
||||
* @param proxy_interface $coursemodule The course module that created the event.
|
||||
* @param proxy_interface|null $category The category associated with the event.
|
||||
* @param proxy_interface|null $course The course associated with the event.
|
||||
* @param proxy_interface|null $group The group associated with the event.
|
||||
* @param proxy_interface|null $user The user associated with the event.
|
||||
* @param event_collection_interface|null $repeats Collection of repeat events.
|
||||
* @param proxy_interface|null $coursemodule The course module that created the event.
|
||||
* @param string $type The event's type.
|
||||
* @param times_interface $times The times associated with the event.
|
||||
* @param bool $visible The event's visibility. True for visible, false for invisible.
|
||||
* @param proxy_interface $subscription The event's subscription.
|
||||
* @param proxy_interface|null $subscription The event's subscription.
|
||||
* @param string $location The event's location.
|
||||
* @param string $component The event's component.
|
||||
*/
|
||||
@@ -135,12 +135,12 @@ class event implements event_interface {
|
||||
$id,
|
||||
$name,
|
||||
description_interface $description,
|
||||
proxy_interface $category = null,
|
||||
proxy_interface $course = null,
|
||||
proxy_interface $group = null,
|
||||
proxy_interface $user = null,
|
||||
event_collection_interface $repeats = null,
|
||||
proxy_interface $coursemodule = null,
|
||||
?proxy_interface $category,
|
||||
?proxy_interface $course,
|
||||
?proxy_interface $group,
|
||||
?proxy_interface $user,
|
||||
?event_collection_interface $repeats,
|
||||
?proxy_interface $coursemodule,
|
||||
$type,
|
||||
times_interface $times,
|
||||
$visible,
|
||||
|
||||
@@ -109,6 +109,9 @@ class create_update_form_mapper implements create_update_form_mapper_interface {
|
||||
} else {
|
||||
$properties->courseid = $data->courseid;
|
||||
}
|
||||
if (empty($properties->groupid)) {
|
||||
$properties->groupid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the form fields back into valid event property.
|
||||
|
||||
@@ -136,16 +136,39 @@ class behat_calendar extends behat_base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to a specific date in the calendar.
|
||||
* Navigate to a specific month in the calendar.
|
||||
*
|
||||
* @Given /^I view the calendar for "(?P<month>\d+)" "(?P<year>\d+)"$/
|
||||
* @param int $month the month selected as a number
|
||||
* @param int $year the four digit year
|
||||
*/
|
||||
public function i_view_the_calendar_for($month, $year) {
|
||||
$time = make_timestamp($year, $month, 1);
|
||||
$this->execute('behat_general::i_visit', ['/calendar/view.php?view=month&course=1&time='.$time]);
|
||||
$this->view_the_calendar('month', 1, $month, $year);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to a specific date in the calendar.
|
||||
*
|
||||
* @Given /^I view the calendar for "(?P<day>\d+)" "(?P<month>\d+)" "(?P<year>\d+)"$/
|
||||
* @param int $day the day selected as a number
|
||||
* @param int $month the month selected as a number
|
||||
* @param int $year the four digit year
|
||||
*/
|
||||
public function i_view_the_calendar_day_view(int $day, int $month, int $year) {
|
||||
$this->view_the_calendar('day', $day, $month, $year);
|
||||
}
|
||||
|
||||
/**
|
||||
* View the correct calendar view with specific day
|
||||
*
|
||||
* @param string $type type of calendar view: month or day
|
||||
* @param int $day the day selected as a number
|
||||
* @param int $month the month selected as a number
|
||||
* @param int $year the four digit year
|
||||
*/
|
||||
private function view_the_calendar(string $type, int $day, int $month, int $year) {
|
||||
$time = make_timestamp($year, $month, $day);
|
||||
$this->execute('behat_general::i_visit', ['/calendar/view.php?view=' . $type . '&course=1&time=' . $time]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,11 +16,13 @@ Feature: Perform basic calendar functionality
|
||||
| Course 1 | C1 | topics |
|
||||
| Course 2 | C2 | topics |
|
||||
| Course 3 | C3 | topics |
|
||||
| Course 4 | C4 | topics |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| student3 | C1 | student |
|
||||
| teacher1 | C1 | teacher |
|
||||
| admin | C1 | editingteacher |
|
||||
And the following "groups" exist:
|
||||
| name | course | idnumber |
|
||||
| Group 1 | C1 | G1 |
|
||||
@@ -195,7 +197,6 @@ Feature: Perform basic calendar functionality
|
||||
Scenario: Admin can only see all courses if calendar_adminseesall setting is enabled.
|
||||
Given I log in as "admin"
|
||||
And I am on "Course 1" course homepage
|
||||
And I enrol "admin" user as "Teacher"
|
||||
And I am viewing site calendar
|
||||
And I click on "New event" "button"
|
||||
And I set the field "Type of event" to "Course"
|
||||
@@ -247,3 +248,69 @@ Feature: Perform basic calendar functionality
|
||||
Then the page should meet accessibility standards
|
||||
And the page should meet "wcag131, wcag143, wcag412" accessibility standards
|
||||
And the page should meet accessibility standards with "wcag131, wcag143, wcag412" extra tests
|
||||
|
||||
@javascript
|
||||
Scenario: Admin can create and edit course events if calendar_adminseesall setting is disabled
|
||||
Given I log in as "admin"
|
||||
And the following config values are set as admin:
|
||||
| calendar_adminseesall | 0 |
|
||||
And I am on "Course 4" course homepage with editing mode on
|
||||
And I add the "Upcoming events" block
|
||||
And I click on "Go to calendar..." "link" in the "Upcoming events" "block"
|
||||
And I click on "New event" "button"
|
||||
And I should see "Course" in the "Type of event" "select"
|
||||
And "Course 4" "autocomplete_selection" should exist
|
||||
And I set the field "Event title" to "Test course event"
|
||||
And I click on "Save" "button"
|
||||
And I should see "Test course event"
|
||||
When I click on "Edit" "link" in the "region-main" "region"
|
||||
Then the field "Event title" matches value "Test course event"
|
||||
And the field "Type of event" matches value "Course"
|
||||
And "Course 4" "autocomplete_selection" should exist
|
||||
|
||||
@javascript
|
||||
Scenario: Changing the event type should clear previous data
|
||||
Given I log in as "admin"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "This month"
|
||||
And I press "New event"
|
||||
And I set the following fields to these values:
|
||||
| Event title | Group 1 event |
|
||||
| Type of event | Group |
|
||||
And I press "Save"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "This month"
|
||||
And I click on "Group 1 event" "link"
|
||||
And I should see "Group event"
|
||||
And I should see "Group 1"
|
||||
When I click on "Edit" "button"
|
||||
And I set the following fields to these values:
|
||||
| Event title | My own user event |
|
||||
| Type of event | user |
|
||||
And I press "Save"
|
||||
And I click on "My own user event" "link"
|
||||
Then I should see "User event"
|
||||
And I should not see "Group 1"
|
||||
And I click on "Edit" "button"
|
||||
And I set the following fields to these values:
|
||||
| Event title | Site event |
|
||||
| Type of event | site |
|
||||
And I press "Save"
|
||||
And I click on "Site event" "link"
|
||||
And I should see "Site event"
|
||||
And I click on "Edit" "button"
|
||||
And I set the following fields to these values:
|
||||
| Event title | Course 1 event |
|
||||
| Type of event | course |
|
||||
And I expand the "Course" autocomplete
|
||||
And I click on "Course 1" item in the autocomplete list
|
||||
And I press "Save"
|
||||
And I click on "Course 1 event" "link"
|
||||
And I should see "Course event"
|
||||
And I click on "Edit" "button"
|
||||
And I set the following fields to these values:
|
||||
| Event title | Category event |
|
||||
| Type of event | category |
|
||||
And I press "Save"
|
||||
And I click on "Category event" "link"
|
||||
And I should see "Category event"
|
||||
|
||||
@@ -74,3 +74,19 @@ Feature: Import and edit calendar events
|
||||
And I upload "calendar/tests/fixtures/import.ics" file to "Calendar file (.ics)" filemanager
|
||||
And I press "Add"
|
||||
And I should see "Site events"
|
||||
|
||||
Scenario: Import iCalendar file with parameter.
|
||||
Given I log in as "admin"
|
||||
And I view the calendar for "7" "2022"
|
||||
And I press "Manage subscriptions"
|
||||
And I set the following fields to these values:
|
||||
| Calendar name | Test Import |
|
||||
| Import from | Calendar file (.ics) |
|
||||
| Type of event | User |
|
||||
And I upload "calendar/tests/fixtures/import_with_parameters.ics" file to "Calendar file (.ics)" filemanager
|
||||
And I press "Add"
|
||||
When I view the calendar for "1" "7" "2022"
|
||||
Then I should see "First event"
|
||||
And I should see "Description of the first event"
|
||||
And I should see "Second event"
|
||||
And I should see "Description of the second event"
|
||||
|
||||
@@ -23,6 +23,7 @@ use core_calendar\local\event\factories\event_factory;
|
||||
use core_calendar\local\event\factories\event_factory_interface;
|
||||
use core_calendar\local\event\mappers\event_mapper;
|
||||
use core_calendar\local\event\mappers\event_mapper_interface;
|
||||
use core_completion\api;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
@@ -366,6 +367,41 @@ class container_test extends \advanced_testcase {
|
||||
$this->assertNull($factory->create_instance($event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that completed activities events do not show.
|
||||
* @covers \core_calendar\local\event::init
|
||||
*/
|
||||
public function test_event_factory_with_completed_module_related_event() {
|
||||
global $CFG, $DB;
|
||||
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create a course.
|
||||
$course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);
|
||||
$user = $this->getDataGenerator()->create_and_enrol($course);
|
||||
// Create an assign activity with a time set.
|
||||
$time = time();
|
||||
$assign = $this->getDataGenerator()->create_module(
|
||||
'assign', ['course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL]);
|
||||
|
||||
// Create the event but set it to tomorrow.
|
||||
$CFG->enablecompletion = true;
|
||||
api::update_completion_date_event($assign->cmid, 'assign', $assign,
|
||||
$time + DAYSECS);
|
||||
|
||||
$this->setUser($user);
|
||||
// Check that we get should be completed event.
|
||||
$this->assertCount(1, \core_calendar\local\event\container::get_event_vault()->get_events());
|
||||
// Then Complete the activity.
|
||||
$completion = new \completion_info($course);
|
||||
$cmassign = get_coursemodule_from_id('assign', $assign->cmid);
|
||||
// This should trigger another call to the update_completion_date_event.
|
||||
$completion->update_state($cmassign, COMPLETION_COMPLETE, $user->id);
|
||||
// Check that we do not see the event anymore.
|
||||
$this->assertCount(0, \core_calendar\local\event\container::get_event_vault()->get_events());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test that the event factory only returns an event if the logged in user
|
||||
* is enrolled in the course.
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
BEGIN:VCALENDAR
|
||||
METHOD:PUBLISH
|
||||
PRODID:-//Huong Nguyen/NONSGML Moodle//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
UID:special_import_moodle_1
|
||||
SUMMARY:First event
|
||||
DESCRIPTION:Description of the first event\n
|
||||
CLASS:PUBLIC
|
||||
LAST-MODIFIED:20220728T034015Z
|
||||
LOCATION:Vietnam
|
||||
DTSTAMP:20220728T034031Z
|
||||
DTSTART:20220701T070000Z
|
||||
DTEND:20220701T070000Z
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
UID:special_import_moodle_2
|
||||
SUMMARY:Second event
|
||||
DESCRIPTION;ALTREP="http://moodle.com/":Description of the second event\n
|
||||
CLASS:PUBLIC
|
||||
LAST-MODIFIED:20220728T034021Z
|
||||
LOCATION:Vietnam
|
||||
DTSTAMP:20220728T034031Z
|
||||
DTSTART:20220701T080000Z
|
||||
DTEND:20220701T080000Z
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
@@ -0,0 +1,261 @@
|
||||
<?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/>.
|
||||
|
||||
use core_competency\competency;
|
||||
use core_competency\competency_framework;
|
||||
use core_competency\plan;
|
||||
|
||||
/**
|
||||
* Behat data generator for core_competency.
|
||||
*
|
||||
* @package core_competency
|
||||
* @category test
|
||||
* @copyright 2022 Noel De Martin
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class behat_core_competency_generator extends behat_generator_base {
|
||||
|
||||
/**
|
||||
* Get a list of the entities that Behat can create using the generator step.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_creatable_entities(): array {
|
||||
return [
|
||||
'competencies' => [
|
||||
'singular' => 'competency',
|
||||
'datagenerator' => 'competency',
|
||||
'required' => ['shortname', 'competencyframework'],
|
||||
'switchids' => ['competencyframework' => 'competencyframeworkid'],
|
||||
],
|
||||
'course_competencies' => [
|
||||
'singular' => 'course_competency',
|
||||
'datagenerator' => 'course_competency',
|
||||
'required' => ['course', 'competency'],
|
||||
'switchids' => ['course' => 'courseid', 'competency' => 'competencyid'],
|
||||
],
|
||||
'frameworks' => [
|
||||
'singular' => 'framework',
|
||||
'datagenerator' => 'framework',
|
||||
'required' => ['shortname'],
|
||||
'switchids' => ['scale' => 'scaleid'],
|
||||
],
|
||||
'plans' => [
|
||||
'singular' => 'plan',
|
||||
'datagenerator' => 'plan',
|
||||
'required' => ['name'],
|
||||
'switchids' => ['user' => 'userid'],
|
||||
],
|
||||
'related_competencies' => [
|
||||
'singular' => 'related_competency',
|
||||
'datagenerator' => 'related_competency',
|
||||
'required' => ['competency', 'relatedcompetency'],
|
||||
'switchids' => ['competency' => 'competencyid', 'relatedcompetency' => 'relatedcompetencyid'],
|
||||
],
|
||||
'user_competency' => [
|
||||
'singular' => 'user_competency',
|
||||
'datagenerator' => 'user_competency',
|
||||
'required' => ['competency', 'user'],
|
||||
'switchids' => ['competency' => 'competencyid', 'user' => 'userid'],
|
||||
],
|
||||
'user_competency_courses' => [
|
||||
'singular' => 'user_competency_course',
|
||||
'datagenerator' => 'user_competency_course',
|
||||
'required' => ['course', 'competency', 'user'],
|
||||
'switchids' => ['course' => 'courseid', 'competency' => 'competencyid', 'user' => 'userid'],
|
||||
],
|
||||
'user_competency_plans' => [
|
||||
'singular' => 'user_competency_plan',
|
||||
'datagenerator' => 'user_competency_plan',
|
||||
'required' => ['plan', 'competency', 'user'],
|
||||
'switchids' => ['plan' => 'planid', 'competency' => 'competencyid', 'user' => 'userid'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the competecy framework id using an idnumber.
|
||||
*
|
||||
* @param string $idnumber
|
||||
* @return int The competecy framework id
|
||||
*/
|
||||
protected function get_competencyframework_id(string $idnumber): int {
|
||||
global $DB;
|
||||
|
||||
if (!$id = $DB->get_field('competency_framework', 'id', ['idnumber' => $idnumber])) {
|
||||
throw new Exception('The specified competency framework with idnumber "' . $idnumber . '" could not be found.');
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the competecy id using an idnumber.
|
||||
*
|
||||
* @param string $idnumber
|
||||
* @return int The competecy id
|
||||
*/
|
||||
protected function get_competency_id(string $idnumber): int {
|
||||
global $DB;
|
||||
|
||||
if (!$id = $DB->get_field('competency', 'id', ['idnumber' => $idnumber])) {
|
||||
throw new Exception('The specified competency with idnumber "' . $idnumber . '" could not be found.');
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the learning plan id using a name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return int The learning plan id
|
||||
*/
|
||||
protected function get_plan_id(string $name): int {
|
||||
global $DB;
|
||||
|
||||
if (!$id = $DB->get_field('competency_plan', 'id', ['name' => $name])) {
|
||||
throw new Exception('The specified learning plan with name "' . $name . '" could not be found.');
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the related competecy id using an idnumber.
|
||||
*
|
||||
* @param string $idnumber
|
||||
* @return int The related competecy id
|
||||
*/
|
||||
protected function get_relatedcompetency_id(string $idnumber): int {
|
||||
return $this->get_competency_id($idnumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a plan.
|
||||
*
|
||||
* @param array $data Plan data.
|
||||
*/
|
||||
public function process_plan(array $data): void {
|
||||
$generator = $this->get_data_generator();
|
||||
$competencyids = $data['competencyids'] ?? [];
|
||||
|
||||
unset($data['competencyids']);
|
||||
|
||||
$plan = $generator->create_plan($data);
|
||||
|
||||
foreach ($competencyids as $competencyid) {
|
||||
$generator->create_plan_competency([
|
||||
'planid' => $plan->get('id'),
|
||||
'competencyid' => $competencyid,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Preprocess user competency data.
|
||||
*
|
||||
* @param array $data Raw data.
|
||||
* @return array Processed data.
|
||||
*/
|
||||
protected function preprocess_user_competency(array $data): array {
|
||||
$this->prepare_grading($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preprocess user course competency data.
|
||||
*
|
||||
* @param array $data Raw data.
|
||||
* @return array Processed data.
|
||||
*/
|
||||
protected function preprocess_user_competency_course(array $data): array {
|
||||
$this->prepare_grading($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preprocess user learning plan competency data.
|
||||
*
|
||||
* @param array $data Raw data.
|
||||
* @return array Processed data.
|
||||
*/
|
||||
protected function preprocess_user_competency_plan(array $data): array {
|
||||
$this->prepare_grading($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preprocess plan data.
|
||||
*
|
||||
* @param array $data Raw data.
|
||||
* @return array Processed data.
|
||||
*/
|
||||
protected function preprocess_plan(array $data): array {
|
||||
if (isset($data['competencies'])) {
|
||||
$competencies = array_map('trim', str_getcsv($data['competencies']));
|
||||
$data['competencyids'] = array_map([$this, 'get_competency_id'], $competencies);
|
||||
|
||||
unset($data['competencies']);
|
||||
}
|
||||
|
||||
global $USER;
|
||||
|
||||
return $data + [
|
||||
'userid' => $USER->id,
|
||||
'status' => plan::STATUS_ACTIVE,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare grading attributes for record data.
|
||||
*
|
||||
* @param array $data Record data.
|
||||
*/
|
||||
protected function prepare_grading(array &$data): void {
|
||||
if (!isset($data['grade'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
global $DB;
|
||||
|
||||
$competency = competency::get_record(['id' => $data['competencyid']]);
|
||||
$competencyframework = competency_framework::get_record(['id' => $competency->get('competencyframeworkid')]);
|
||||
$scale = $DB->get_field('scale', 'scale', ['id' => $competencyframework->get('scaleid')]);
|
||||
$grades = array_map('trim', explode(',', $scale));
|
||||
$grade = array_search($data['grade'], $grades);
|
||||
|
||||
if ($grade === false) {
|
||||
throw new Exception('The grade "'.$data['grade'].'" was not found in the "'.
|
||||
$competencyframework->get('shortname').'" competency framework.');
|
||||
}
|
||||
|
||||
$data['proficiency'] = true;
|
||||
$data['grade'] = $grade + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the module data generator.
|
||||
*
|
||||
* @return core_competency_generator Competency data generator.
|
||||
*/
|
||||
protected function get_data_generator(): core_competency_generator {
|
||||
return $this->componentdatagenerator;
|
||||
}
|
||||
}
|
||||
@@ -136,7 +136,11 @@ class core_competency_generator extends component_generator_base {
|
||||
* @return competency_framework
|
||||
*/
|
||||
public function create_framework($record = null) {
|
||||
$generator = phpunit_util::get_data_generator();
|
||||
if (defined('BEHAT_TEST') && BEHAT_TEST) {
|
||||
$generator = behat_util::get_data_generator();
|
||||
} else {
|
||||
$generator = phpunit_util::get_data_generator();
|
||||
}
|
||||
$this->frameworkcount++;
|
||||
$i = $this->frameworkcount;
|
||||
$record = (object) $record;
|
||||
@@ -584,4 +588,3 @@ class core_competency_generator extends component_generator_base {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -58,9 +58,9 @@ class bankcontent implements renderable, templatable {
|
||||
*
|
||||
* @param \core_contentbank\content[] $contents Array of content bank contents.
|
||||
* @param array $toolbar List of content bank toolbar options.
|
||||
* @param \context $context Optional context to check (default null)
|
||||
* @param \context|null $context Optional context to check (default null)
|
||||
*/
|
||||
public function __construct(array $contents, array $toolbar, \context $context = null) {
|
||||
public function __construct(array $contents, array $toolbar, ?\context $context) {
|
||||
$this->contents = $contents;
|
||||
$this->toolbar = $toolbar;
|
||||
$this->context = $context;
|
||||
|
||||
+2
-5
@@ -2477,9 +2477,7 @@ function update_course($data, $editoroptions = NULL) {
|
||||
function average_number_of_participants(bool $onlyactive = false, int $lastloginsince = null): float {
|
||||
global $DB;
|
||||
|
||||
$params = [
|
||||
'siteid' => SITEID,
|
||||
];
|
||||
$params = [];
|
||||
|
||||
$sql = "SELECT DISTINCT ue.userid, e.courseid
|
||||
FROM {user_enrolments} ue
|
||||
@@ -2490,8 +2488,7 @@ function average_number_of_participants(bool $onlyactive = false, int $lastlogin
|
||||
$sql .= "JOIN {user} u ON u.id = ue.userid ";
|
||||
}
|
||||
|
||||
$sql .= "WHERE e.courseid <> :siteid
|
||||
AND c.visible = 1 ";
|
||||
$sql .= "WHERE e.courseid <> " . SITEID . " AND c.visible = 1 ";
|
||||
|
||||
if ($onlyactive) {
|
||||
$sql .= "AND ue.status = :active
|
||||
|
||||
@@ -14,11 +14,11 @@ Feature: Force group mode in a course
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And the following "activities" exist:
|
||||
| activity | course | name |
|
||||
| chat | C1 | Chat room |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Chat" to section "1" and I fill the form with:
|
||||
| Name of this chat room | Chat room |
|
||||
| Description | Chat description |
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
|
||||
@javascript
|
||||
|
||||
@@ -43,15 +43,15 @@ Feature: Course paged mode
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | category | format | coursedisplay | numsections | startdate |
|
||||
| Course 1 | C1 | 0 | <courseformat> | 1 | 3 | 0 |
|
||||
And I log in as "admin"
|
||||
And the following "activities" exist:
|
||||
| activity | course | name |
|
||||
| chat | C1 | Chat room |
|
||||
When I log in as "admin"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I click on <section1> "link" in the <section1> "section"
|
||||
And I should see <section1> in the "div.single-section" "css_element"
|
||||
And I should see <section2> in the ".single-section span.mdl-right" "css_element"
|
||||
And I should not see <prevunexistingsection> in the ".single-section" "css_element"
|
||||
When I add a "Chat" to section "1" and I fill the form with:
|
||||
| Name of this chat room | Chat room |
|
||||
| Description | Chat description |
|
||||
Then I should see <section1> in the "div.single-section" "css_element"
|
||||
And I should see <section2> in the ".single-section span.mdl-right" "css_element"
|
||||
And I should not see <prevunexistingsection> in the ".single-section" "css_element"
|
||||
|
||||
@@ -14,6 +14,9 @@ Feature: Restrict activities availability
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And the following "activities" exist:
|
||||
| activity | course | name |
|
||||
| chat | C1 | Test chat name |
|
||||
|
||||
@javascript
|
||||
Scenario: Activities can be added with the default permissions
|
||||
@@ -22,9 +25,6 @@ Feature: Restrict activities availability
|
||||
When I add a "Glossary" to section "1" and I fill the form with:
|
||||
| Name | Test glossary name |
|
||||
| Description | Test glossary description |
|
||||
And I add a "Chat" to section "1" and I fill the form with:
|
||||
| Name of this chat room | Test chat name |
|
||||
| Description | Test chat description |
|
||||
Then I should see "Test glossary name"
|
||||
And I should see "Test chat name"
|
||||
|
||||
|
||||
@@ -14,13 +14,10 @@ Feature: View subfolders in a course in-line
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Folder" to section "3" and I fill the form with:
|
||||
| Name | Test folder |
|
||||
| Display folder contents | On a separate page |
|
||||
| Show subfolders expanded | |
|
||||
And I am on the "Test folder" "folder activity" page
|
||||
And the following "activities" exist:
|
||||
| activity | course | name | display | showexpanded |
|
||||
| folder | C1 | Test folder | 0 | 0 |
|
||||
And I am on the "Test folder" "folder activity" page logged in as "teacher1"
|
||||
And I press "Edit"
|
||||
And I press "Create folder"
|
||||
And I set the field "New folder name" to "Test subfolder 1"
|
||||
|
||||
@@ -101,7 +101,8 @@ class manage_table extends \table_sql {
|
||||
* @return string
|
||||
*/
|
||||
public function col_name($tool) {
|
||||
$name = helper::get_name($tool);
|
||||
$toolcontext = \context::instance_by_id($tool->contextid, IGNORE_MISSING);
|
||||
$name = $toolcontext ? helper::get_name($tool) : $this->get_deleted_activity_name_html($tool);
|
||||
|
||||
return $this->get_display_text($tool, $name);
|
||||
}
|
||||
@@ -243,9 +244,39 @@ class manage_table extends \table_sql {
|
||||
*/
|
||||
protected function get_display_text($tool, $text) {
|
||||
if ($tool->status != ENROL_INSTANCE_ENABLED) {
|
||||
return \html_writer::tag('span', $text, array('class' => 'dimmed_text'));
|
||||
return \html_writer::tag('div', $text, array('class' => 'dimmed_text'));
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a warning icon, with tooltip, describing enrolment instances sharing activities which have been deleted.
|
||||
*
|
||||
* @param \stdClass $tool the tool instance record.
|
||||
* @return string the HTML for the name column.
|
||||
*/
|
||||
protected function get_deleted_activity_name_html(\stdClass $tool): string {
|
||||
global $OUTPUT;
|
||||
$icon = \html_writer::tag(
|
||||
'a',
|
||||
$OUTPUT->pix_icon('enrolinstancewarning', get_string('deletedactivityalt' , 'enrol_lti'), 'enrol_lti'), [
|
||||
"class" => "btn btn-link p-0",
|
||||
"role" => "button",
|
||||
"data-container" => "body",
|
||||
"data-toggle" => "popover",
|
||||
"data-placement" => right_to_left() ? "left" : "right",
|
||||
"data-content" => get_string('deletedactivitydescription', 'enrol_lti'),
|
||||
"data-html" => "true",
|
||||
"tabindex" => "0",
|
||||
"data-trigger" => "focus"
|
||||
]
|
||||
);
|
||||
$name = \html_writer::span($icon . get_string('deletedactivity', 'enrol_lti'));
|
||||
if ($tool->name) {
|
||||
$name .= \html_writer::empty_tag('br') . \html_writer::empty_tag('br') . $tool->name;
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,9 +50,6 @@ class sync_members extends scheduled_task {
|
||||
/** @var array Array of user photos. */
|
||||
protected $userphotos = [];
|
||||
|
||||
/** @var array Array of current LTI users. */
|
||||
protected $currentusers = [];
|
||||
|
||||
/** @var data_connector $dataconnector A data_connector instance. */
|
||||
protected $dataconnector;
|
||||
|
||||
@@ -111,15 +108,15 @@ class sync_members extends scheduled_task {
|
||||
|
||||
// Fetched members count.
|
||||
$membercount = count($members);
|
||||
$usercount += $membercount;
|
||||
mtrace("$membercount members received.\n");
|
||||
|
||||
// Process member information.
|
||||
list($usercount, $enrolcount) = $this->sync_member_information($tool, $consumer, $members);
|
||||
}
|
||||
list($users, $enrolledcount) = $this->sync_member_information($tool, $consumer, $members);
|
||||
$enrolcount += $enrolledcount;
|
||||
|
||||
// Now we check if we have to unenrol users who were not listed.
|
||||
if ($this->should_sync_unenrol($tool->membersyncmode)) {
|
||||
$unenrolcount = $this->sync_unenrol($tool);
|
||||
// Now sync unenrolments for the consumer.
|
||||
$unenrolcount += $this->sync_unenrol($tool, $consumer->getKey(), $users);
|
||||
}
|
||||
|
||||
mtrace("Completed - Synced members for tool '$tool->id' in the course '$tool->courseid'. " .
|
||||
@@ -216,17 +213,15 @@ class sync_members extends scheduled_task {
|
||||
* @param stdClass $tool
|
||||
* @param ToolConsumer $consumer
|
||||
* @param User[] $members
|
||||
* @return array An array containing the number of members that were processed and the number of members that were enrolled.
|
||||
* @return array An array of users from processed members and the number that were enrolled.
|
||||
*/
|
||||
protected function sync_member_information(stdClass $tool, ToolConsumer $consumer, $members) {
|
||||
global $DB;
|
||||
$usercount = 0;
|
||||
$users = [];
|
||||
$enrolcount = 0;
|
||||
|
||||
// Process member information.
|
||||
foreach ($members as $member) {
|
||||
$usercount++;
|
||||
|
||||
// Set the user data.
|
||||
$user = new stdClass();
|
||||
$user->username = helper::create_username($consumer->getKey(), $member->ltiUserId);
|
||||
@@ -248,9 +243,7 @@ class sync_members extends scheduled_task {
|
||||
user_update_user($user);
|
||||
|
||||
// Add the information to the necessary arrays.
|
||||
if (!in_array($user->id, $this->currentusers)) {
|
||||
$this->currentusers[] = $user->id;
|
||||
}
|
||||
$users[$user->id] = $user;
|
||||
$this->userphotos[$user->id] = $member->image;
|
||||
} else {
|
||||
if ($this->should_sync_enrol($tool->membersyncmode)) {
|
||||
@@ -264,7 +257,7 @@ class sync_members extends scheduled_task {
|
||||
$user->id = user_create_user($user);
|
||||
|
||||
// Add the information to the necessary arrays.
|
||||
$this->currentusers[] = $user->id;
|
||||
$users[$user->id] = $user;
|
||||
$this->userphotos[$user->id] = $member->image;
|
||||
}
|
||||
}
|
||||
@@ -290,16 +283,18 @@ class sync_members extends scheduled_task {
|
||||
}
|
||||
}
|
||||
|
||||
return [$usercount, $enrolcount];
|
||||
return [$users, $enrolcount];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs unenrolment of users that are no longer enrolled in the consumer side.
|
||||
*
|
||||
* @param stdClass $tool The tool record object.
|
||||
* @param string $consumerkey ensure we only unenrol users from this tool consumer.
|
||||
* @param array $currentusers The list of current users.
|
||||
* @return int The number of users that have been unenrolled.
|
||||
*/
|
||||
protected function sync_unenrol(stdClass $tool) {
|
||||
protected function sync_unenrol(stdClass $tool, string $consumerkey, array $currentusers) {
|
||||
global $DB;
|
||||
|
||||
$ltiplugin = enrol_get_plugin('lti');
|
||||
@@ -308,16 +303,18 @@ class sync_members extends scheduled_task {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (empty($this->currentusers)) {
|
||||
if (empty($currentusers)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$unenrolcount = 0;
|
||||
|
||||
$ltiusersrs = $DB->get_recordset('enrol_lti_users', array('toolid' => $tool->id), 'lastaccess DESC', 'userid');
|
||||
$select = "toolid = :toolid AND " . $DB->sql_compare_text('consumerkey', 255) . " = :consumerkey";
|
||||
$ltiusersrs = $DB->get_recordset_select('enrol_lti_users', $select, ['toolid' => $tool->id, 'consumerkey' => $consumerkey],
|
||||
'lastaccess DESC', 'userid');
|
||||
// Go through the users and check if any were never listed, if so, remove them.
|
||||
foreach ($ltiusersrs as $ltiuser) {
|
||||
if (!in_array($ltiuser->userid, $this->currentusers)) {
|
||||
if (!array_key_exists($ltiuser->userid, $currentusers)) {
|
||||
$instance = new stdClass();
|
||||
$instance->id = $tool->enrolid;
|
||||
$instance->courseid = $tool->courseid;
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
* @return boolean
|
||||
*/
|
||||
function xmldb_enrol_lti_upgrade($oldversion) {
|
||||
global $CFG;
|
||||
global $CFG, $DB;
|
||||
|
||||
// Automatically generated Moodle v3.6.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
@@ -57,5 +57,17 @@ function xmldb_enrol_lti_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.11.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2021051701) {
|
||||
// Disable all orphaned enrolment method instances.
|
||||
$sql = "id IN (SELECT t.enrolid
|
||||
FROM {enrol_lti_tools} t
|
||||
LEFT JOIN {context} c ON (t.contextid = c.id)
|
||||
WHERE c.id IS NULL)";
|
||||
$DB->set_field_select('enrol', 'status', 1, $sql);
|
||||
|
||||
// Lti savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2021051701, 'enrol', 'lti');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
$string['allowframeembedding'] = 'Note: It is recommended that the site administration setting \'Allow frame embedding\' is enabled, so that tools are displayed within a frame rather than in a new window.';
|
||||
$string['authltimustbeenabled'] = 'Note: This plugin requires the LTI authentication plugin to be enabled too.';
|
||||
$string['couldnotestablishproxy'] = 'Could not establish proxy with consumer.';
|
||||
$string['deletedactivity'] = 'Deleted activity';
|
||||
$string['deletedactivityalt'] = 'The activity shared by this instance has been deleted';
|
||||
$string['deletedactivitydescription'] = 'The activity shared by this instance has been deleted. You can select another activity to share by editing the instance or, if no longer required, you can simply delete the instance. Deleting the instance will remove any associated user enrolments.';
|
||||
$string['enrolenddate'] = 'End date';
|
||||
$string['enrolenddate_help'] = 'If enabled, users can access until this date only.';
|
||||
$string['enrolenddateerror'] = 'Enrolment end date cannot be earlier than start date';
|
||||
|
||||
@@ -415,3 +415,27 @@ function enrol_lti_extend_navigation_course($navigation, $course, $context) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get icon mapping for font-awesome.
|
||||
*/
|
||||
function enrol_lti_get_fontawesome_icon_map() {
|
||||
return [
|
||||
'enrol_lti:enrolinstancewarning' => 'fa-exclamation-circle text-danger',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-delete course module hook which disables any methods referring to the deleted module, preventing launches and allowing remap.
|
||||
*
|
||||
* @param stdClass $cm The deleted course module record.
|
||||
*/
|
||||
function enrol_lti_pre_course_module_delete(stdClass $cm) {
|
||||
global $DB;
|
||||
$sql = "id IN (SELECT t.enrolid
|
||||
FROM {enrol_lti_tools} t
|
||||
JOIN {context} c ON (t.contextid = c.id)
|
||||
WHERE c.contextlevel = :contextlevel
|
||||
AND c.instanceid = :cmid)";
|
||||
$DB->set_field_select('enrol', 'status', ENROL_INSTANCE_DISABLED, $sql, ['contextlevel' => CONTEXT_MODULE, 'cmid' => $cm->id]);
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 339 B |
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMid meet"><path d="M896 128q209 0 385.5 103t279.5 279.5 103 385.5-103 385.5-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103zm128 1247v-190q0-14-9-23.5t-22-9.5h-192q-13 0-23 10t-10 23v190q0 13 10 23t23 10h192q13 0 22-9.5t9-23.5zm-2-344l18-621q0-12-10-18-10-8-24-8h-220q-14 0-24 8-10 6-10 18l17 621q0 10 10 17.5t24 7.5h185q14 0 23.5-7.5t10.5-17.5z" fill="#999"/></svg>
|
||||
|
After Width: | Height: | Size: 560 B |
@@ -169,4 +169,36 @@ class lib_test extends \advanced_testcase {
|
||||
// LTI enrolment has 1 enrol actions for active users -- unenrol.
|
||||
$this->assertCount(1, $actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behaviour of an enrolment method when the activity to which it provides access is deleted.
|
||||
*
|
||||
* @covers \enrol_lti_pre_course_module_delete
|
||||
*/
|
||||
public function test_course_module_deletion() {
|
||||
// Create two modules and publish them.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$mod = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);
|
||||
$mod2 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);
|
||||
$tooldata = [
|
||||
'cmid' => $mod->cmid,
|
||||
'courseid' => $course->id,
|
||||
];
|
||||
$tool = $this->getDataGenerator()->create_lti_tool((object)$tooldata);
|
||||
$tooldata['cmid'] = $mod2->cmid;
|
||||
$tool2 = $this->getDataGenerator()->create_lti_tool((object)$tooldata);
|
||||
|
||||
// Verify the instances are both enabled.
|
||||
$modinstance = helper::get_lti_tool($tool->id);
|
||||
$mod2instance = helper::get_lti_tool($tool2->id);
|
||||
$this->assertEquals(ENROL_INSTANCE_ENABLED, $modinstance->status);
|
||||
$this->assertEquals(ENROL_INSTANCE_ENABLED, $mod2instance->status);
|
||||
|
||||
// Delete a module and verify the associated instance is disabled.
|
||||
course_delete_module($mod->cmid);
|
||||
$modinstance = helper::get_lti_tool($tool->id);
|
||||
$mod2instance = helper::get_lti_tool($tool2->id);
|
||||
$this->assertEquals(ENROL_INSTANCE_DISABLED, $modinstance->status);
|
||||
$this->assertEquals(ENROL_INSTANCE_ENABLED, $mod2instance->status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,10 +213,10 @@ class sync_members_test extends \advanced_testcase {
|
||||
* Test for sync_members::sync_member_information().
|
||||
*/
|
||||
public function test_sync_member_information() {
|
||||
list($totalcount, $enrolledcount) = $this->task->sync_member_information($this->tool, $this->consumer, $this->members);
|
||||
list($users, $enrolledcount) = $this->task->sync_member_information($this->tool, $this->consumer, $this->members);
|
||||
$membercount = count($this->members);
|
||||
$this->assertCount(10, $this->members);
|
||||
$this->assertEquals($membercount, $totalcount);
|
||||
$this->assertCount($membercount, $users);
|
||||
$this->assertEquals($membercount, $enrolledcount);
|
||||
}
|
||||
|
||||
@@ -225,10 +225,10 @@ class sync_members_test extends \advanced_testcase {
|
||||
*/
|
||||
public function test_sync_profile_images() {
|
||||
$task = $this->task;
|
||||
list($totalcount, $enrolledcount) = $task->sync_member_information($this->tool, $this->consumer, $this->members);
|
||||
list($users, $enrolledcount) = $task->sync_member_information($this->tool, $this->consumer, $this->members);
|
||||
$membercount = count($this->members);
|
||||
$this->assertCount(10, $this->members);
|
||||
$this->assertEquals($membercount, $totalcount);
|
||||
$this->assertCount($membercount, $users);
|
||||
$this->assertEquals($membercount, $enrolledcount);
|
||||
|
||||
// Suppress output.
|
||||
@@ -244,14 +244,14 @@ class sync_members_test extends \advanced_testcase {
|
||||
$tool = $this->tool;
|
||||
$task = $this->task;
|
||||
|
||||
$task->sync_member_information($tool, $this->consumer, $this->members);
|
||||
list($users) = $task->sync_member_information($tool, $this->consumer, $this->members);
|
||||
|
||||
// Simulate that the fetched list of current users has been reduced by 3.
|
||||
$unenrolcount = 3;
|
||||
for ($i = 0; $i < $unenrolcount; $i++) {
|
||||
$task->pop_current_users();
|
||||
array_pop($users);
|
||||
}
|
||||
$this->assertEquals($unenrolcount, $task->sync_unenrol($tool));
|
||||
$this->assertEquals($unenrolcount, $task->sync_unenrol($tool, 'Consumer1Key', $users));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,13 +297,6 @@ class dummy_sync_members_task extends sync_members {
|
||||
return $this->dataconnector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that removes an element in the array of current users.
|
||||
*/
|
||||
public function pop_current_users() {
|
||||
array_pop($this->currentusers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes sync_members::do_context_membership_request()
|
||||
*
|
||||
@@ -390,10 +383,12 @@ class dummy_sync_members_task extends sync_members {
|
||||
* Exposes sync_members::sync_unenrol()
|
||||
*
|
||||
* @param \stdClass $tool
|
||||
* @param string $consumerkey
|
||||
* @param array $currentusers
|
||||
* @return int
|
||||
*/
|
||||
public function sync_unenrol(\stdClass $tool) {
|
||||
$count = parent::sync_unenrol($tool);
|
||||
public function sync_unenrol(\stdClass $tool, string $consumerkey, array $currentusers) {
|
||||
$count = parent::sync_unenrol($tool, $consumerkey, $currentusers);
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2021051700; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2021051701; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2021051100; // Requires this Moodle version.
|
||||
$plugin->component = 'enrol_lti'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
|
||||
.gradingform_guide.editor .addcriterion input,
|
||||
.gradingform_guide.editor .addcomment input {
|
||||
background-position: 5px 8px;
|
||||
background-position: left 5px top 8px;
|
||||
height: 30px;
|
||||
line-height: 29px;
|
||||
margin-bottom: 14px;
|
||||
|
||||
+6
-1
@@ -3050,7 +3050,7 @@ abstract class grade_helper {
|
||||
* @return array
|
||||
*/
|
||||
public static function get_plugins_reports($courseid) {
|
||||
global $SITE;
|
||||
global $SITE, $CFG;
|
||||
|
||||
if (self::$gradereports !== null) {
|
||||
return self::$gradereports;
|
||||
@@ -3064,6 +3064,11 @@ abstract class grade_helper {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove outcomes report if outcomes not enabled.
|
||||
if ($plugin === 'outcomes' && empty($CFG->enableoutcomes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove ones we can't see
|
||||
if (!has_capability('gradereport/'.$plugin.':view', $context)) {
|
||||
continue;
|
||||
|
||||
@@ -39,6 +39,10 @@ $context = context_course::instance($course->id);
|
||||
|
||||
require_capability('gradereport/outcomes:view', $context);
|
||||
|
||||
if (empty($CFG->enableoutcomes)) {
|
||||
redirect(course_get_url($course), get_string('outcomesdisabled', 'core_grades'));
|
||||
}
|
||||
|
||||
// First make sure we have proper final grades.
|
||||
grade_regrade_final_grades_if_required($course);
|
||||
|
||||
|
||||
@@ -71,7 +71,6 @@ Feature: We can enter in grades and view reports from the gradebook
|
||||
Scenario: Grade a grade item and ensure the results display correctly in the gradebook
|
||||
When I navigate to "View > User report" in the course gradebook
|
||||
And "Grader report" "link" should exist
|
||||
And "Outcomes report" "link" should exist
|
||||
And the "Select all or one user" select box should contain "All users (1)"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
|
||||
@@ -277,7 +277,13 @@ class core extends H5PCore {
|
||||
$file->delete();
|
||||
|
||||
$librarykey = static::libraryToString($library);
|
||||
$libraryid = $factory->get_storage()->h5pC->librariesJsonData[$librarykey]["libraryId"];
|
||||
|
||||
$libraryjson = $factory->get_storage()->h5pC->librariesJsonData[$librarykey];
|
||||
if (!array_key_exists('libraryId', $libraryjson)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$libraryid = $libraryjson['libraryId'];
|
||||
|
||||
// Update example and tutorial (if any of them are defined in $library).
|
||||
$params = ['id' => $libraryid];
|
||||
|
||||
@@ -93,11 +93,13 @@ class libraries implements renderable, templatable {
|
||||
$version->toggleenabledurl = new moodle_url('/h5p/libraries.php', [
|
||||
'id' => $version->id,
|
||||
'action' => 'disable',
|
||||
'sesskey' => sesskey(),
|
||||
]);
|
||||
} else {
|
||||
$version->toggleenabledurl = new moodle_url('/h5p/libraries.php', [
|
||||
'id' => $version->id,
|
||||
'action' => 'enable',
|
||||
'sesskey' => sesskey(),
|
||||
]);
|
||||
}
|
||||
$installed[] = $version;
|
||||
|
||||
@@ -66,6 +66,8 @@ if ($deletelibrary) {
|
||||
}
|
||||
|
||||
if (!is_null($action)) {
|
||||
require_sesskey();
|
||||
|
||||
if ($action == 'enable' || $action == 'disable') {
|
||||
// If action is enable or disable, library id is required too.
|
||||
$libraryid = required_param('id', PARAM_INT);
|
||||
|
||||
@@ -31,4 +31,4 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['parentlanguage'] = 'de';
|
||||
$string['thisdirection'] = 'ltr';
|
||||
$string['thislanguage'] = 'Deutsch (Du)';
|
||||
$string['thislanguage'] = 'Deutsch (du)';
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
*
|
||||
* Do not edit this file manually! It contains just a subset of strings
|
||||
* needed during the very first steps of installation. This file was
|
||||
* generated automatically by export-installer.php (which is part of AMOS
|
||||
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
|
||||
* list of strings defined in /install/stringnames.txt.
|
||||
*
|
||||
* @package installer
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['thislanguage'] = 'isiXhosa';
|
||||
+4
-1
@@ -419,7 +419,7 @@ $string['courselistshortnames_desc'] = 'If enabled, course short names will be d
|
||||
$string['coursemgmt'] = 'Manage courses and categories';
|
||||
$string['courseoverview'] = 'Course overview';
|
||||
$string['courserequestnotify'] = 'Course request notification';
|
||||
$string['courserequestnotifyemail'] = 'User {$a->user} requested a new course at {$a->link}';
|
||||
$string['courserequestnotifyemail'] = '{$a->user} requested a new course <a href="{$a->link}">{$a->link}</a>';
|
||||
$string['courserequests'] = 'Course requests';
|
||||
$string['courserequestspending'] = 'Pending course requests';
|
||||
$string['courses'] = 'Courses';
|
||||
@@ -1506,6 +1506,9 @@ $string['warningiconvbuggy'] = 'Your version of the iconv library does not suppo
|
||||
$string['webproxy'] = 'Web proxy';
|
||||
$string['webproxyinfo'] = 'Fill in the following options if your Moodle server cannot access the internet directly. Internet access is required for the download of environment data, language packs, RSS feeds, timezones, etc.<br /><em>The PHP cURL extension is highly recommended.</em>';
|
||||
$string['xmlrpcrecommended'] = 'The XMLRPC extension is useful for web services and Moodle networking.';
|
||||
$string['xmlrpcmaharaenabled'] = 'It has been detected that the Mahara ePortfolio is enabled on your site. This feature relies on the PHP XML-RPC extension which is no longer maintained by PHP.';
|
||||
$string['xmlrpcmnetenabled'] = 'It has been detected that the Moodle Networking is enabled on your site. This feature relies on the PHP XML-RPC extension which is no longer maintained by PHP.';
|
||||
$string['xmlrpcwebserviceenabled'] = 'It has been detected that the XML-RPC Web Service protocol is enabled on your site. This feature relies on the PHP XML-RPC extension which is no longer maintained by PHP.';
|
||||
$string['yuicomboloading'] = 'YUI combo loading';
|
||||
$string['ziprequired'] = 'The Zip PHP extension is now required by Moodle, info-ZIP binaries or PclZip library are not used anymore.';
|
||||
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ $string['asyncemailenable'] = 'Enable message notifications';
|
||||
$string['asyncemailenabledetail'] = 'If enabled, users will receive a message when an asynchronous backup or restore completes.';
|
||||
$string['asyncmessagebody'] = 'Message';
|
||||
$string['asyncmessagebodydetail'] = 'Message to send when an asynchronous backup or restore completes.';
|
||||
$string['asyncmessagebodydefault'] = 'Hi {user_firstname},<br/> Your {operation} (ID: {backupid}) has completed successfully. <br/><br/>You can access it here: {link}.';
|
||||
$string['asyncmessagebodydefault'] = 'Your {operation} (ID: {backupid}) has completed successfully. You can access it here: <a href="{link}">{link}</a>.';
|
||||
$string['asyncmessagesubject'] = 'Subject';
|
||||
$string['asyncmessagesubjectdetail'] = 'Message subject';
|
||||
$string['asyncmessagesubjectdefault'] = 'Moodle {operation} completed successfully';
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ $string['selecteditems'] = 'Selected items:';
|
||||
$string['showadvanced'] = 'Show advanced';
|
||||
$string['showless'] = 'Show less...';
|
||||
$string['showmore'] = 'Show more...';
|
||||
$string['somefieldsrequired'] = 'There are required fields in this form marked {$a}.';
|
||||
$string['somefieldsrequired'] = '{$a} Required';
|
||||
$string['suggestions'] = 'Suggestions';
|
||||
$string['time'] = 'Time';
|
||||
$string['timeunit'] = 'Time unit';
|
||||
|
||||
@@ -599,6 +599,7 @@ $string['outcomescoursecustom'] = 'Custom used (no remove)';
|
||||
$string['outcomescoursenotused'] = 'Standard not used';
|
||||
$string['outcomescourseused'] = 'Standard used (no remove)';
|
||||
$string['outcomescustom'] = 'Custom outcomes';
|
||||
$string['outcomesdisabled'] = 'Outcomes are not enabled';
|
||||
$string['outcomeshortname'] = 'Short name';
|
||||
$string['outcomesstandard'] = 'Standard outcomes';
|
||||
$string['outcomesstandardavailable'] = 'Available standard outcomes';
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ $string['disableall'] = 'Disable notifications';
|
||||
$string['disabled'] = 'Messaging is disabled on this site';
|
||||
$string['disallowed'] = 'Disallowed';
|
||||
$string['editmessages'] = 'Edit messages';
|
||||
$string['emailtagline'] = 'This is a copy of a message sent to you at "{$a->sitename}". Go to {$a->url} to reply.';
|
||||
$string['emailtagline'] = 'This is a copy of a message sent to you on the site {$a->sitename}. Go to <a href="{$a->url}">{$a->url}</a> to reply.';
|
||||
$string['enabled'] = 'Enabled';
|
||||
$string['errorcallingprocessor'] = 'Error calling defined output';
|
||||
$string['errorconversationdoesnotexist'] = 'Conversation does not exist';
|
||||
|
||||
@@ -363,6 +363,7 @@ $string['europe/jersey'] = 'Europe/Jersey';
|
||||
$string['europe/kaliningrad'] = 'Europe/Kaliningrad';
|
||||
$string['europe/kiev'] = 'Europe/Kiev';
|
||||
$string['europe/kirov'] = 'Europe/Kirov';
|
||||
$string['europe/kyiv'] = 'Europe/Kyiv';
|
||||
$string['europe/lisbon'] = 'Europe/Lisbon';
|
||||
$string['europe/ljubljana'] = 'Europe/Ljubljana';
|
||||
$string['europe/london'] = 'Europe/London';
|
||||
|
||||
@@ -944,8 +944,11 @@ EOF;
|
||||
}
|
||||
|
||||
} else {
|
||||
$errorinfo = $this->get_debug_text($errorinfoboxes[0]->getHtml()) . "\n" .
|
||||
$this->get_debug_text($errorinfoboxes[1]->getHtml());
|
||||
$errorinfo = implode("\n", [
|
||||
$this->get_debug_text($errorinfoboxes[0]->getHtml()),
|
||||
$this->get_debug_text($errorinfoboxes[1]->getHtml()),
|
||||
html_to_text($errorinfoboxes[2]->find('css', 'ul')->getHtml()),
|
||||
]);
|
||||
}
|
||||
|
||||
$msg = "Moodle exception: " . $errormsg->getText() . "\n" . $errorinfo;
|
||||
|
||||
@@ -303,6 +303,18 @@ class iCalendar_component {
|
||||
$component = $this; // use the iCalendar
|
||||
}
|
||||
|
||||
$cleanedparams = [];
|
||||
// Some parameter values are wrapped by DQUOTE character.
|
||||
// We need to go through and get the actual value inside the quoted string.
|
||||
foreach ($params as $param => $value) {
|
||||
if (preg_match('#"(?P<actualvalue>[^"]*?)"#', $value, $matches)) {
|
||||
$cleanedparams[$param] = $matches['actualvalue'];
|
||||
} else {
|
||||
$cleanedparams[$param] = $value;
|
||||
}
|
||||
}
|
||||
$params = $cleanedparams;
|
||||
|
||||
if ($component->add_property($label, $data, $params) === false) {
|
||||
$this->parser_error("Failed to add property '$label' on line $key");
|
||||
}
|
||||
|
||||
@@ -28,3 +28,4 @@ Changelog
|
||||
9/ MDL-60391: replace create_function() with lambda function for PHP 7.2 compatibility (13 Oct 2017)
|
||||
10/ MDL-62914: added handling for TZURL property (13 July 2018)
|
||||
11/ MDL-67029: replace curly by square brackets for string offsets. PHP 7.4 compatibility (25 Oct 2019)
|
||||
12/ MDL-74866: fixed parameter parsing if the value is wrapped by DQUOTE character (28 Jul 2022)
|
||||
|
||||
@@ -55,8 +55,9 @@ class mustache_shorten_text_helper {
|
||||
$length = trim($length);
|
||||
$text = trim($text);
|
||||
|
||||
// Allow mustache tags in the text.
|
||||
// Allow mustache tags in the length and text.
|
||||
$text = $helper->render($text);
|
||||
$length = $helper->render($length);
|
||||
|
||||
return shorten_text($text, $length);
|
||||
}
|
||||
|
||||
+11
-7
@@ -522,22 +522,26 @@ function get_users_listing($sort='lastaccess', $dir='ASC', $page=0, $recordsperp
|
||||
$params = $params + (array)$extraparams;
|
||||
}
|
||||
|
||||
if ($sort) {
|
||||
$sort = " ORDER BY $sort $dir";
|
||||
}
|
||||
|
||||
// If a context is specified, get extra user fields that the current user
|
||||
// is supposed to see, otherwise just get the name fields.
|
||||
$userfields = \core_user\fields::for_name();
|
||||
if ($extracontext) {
|
||||
$userfields->with_identity($extracontext, true);
|
||||
}
|
||||
$userfields->excluding('id', 'username', 'email', 'city', 'country', 'lastaccess', 'confirmed', 'mnethostid');
|
||||
['selects' => $selects, 'joins' => $joins, 'params' => $joinparams] =
|
||||
|
||||
$userfields->excluding('id');
|
||||
$userfields->including('username', 'email', 'city', 'country', 'lastaccess', 'confirmed', 'mnethostid', 'suspended');
|
||||
['selects' => $selects, 'joins' => $joins, 'params' => $joinparams, 'mappings' => $mappings] =
|
||||
(array)$userfields->get_sql('u', true);
|
||||
|
||||
if ($sort) {
|
||||
$orderbymap = $mappings;
|
||||
$orderbymap['default'] = 'lastaccess';
|
||||
$sort = get_safe_orderby($orderbymap, $sort, $dir);
|
||||
}
|
||||
|
||||
// warning: will return UNCONFIRMED USERS
|
||||
return $DB->get_records_sql("SELECT u.id, username, email, city, country, lastaccess, confirmed, mnethostid, suspended $selects
|
||||
return $DB->get_records_sql("SELECT u.id $selects
|
||||
FROM {user} u
|
||||
$joins
|
||||
WHERE $select
|
||||
|
||||
+80
-72
@@ -14,18 +14,26 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core;
|
||||
|
||||
use database_column_info;
|
||||
use moodle_database;
|
||||
use sql_generator;
|
||||
use xmldb_field;
|
||||
use xmldb_index;
|
||||
use xmldb_key;
|
||||
use xmldb_structure;
|
||||
use xmldb_table;
|
||||
|
||||
/**
|
||||
* DDL layer tests.
|
||||
*
|
||||
* @package core_ddl
|
||||
* @category phpunit
|
||||
* @category test
|
||||
* @copyright 2008 Nicolas Connault
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
class core_ddl_testcase extends database_driver_testcase {
|
||||
class ddl_test extends \database_driver_testcase {
|
||||
/** @var xmldb_table[] keys are table name. Created in setUp. */
|
||||
private $tables = array();
|
||||
/** @var array table name => array of stdClass test records loaded into that table. Created in setUp. */
|
||||
@@ -165,7 +173,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
|
||||
try {
|
||||
$result = $DB->get_records('test_table0');
|
||||
} catch (dml_exception $e) {
|
||||
} catch (\dml_exception $e) {
|
||||
$result = false;
|
||||
}
|
||||
$this->resetDebugging();
|
||||
@@ -187,7 +195,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
|
||||
try {
|
||||
$result = $DB->get_records('test_table0');
|
||||
} catch (dml_exception $e) {
|
||||
} catch (\dml_exception $e) {
|
||||
$result = false;
|
||||
}
|
||||
$this->resetDebugging();
|
||||
@@ -274,7 +282,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_exception', $e);
|
||||
}
|
||||
|
||||
@@ -289,7 +297,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_exception', $e);
|
||||
}
|
||||
|
||||
@@ -320,7 +328,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -336,7 +344,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -365,7 +373,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -381,7 +389,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -397,7 +405,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -413,7 +421,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -429,7 +437,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -445,7 +453,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -461,7 +469,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -477,7 +485,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -493,7 +501,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -509,7 +517,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -525,7 +533,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -541,7 +549,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
}
|
||||
@@ -558,7 +566,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
|
||||
$text = str_repeat('š', 1333);
|
||||
|
||||
$data = new stdClass();
|
||||
$data = new \stdClass();
|
||||
$data->name = 'test';
|
||||
$table = new xmldb_table('test_innodb');
|
||||
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
|
||||
@@ -575,7 +583,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
$expected = (array)$data;
|
||||
$expected['id'] = (string)$id;
|
||||
$this->assertEqualsCanonicalizing($expected, (array)$DB->get_record('test_innodb', array('id' => $id)));
|
||||
} catch (dml_exception $e) {
|
||||
} catch (\dml_exception $e) {
|
||||
// Give some nice error message when known problematic MySQL with InnoDB detected.
|
||||
if ($DB->get_dbfamily() === 'mysql') {
|
||||
$engine = strtolower($DB->get_dbengine());
|
||||
@@ -590,7 +598,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
|
||||
$dbman->drop_table($table);
|
||||
|
||||
$data = new stdClass();
|
||||
$data = new \stdClass();
|
||||
$data->name = 'test';
|
||||
$table = new xmldb_table('test_innodb');
|
||||
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
|
||||
@@ -613,7 +621,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
$dbman->drop_table($table);
|
||||
|
||||
// MySQL VARCHAR fields may hit a different 65535 row size limit when creating tables.
|
||||
$data = new stdClass();
|
||||
$data = new \stdClass();
|
||||
$data->name = 'test';
|
||||
$table = new xmldb_table('test_innodb');
|
||||
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
|
||||
@@ -695,7 +703,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->rename_table($sourcetable, $targettable->getName());
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_exception', $e);
|
||||
$this->assertEquals('Table "test_table1" already exists (can not rename table)', $e->getMessage());
|
||||
}
|
||||
@@ -714,7 +722,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->field_exists('nonexistenttable', 'id');
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('moodle_exception', $e);
|
||||
}
|
||||
|
||||
@@ -732,7 +740,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->field_exists($nonexistenttable, $realfield);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('moodle_exception', $e);
|
||||
}
|
||||
|
||||
@@ -751,13 +759,13 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$this->assertFalse($dbman->field_exists($nonexistenttable, 'id'));
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('moodle_exception', $e);
|
||||
}
|
||||
try {
|
||||
$this->assertFalse($dbman->field_exists('nonexistenttable', $realfield));
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('moodle_exception', $e);
|
||||
}
|
||||
// Non existing fields (return false).
|
||||
@@ -783,7 +791,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->add_field($table, $field);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_exception', $e);
|
||||
}
|
||||
|
||||
@@ -793,7 +801,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->add_field($table, $field);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_exception', $e);
|
||||
}
|
||||
|
||||
@@ -948,7 +956,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->drop_field($table, $field);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_dependency_exception', $e);
|
||||
}
|
||||
$this->assertTrue($dbman->field_exists($table, 'type')); // Continues existing, drop aborted.
|
||||
@@ -959,7 +967,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->drop_field($table, $field);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_dependency_exception', $e);
|
||||
}
|
||||
$this->assertTrue($dbman->field_exists($table, $field)); // Continues existing, drop aborted.
|
||||
@@ -970,7 +978,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->drop_field($table, $field);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_field_missing_exception', $e);
|
||||
}
|
||||
|
||||
@@ -1004,7 +1012,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
$table->add_index('onenumber', XMLDB_INDEX_NOTUNIQUE, array('onenumber'));
|
||||
$dbman->create_table($table);
|
||||
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->onenumber = 2;
|
||||
$record->anothernumber = 4;
|
||||
$recoriginal = $DB->insert_record('test_table_cust0', $record);
|
||||
@@ -1015,7 +1023,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->change_field_type($table, $field);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_dependency_exception', $e);
|
||||
}
|
||||
// Column continues being integer 10 not null default 2.
|
||||
@@ -1051,7 +1059,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
// TODO: check the rest of attributes.
|
||||
|
||||
// Insert one string value and try to convert to integer. Must throw exception.
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->onenumber = 7;
|
||||
$record->anothernumber = 'string value';
|
||||
$rectodrop = $DB->insert_record('test_table_cust0', $record);
|
||||
@@ -1060,7 +1068,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->change_field_type($table, $field);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_change_structure_exception', $e);
|
||||
}
|
||||
// Column continues being char 30 not null default "test'n drop" now.
|
||||
@@ -1182,7 +1190,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->change_field_precision($table, $field);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_change_structure_exception', $e);
|
||||
}
|
||||
// No changes in field specs at all.
|
||||
@@ -1216,7 +1224,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
// TODO: check the rest of attributes.
|
||||
|
||||
// Insert one record with 6-digit field.
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->course = 10;
|
||||
$record->secondname = 'third record';
|
||||
$record->intro = 'third record';
|
||||
@@ -1228,7 +1236,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->change_field_precision($table, $field);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_change_structure_exception', $e);
|
||||
}
|
||||
// No changes in field specs at all.
|
||||
@@ -1242,7 +1250,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->change_field_precision($table, $field);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_dependency_exception', $e);
|
||||
}
|
||||
// No changes in field specs at all.
|
||||
@@ -1261,12 +1269,12 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
|
||||
$dbman->create_table($table);
|
||||
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->name = null;
|
||||
|
||||
try {
|
||||
$result = $DB->insert_record('test_table_cust0', $record, false);
|
||||
} catch (dml_exception $e) {
|
||||
} catch (\dml_exception $e) {
|
||||
$result = false;
|
||||
}
|
||||
$this->resetDebugging();
|
||||
@@ -1287,7 +1295,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
|
||||
try {
|
||||
$result = $DB->insert_record('test_table_cust0', $record, false);
|
||||
} catch (dml_exception $e) {
|
||||
} catch (\dml_exception $e) {
|
||||
$result = false;
|
||||
}
|
||||
$this->resetDebugging();
|
||||
@@ -1311,7 +1319,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
$field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'Moodle2');
|
||||
$dbman->change_field_default($table, $field);
|
||||
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->onenumber = 666;
|
||||
$id = $DB->insert_record('test_table_cust0', $record);
|
||||
|
||||
@@ -1322,7 +1330,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
$field->set_attributes(XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 666);
|
||||
$dbman->change_field_default($table, $field);
|
||||
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->name = 'something';
|
||||
$id = $DB->insert_record('test_table_cust0', $record);
|
||||
|
||||
@@ -1343,7 +1351,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
|
||||
$dbman->create_table($table);
|
||||
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->onenumber = 666;
|
||||
$record->name = 'something';
|
||||
$DB->insert_record('test_table_cust0', $record, false);
|
||||
@@ -1354,7 +1362,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
|
||||
try {
|
||||
$result = $DB->insert_record('test_table_cust0', $record, false);
|
||||
} catch (dml_exception $e) {
|
||||
} catch (\dml_exception $e) {
|
||||
$result = false;
|
||||
}
|
||||
$this->resetDebugging();
|
||||
@@ -1375,7 +1383,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->add_index($table, $index);
|
||||
$this->fail('Exception expected for duplicate indexes');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_exception', $e);
|
||||
}
|
||||
|
||||
@@ -1384,7 +1392,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->add_index($table, $index);
|
||||
$this->fail('Exception expected for duplicate indexes');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_exception', $e);
|
||||
}
|
||||
|
||||
@@ -1398,7 +1406,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$table->add_index('onenumber', XMLDB_INDEX_NOTUNIQUE, array('onenumber'));
|
||||
$this->fail('Coding exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -1412,7 +1420,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$table->add_key('onenumber', XMLDB_KEY_FOREIGN, array('onenumber'));
|
||||
$this->fail('Coding exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -1614,7 +1622,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->delete_tables_from_xmldb_file('fpsoiudfposui');
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->resetDebugging();
|
||||
$this->assertInstanceOf('moodle_exception', $e);
|
||||
}
|
||||
@@ -1622,7 +1630,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->delete_tables_from_xmldb_file(__DIR__ . '/fixtures/invalid.xml');
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->resetDebugging();
|
||||
$this->assertInstanceOf('moodle_exception', $e);
|
||||
}
|
||||
@@ -1645,7 +1653,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->install_from_xmldb_file('fpsoiudfposui');
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->resetDebugging();
|
||||
$this->assertInstanceOf('moodle_exception', $e);
|
||||
}
|
||||
@@ -1653,7 +1661,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->install_from_xmldb_file(__DIR__ . '/fixtures/invalid.xml');
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->resetDebugging();
|
||||
$this->assertInstanceOf('moodle_exception', $e);
|
||||
}
|
||||
@@ -1680,7 +1688,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_temp_table($dupetable);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_exception', $e);
|
||||
}
|
||||
|
||||
@@ -1689,7 +1697,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($dupetable);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_exception', $e);
|
||||
}
|
||||
|
||||
@@ -1726,7 +1734,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->drop_table($noetable);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('ddl_table_missing_exception', $e);
|
||||
}
|
||||
|
||||
@@ -1875,7 +1883,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
|
||||
$dbman->drop_table($table0);
|
||||
|
||||
$rc = new ReflectionClass('moodle_database');
|
||||
$rc = new \ReflectionClass('moodle_database');
|
||||
$rcm = $rc->getMethod('get_temp_tables_cache');
|
||||
$rcm->setAccessible(true);
|
||||
$metacachetemp = $rcm->invokeArgs($DB, []);
|
||||
@@ -1886,7 +1894,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
// Data of test_table1 should be intact.
|
||||
$this->assertEquals(true, $metacachetemp->has('test_table1'));
|
||||
|
||||
$rc = new ReflectionClass('moodle_database');
|
||||
$rc = new \ReflectionClass('moodle_database');
|
||||
$rcm = $rc->getMethod('get_metacache');
|
||||
$rcm->setAccessible(true);
|
||||
$metacache = $rcm->invokeArgs($DB, []);
|
||||
@@ -2009,7 +2017,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
$tablename = $table->getName();
|
||||
$this->tables[$tablename] = $table;
|
||||
|
||||
$rec = new stdClass();
|
||||
$rec = new \stdClass();
|
||||
$rec->name = $maxstr;
|
||||
|
||||
$id = $DB->insert_record($tablename, $rec);
|
||||
@@ -2029,7 +2037,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
}
|
||||
@@ -2062,7 +2070,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
$tablename = $table->getName();
|
||||
$this->tables[$tablename] = $table;
|
||||
|
||||
$rec = new stdClass();
|
||||
$rec = new \stdClass();
|
||||
$rec->name1 = $maxstr;
|
||||
$rec->name2 = $reststr;
|
||||
|
||||
@@ -2088,7 +2096,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
}
|
||||
@@ -2116,7 +2124,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
$maxstr .= 'a'; // Ascii only.
|
||||
}
|
||||
|
||||
$rec = new stdClass();
|
||||
$rec = new \stdClass();
|
||||
$rec->name = $maxstr;
|
||||
|
||||
$id = $DB->insert_record($tablename, $rec);
|
||||
@@ -2131,7 +2139,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
$maxstr .= '言'; // Random long string that should fix exactly the limit for one char column.
|
||||
}
|
||||
|
||||
$rec = new stdClass();
|
||||
$rec = new \stdClass();
|
||||
$rec->name = $maxstr;
|
||||
|
||||
try {
|
||||
@@ -2163,7 +2171,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,12 +415,12 @@ abstract class moodle_database {
|
||||
/**
|
||||
* This should be called before each db query.
|
||||
* @param string $sql The query string.
|
||||
* @param array $params An array of parameters.
|
||||
* @param array|null $params An array of parameters.
|
||||
* @param int $type The type of query. ( SQL_QUERY_SELECT | SQL_QUERY_AUX | SQL_QUERY_INSERT | SQL_QUERY_UPDATE | SQL_QUERY_STRUCTURE )
|
||||
* @param mixed $extrainfo This is here for any driver specific extra information.
|
||||
* @return void
|
||||
*/
|
||||
protected function query_start($sql, array $params=null, $type, $extrainfo=null) {
|
||||
protected function query_start($sql, ?array $params, $type, $extrainfo=null) {
|
||||
if ($this->loggingquery) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -264,16 +264,35 @@ trait moodle_read_slave_trait {
|
||||
/**
|
||||
* Called before each db query.
|
||||
* @param string $sql
|
||||
* @param array $params array of parameters
|
||||
* @param array|null $params An array of parameters.
|
||||
* @param int $type type of query
|
||||
* @param mixed $extrainfo driver specific extra information
|
||||
* @return void
|
||||
*/
|
||||
protected function query_start($sql, array $params = null, $type, $extrainfo = null) {
|
||||
protected function query_start($sql, ?array $params, $type, $extrainfo = null) {
|
||||
parent::query_start($sql, $params, $type, $extrainfo);
|
||||
$this->select_db_handle($type, $sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be called immediately after each db query. It does a clean up of resources.
|
||||
*
|
||||
* @param mixed $result The db specific result obtained from running a query.
|
||||
* @return void
|
||||
*/
|
||||
protected function query_end($result) {
|
||||
if ($this->written) {
|
||||
// Adjust the written time.
|
||||
array_walk($this->written, function (&$val) {
|
||||
if ($val === true) {
|
||||
$val = microtime(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parent::query_end($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select appropriate db handle - readwrite or readonly
|
||||
* @param int $type type of query
|
||||
@@ -339,10 +358,8 @@ trait moodle_read_slave_trait {
|
||||
return true;
|
||||
case SQL_QUERY_INSERT:
|
||||
case SQL_QUERY_UPDATE:
|
||||
// If we are in transaction we cannot set the written time yet.
|
||||
$now = $this->slavelatency && !$this->transactions ? microtime(true) : true;
|
||||
foreach ($this->table_names($sql) as $tablename) {
|
||||
$this->written[$tablename] = $now;
|
||||
$this->written[$tablename] = true;
|
||||
}
|
||||
return false;
|
||||
case SQL_QUERY_STRUCTURE:
|
||||
@@ -370,16 +387,10 @@ trait moodle_read_slave_trait {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->slavelatency) {
|
||||
return;
|
||||
}
|
||||
|
||||
$now = null;
|
||||
foreach ($this->written as $tablename => $when) {
|
||||
if ($when === true) {
|
||||
$now = $now ?: microtime(true);
|
||||
$this->written[$tablename] = $now;
|
||||
}
|
||||
$now = $now ?: microtime(true);
|
||||
$this->written[$tablename] = $now;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -245,12 +245,12 @@ class oci_native_moodle_database extends moodle_database {
|
||||
/**
|
||||
* Called before each db query.
|
||||
* @param string $sql
|
||||
* @param array array of parameters
|
||||
* @param array|null $params An array of parameters.
|
||||
* @param int $type type of query
|
||||
* @param mixed $extrainfo driver specific extra information
|
||||
* @return void
|
||||
*/
|
||||
protected function query_start($sql, array $params=null, $type, $extrainfo=null) {
|
||||
protected function query_start($sql, ?array $params, $type, $extrainfo=null) {
|
||||
parent::query_start($sql, $params, $type, $extrainfo);
|
||||
// oci driver tents to send debug to output, we do not need that ;-)
|
||||
$this->last_error_reporting = error_reporting(0);
|
||||
|
||||
@@ -614,12 +614,12 @@ abstract class pdo_moodle_database extends moodle_database {
|
||||
* Overridden to ensure $this->lastErorr is reset each query
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array array of parameters
|
||||
* @param array|null $params An array of parameters.
|
||||
* @param int $type type of query
|
||||
* @param mixed $extrainfo driver specific extra information
|
||||
* @return void
|
||||
*/
|
||||
protected function query_start($sql, array $params=null, $type, $extrainfo=null) {
|
||||
protected function query_start($sql, ?array $params, $type, $extrainfo=null) {
|
||||
$this->lastError = null;
|
||||
parent::query_start($sql, $params, $type, $extrainfo);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ class pgsql_native_moodle_database extends moodle_database {
|
||||
select_db_handle as read_slave_select_db_handle;
|
||||
can_use_readonly as read_slave_can_use_readonly;
|
||||
query_start as read_slave_query_start;
|
||||
query_end as read_slave_query_end;
|
||||
}
|
||||
|
||||
/** @var array $dbhcursor keep track of open cursors */
|
||||
@@ -184,12 +185,20 @@ class pgsql_native_moodle_database extends moodle_database {
|
||||
}
|
||||
|
||||
ob_start();
|
||||
if (empty($this->dboptions['dbpersist'])) {
|
||||
$this->pgsql = pg_connect($connection, PGSQL_CONNECT_FORCE_NEW);
|
||||
} else {
|
||||
$this->pgsql = pg_pconnect($connection, PGSQL_CONNECT_FORCE_NEW);
|
||||
// It seems that pg_connect() handles some errors differently.
|
||||
// For example, name resolution error will raise an exception, and non-existing
|
||||
// database or wrong credentials will just return false.
|
||||
// We need to cater for both.
|
||||
try {
|
||||
if (empty($this->dboptions['dbpersist'])) {
|
||||
$this->pgsql = pg_connect($connection, PGSQL_CONNECT_FORCE_NEW);
|
||||
} else {
|
||||
$this->pgsql = pg_pconnect($connection, PGSQL_CONNECT_FORCE_NEW);
|
||||
}
|
||||
$dberr = ob_get_contents();
|
||||
} catch (\Exception $e) {
|
||||
$dberr = $e->getMessage();
|
||||
}
|
||||
$dberr = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
$status = $this->pgsql ? pg_connection_status($this->pgsql) : false;
|
||||
@@ -306,12 +315,12 @@ class pgsql_native_moodle_database extends moodle_database {
|
||||
/**
|
||||
* Called before each db query.
|
||||
* @param string $sql
|
||||
* @param array array of parameters
|
||||
* @param array|null $params An array of parameters.
|
||||
* @param int $type type of query
|
||||
* @param mixed $extrainfo driver specific extra information
|
||||
* @return void
|
||||
*/
|
||||
protected function query_start($sql, array $params=null, $type, $extrainfo=null) {
|
||||
protected function query_start($sql, ?array $params, $type, $extrainfo=null) {
|
||||
$this->read_slave_query_start($sql, $params, $type, $extrainfo);
|
||||
// pgsql driver tends to send debug to output, we do not need that.
|
||||
$this->last_error_reporting = error_reporting(0);
|
||||
@@ -326,7 +335,7 @@ class pgsql_native_moodle_database extends moodle_database {
|
||||
// reset original debug level
|
||||
error_reporting($this->last_error_reporting);
|
||||
try {
|
||||
parent::query_end($result);
|
||||
$this->read_slave_query_end($result);
|
||||
if ($this->savepointpresent and $this->last_type != SQL_QUERY_AUX and $this->last_type != SQL_QUERY_SELECT) {
|
||||
$res = @pg_query($this->pgsql, "RELEASE SAVEPOINT moodle_pg_savepoint; SAVEPOINT moodle_pg_savepoint");
|
||||
if ($res) {
|
||||
|
||||
@@ -306,12 +306,12 @@ class sqlsrv_native_moodle_database extends moodle_database {
|
||||
/**
|
||||
* Called before each db query.
|
||||
* @param string $sql
|
||||
* @param array $params array of parameters
|
||||
* @param array|null $params An array of parameters.
|
||||
* @param int $type type of query
|
||||
* @param mixed $extrainfo driver specific extra information
|
||||
* @return void
|
||||
*/
|
||||
protected function query_start($sql, array $params = null, $type, $extrainfo = null) {
|
||||
protected function query_start($sql, ?array $params, $type, $extrainfo = null) {
|
||||
parent::query_start($sql, $params, $type, $extrainfo);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,9 @@ require_once(__DIR__.'/fixtures/read_slave_moodle_database_mock_mysqli.php');
|
||||
* @category dml
|
||||
* @copyright 2018 Catalyst IT
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \mysqli_native_moodle_database
|
||||
*/
|
||||
class core_dml_mysqli_read_slave_testcase extends base_testcase {
|
||||
class dml_mysqli_read_slave_test extends base_testcase {
|
||||
/**
|
||||
* Test readonly handle is not used for reading from special pg_*() call queries,
|
||||
* pg_try_advisory_lock and pg_advisory_unlock.
|
||||
|
||||
@@ -34,8 +34,9 @@ require_once(__DIR__.'/fixtures/read_slave_moodle_database_mock_pgsql.php');
|
||||
* @category dml
|
||||
* @copyright 2018 Catalyst IT
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \pgsql_native_moodle_database
|
||||
*/
|
||||
class core_dml_pgsql_read_slave_testcase extends base_testcase {
|
||||
class dml_pgsql_read_slave_test extends base_testcase {
|
||||
/**
|
||||
* Test correct database handles are used for cursors
|
||||
*
|
||||
|
||||
@@ -35,8 +35,9 @@ require_once(__DIR__.'/fixtures/read_slave_moodle_database_special.php');
|
||||
* @category dml
|
||||
* @copyright 2018 Catalyst IT
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \moodle_read_slave_trait
|
||||
*/
|
||||
class core_dml_read_slave_testcase extends base_testcase {
|
||||
class dml_read_slave_test extends base_testcase {
|
||||
|
||||
/** @var float */
|
||||
static private $dbreadonlylatency = 0.8;
|
||||
@@ -311,6 +312,50 @@ class core_dml_read_slave_testcase extends base_testcase {
|
||||
$this->assert_readonly_handle($handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test readonly handle is not used immediately after update
|
||||
* Test last written time is adjusted post-write,
|
||||
* so the latency parameter is applied properly.
|
||||
*
|
||||
* @return void
|
||||
* @covers ::can_use_readonly
|
||||
* @covers ::query_end
|
||||
*/
|
||||
public function test_long_update(): void {
|
||||
$DB = $this->new_db(true);
|
||||
|
||||
$this->assertNull($DB->get_dbhwrite());
|
||||
|
||||
$skip = false;
|
||||
|
||||
list($sql, $params, $ptype) = $DB->fix_sql_params("UPDATE {table} SET a = 1 WHERE id = 1");
|
||||
$DB->with_query_start_end($sql, $params, SQL_QUERY_UPDATE, function ($dbh) use (&$now) {
|
||||
sleep(1);
|
||||
$now = microtime(true);
|
||||
});
|
||||
|
||||
// This condition should always evaluate true, however we need to
|
||||
// safeguard from an unaccounted delay that can break this test.
|
||||
if (microtime(true) - $now < self::$dbreadonlylatency) {
|
||||
// Not enough time passed, use rw handle.
|
||||
$handle = $DB->get_records_sql("SELECT * FROM {table}");
|
||||
$this->assertEquals('test_rw::test:test', $handle);
|
||||
|
||||
// Make sure enough time passes.
|
||||
sleep(1);
|
||||
} else {
|
||||
$skip = true;
|
||||
}
|
||||
|
||||
// Exceeded latency time, use ro handle.
|
||||
$handle = $DB->get_records_sql("SELECT * FROM {table}");
|
||||
$this->assert_readonly_handle($handle);
|
||||
|
||||
if ($skip) {
|
||||
$this->markTestSkipped("Delay too long to test write handle immediately after transaction");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test failed readonly connection falls back to write connection.
|
||||
*
|
||||
|
||||
@@ -14,27 +14,21 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* DML Table tests.
|
||||
*
|
||||
* @package core_dml
|
||||
* @category phpunit
|
||||
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use \core\dml\table;
|
||||
use core\dml\table;
|
||||
use xmldb_table;
|
||||
|
||||
/**
|
||||
* DML Table tests.
|
||||
*
|
||||
* @package core
|
||||
* @category test
|
||||
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \core\dml\table
|
||||
*/
|
||||
class core_dml_table_testcase extends database_driver_testcase {
|
||||
class dml_table_test extends \database_driver_testcase {
|
||||
|
||||
/**
|
||||
* Data provider for various \core\dml\table method tests.
|
||||
@@ -162,15 +156,15 @@ class core_dml_table_testcase extends database_driver_testcase {
|
||||
* @param array $fieldlist The list of fields
|
||||
* @param string $primarykey The name of the primary key
|
||||
* @param string $fieldprefix The prefix to use for each field
|
||||
* @param stdClass $result The result of the get_records_sql
|
||||
* @param stdClass $expected The expected output
|
||||
* @param \stdClass $result The result of the get_records_sql
|
||||
* @param \stdClass $expected The expected output
|
||||
*/
|
||||
public function test_extract_fields_from_result(
|
||||
array $fieldlist,
|
||||
string $primarykey,
|
||||
string $fieldprefix,
|
||||
stdClass $result,
|
||||
stdClass $expected
|
||||
\stdClass $result,
|
||||
\stdClass $expected
|
||||
) {
|
||||
$dbman = $this->tdb->get_manager();
|
||||
|
||||
|
||||
+145
-127
@@ -17,15 +17,33 @@
|
||||
/**
|
||||
* DML layer tests.
|
||||
*
|
||||
* @package core_dml
|
||||
* @category phpunit
|
||||
* @package core
|
||||
* @category test
|
||||
* @copyright 2008 Nicolas Connault
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core;
|
||||
|
||||
use dml_exception;
|
||||
use dml_missing_record_exception;
|
||||
use dml_multiple_records_exception;
|
||||
use moodle_database;
|
||||
use moodle_transaction;
|
||||
use xmldb_key;
|
||||
use xmldb_table;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
class core_dml_testcase extends database_driver_testcase {
|
||||
/**
|
||||
* DML layer tests.
|
||||
*
|
||||
* @package core
|
||||
* @category test
|
||||
* @copyright 2008 Nicolas Connault
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class dml_test extends \database_driver_testcase {
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
@@ -212,7 +230,7 @@ class core_dml_testcase extends database_driver_testcase {
|
||||
try {
|
||||
list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_DOLLAR, 'param', false);
|
||||
$this->fail('An Exception is missing, expected due to not supported SQL_PARAMS_DOLLAR');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
$this->assertSame('typenotimplement', $e->errorcode);
|
||||
}
|
||||
@@ -222,7 +240,7 @@ class core_dml_testcase extends database_driver_testcase {
|
||||
try {
|
||||
list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
|
||||
$this->fail('An Exception is missing, expected due to empty array of items');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -354,7 +372,7 @@ class core_dml_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$DB->fix_sql_params($sql, $params);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
}
|
||||
|
||||
@@ -364,7 +382,7 @@ class core_dml_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$DB->fix_sql_params($sql, $params);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
}
|
||||
|
||||
@@ -374,7 +392,7 @@ class core_dml_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$DB->fix_sql_params($sql, $params);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
}
|
||||
|
||||
@@ -391,7 +409,7 @@ class core_dml_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$DB->fix_sql_params($sql, $params);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
}
|
||||
|
||||
@@ -402,7 +420,7 @@ class core_dml_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$DB->fix_sql_params($sql, $params);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
}
|
||||
|
||||
@@ -419,7 +437,7 @@ class core_dml_testcase extends database_driver_testcase {
|
||||
try {
|
||||
$DB->fix_sql_params($sql, $params);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -459,7 +477,7 @@ class core_dml_testcase extends database_driver_testcase {
|
||||
$DB = $this->tdb;
|
||||
|
||||
require_once($CFG->dirroot . '/lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php');
|
||||
$fixture = new test_dml_sql_debugging_fixture($this);
|
||||
$fixture = new \test_dml_sql_debugging_fixture($this);
|
||||
|
||||
$sql = "SELECT * FROM {users}";
|
||||
|
||||
@@ -493,7 +511,7 @@ SELECT * FROM {users}
|
||||
-- line 74 of /lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to test_dml_sql_debugging_fixture->one()
|
||||
-- line 83 of /lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to test_dml_sql_debugging_fixture->two()
|
||||
-- line 92 of /lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to test_dml_sql_debugging_fixture->three()
|
||||
-- line 489 of /lib/dml/tests/dml_test.php: call to test_dml_sql_debugging_fixture->four()
|
||||
-- line 507 of /lib/dml/tests/dml_test.php: call to test_dml_sql_debugging_fixture->four()
|
||||
EOD;
|
||||
$this->assertEquals($this->unix_to_os_dirsep($expected), $out);
|
||||
|
||||
@@ -549,7 +567,7 @@ EOD;
|
||||
$this->assertTrue($dbman->table_exists($tablename));
|
||||
|
||||
// Test insert record.
|
||||
$rec1 = new stdClass();
|
||||
$rec1 = new \stdClass();
|
||||
$rec1->long_int_columnname_with_28c = 28;
|
||||
$rec1->long_dec_columnname_with_28c = 28.28;
|
||||
$rec1->long_str_columnname_with_28c = '28';
|
||||
@@ -1007,7 +1025,7 @@ EOD;
|
||||
try {
|
||||
$DB->execute($sql);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
}
|
||||
|
||||
@@ -1138,7 +1156,7 @@ EOD;
|
||||
try {
|
||||
$rs = $DB->get_recordset($tablename, $conditions);
|
||||
$this->fail('An Exception is missing, expected due to equating of text fields');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
$this->assertSame('textconditionsnotallowed', $e->errorcode);
|
||||
}
|
||||
@@ -1546,7 +1564,7 @@ EOD;
|
||||
// Only in debug mode - hopefully all devs test code in debug mode...
|
||||
$this->fail('An Exception is missing, expected due to equating of text fields');
|
||||
}
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
$this->assertSame('textconditionsnotallowed', $e->errorcode);
|
||||
}
|
||||
@@ -1556,7 +1574,7 @@ EOD;
|
||||
try {
|
||||
$records = $DB->get_records('xxxx', array('id' => 0));
|
||||
$this->fail('An Exception is missing, expected due to query against non-existing table');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
if (debugging()) {
|
||||
// Information for developers only, normal users get general error message.
|
||||
@@ -1567,7 +1585,7 @@ EOD;
|
||||
try {
|
||||
$records = $DB->get_records('xxxx', array('id' => '1'));
|
||||
$this->fail('An Exception is missing, expected due to query against non-existing table');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
if (debugging()) {
|
||||
// Information for developers only, normal users get general error message.
|
||||
@@ -1579,7 +1597,7 @@ EOD;
|
||||
try {
|
||||
$records = $DB->get_records($tablename, array('xxxx' => 0));
|
||||
$this->fail('An Exception is missing, expected due to query against non-existing column');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
if (debugging()) {
|
||||
// Information for developers only, normal users get general error message.
|
||||
@@ -1847,7 +1865,7 @@ EOD;
|
||||
$DB->insert_record($tablename, array('course' => 2));
|
||||
|
||||
$record = $DB->get_record($tablename, array('id' => 2));
|
||||
$this->assertInstanceOf('stdClass', $record);
|
||||
$this->assertInstanceOf(\stdClass::class, $record);
|
||||
|
||||
$this->assertEquals(2, $record->course);
|
||||
$this->assertEquals(2, $record->id);
|
||||
@@ -1870,7 +1888,7 @@ EOD;
|
||||
$DB->insert_record($tablename, array('course' => 2));
|
||||
|
||||
$record = $DB->get_record_select($tablename, "id = ?", array(2));
|
||||
$this->assertInstanceOf('stdClass', $record);
|
||||
$this->assertInstanceOf(\stdClass::class, $record);
|
||||
|
||||
$this->assertEquals(2, $record->course);
|
||||
|
||||
@@ -1894,7 +1912,7 @@ EOD;
|
||||
|
||||
// Standard use.
|
||||
$record = $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(2));
|
||||
$this->assertInstanceOf('stdClass', $record);
|
||||
$this->assertInstanceOf(\stdClass::class, $record);
|
||||
$this->assertEquals(2, $record->course);
|
||||
$this->assertEquals(2, $record->id);
|
||||
|
||||
@@ -1975,7 +1993,7 @@ EOD;
|
||||
// Only in debug mode - hopefully all devs test code in debug mode...
|
||||
$this->fail('An Exception is missing, expected due to equating of text fields');
|
||||
}
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
$this->assertSame('textconditionsnotallowed', $e->errorcode);
|
||||
}
|
||||
@@ -2098,7 +2116,7 @@ EOD;
|
||||
$this->assertEquals($record, $before);
|
||||
|
||||
$record = $DB->get_record($tablename, array('course' => 1));
|
||||
$this->assertInstanceOf('stdClass', $record);
|
||||
$this->assertInstanceOf(\stdClass::class, $record);
|
||||
$this->assertSame('xx', $record->onechar);
|
||||
|
||||
$result = $DB->insert_record_raw($tablename, array('course' => 2, 'onechar' => 'yy'), false);
|
||||
@@ -2107,21 +2125,21 @@ EOD;
|
||||
// Note: bulk not implemented yet.
|
||||
$DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'zz'), true, true);
|
||||
$record = $DB->get_record($tablename, array('course' => 3));
|
||||
$this->assertInstanceOf('stdClass', $record);
|
||||
$this->assertInstanceOf(\stdClass::class, $record);
|
||||
$this->assertSame('zz', $record->onechar);
|
||||
|
||||
// Custom sequence (id) - returnid is ignored.
|
||||
$result = $DB->insert_record_raw($tablename, array('id' => 10, 'course' => 3, 'onechar' => 'bb'), true, false, true);
|
||||
$this->assertTrue($result);
|
||||
$record = $DB->get_record($tablename, array('id' => 10));
|
||||
$this->assertInstanceOf('stdClass', $record);
|
||||
$this->assertInstanceOf(\stdClass::class, $record);
|
||||
$this->assertSame('bb', $record->onechar);
|
||||
|
||||
// Custom sequence - missing id error.
|
||||
try {
|
||||
$DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'bb'), true, false, true);
|
||||
$this->fail('Exception expected due to missing record');
|
||||
} catch (coding_exception $ex) {
|
||||
} catch (\coding_exception $ex) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
@@ -2184,7 +2202,7 @@ EOD;
|
||||
$this->assertEquals(99, $record->course);
|
||||
|
||||
// Check nulls are set properly for all types.
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->oneint = null;
|
||||
$record->onenum = null;
|
||||
$record->onechar = null;
|
||||
@@ -2200,7 +2218,7 @@ EOD;
|
||||
$this->assertNull($record->onebinary);
|
||||
|
||||
// Check zeros are set properly for all types.
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->oneint = 0;
|
||||
$record->onenum = 0;
|
||||
$recid = $DB->insert_record($tablename, $record);
|
||||
@@ -2209,7 +2227,7 @@ EOD;
|
||||
$this->assertEquals(0, $record->onenum);
|
||||
|
||||
// Check booleans are set properly for all types.
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->oneint = true; // Trues.
|
||||
$record->onenum = true;
|
||||
$record->onechar = true;
|
||||
@@ -2221,7 +2239,7 @@ EOD;
|
||||
$this->assertEquals(1, $record->onechar);
|
||||
$this->assertEquals(1, $record->onetext);
|
||||
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->oneint = false; // Falses.
|
||||
$record->onenum = false;
|
||||
$record->onechar = false;
|
||||
@@ -2234,34 +2252,34 @@ EOD;
|
||||
$this->assertEquals(0, $record->onetext);
|
||||
|
||||
// Check string data causes exception in numeric types.
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->oneint = 'onestring';
|
||||
$record->onenum = 0;
|
||||
try {
|
||||
$DB->insert_record($tablename, $record);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
}
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->oneint = 0;
|
||||
$record->onenum = 'onestring';
|
||||
try {
|
||||
$DB->insert_record($tablename, $record);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
}
|
||||
|
||||
// Check empty string data is stored as 0 in numeric datatypes.
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->oneint = ''; // Empty string.
|
||||
$record->onenum = 0;
|
||||
$recid = $DB->insert_record($tablename, $record);
|
||||
$record = $DB->get_record($tablename, array('id' => $recid));
|
||||
$this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
|
||||
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->oneint = 0;
|
||||
$record->onenum = ''; // Empty string.
|
||||
$recid = $DB->insert_record($tablename, $record);
|
||||
@@ -2269,7 +2287,7 @@ EOD;
|
||||
$this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
|
||||
|
||||
// Check empty strings are set properly in string types.
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->oneint = 0;
|
||||
$record->onenum = 0;
|
||||
$record->onechar = '';
|
||||
@@ -2280,7 +2298,7 @@ EOD;
|
||||
$this->assertTrue($record->onetext === '');
|
||||
|
||||
// Check operation ((210.10 + 39.92) - 150.02) against numeric types.
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->oneint = ((210.10 + 39.92) - 150.02);
|
||||
$record->onenum = ((210.10 + 39.92) - 150.02);
|
||||
$recid = $DB->insert_record($tablename, $record);
|
||||
@@ -2295,7 +2313,7 @@ EOD;
|
||||
'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
|
||||
'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
|
||||
foreach ($teststrings as $teststring) {
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->onechar = $teststring;
|
||||
$record->onetext = $teststring;
|
||||
$recid = $DB->insert_record($tablename, $record);
|
||||
@@ -2307,7 +2325,7 @@ EOD;
|
||||
// Check LOBs in text/binary columns.
|
||||
$clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
|
||||
$blob = file_get_contents(__DIR__ . '/fixtures/randombinary');
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->onetext = $clob;
|
||||
$record->onebinary = $blob;
|
||||
$recid = $DB->insert_record($tablename, $record);
|
||||
@@ -2320,7 +2338,7 @@ EOD;
|
||||
// And "small" LOBs too, just in case.
|
||||
$newclob = substr($clob, 0, 500);
|
||||
$newblob = substr($blob, 0, 250);
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->onetext = $newclob;
|
||||
$record->onebinary = $newblob;
|
||||
$recid = $DB->insert_record($tablename, $record);
|
||||
@@ -2334,7 +2352,7 @@ EOD;
|
||||
// And "diagnostic" LOBs too, just in case.
|
||||
$newclob = '\'"\\;/ěščřžýáíé';
|
||||
$newblob = '\'"\\;/ěščřžýáíé';
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->onetext = $newclob;
|
||||
$record->onebinary = $newblob;
|
||||
$recid = $DB->insert_record($tablename, $record);
|
||||
@@ -2346,7 +2364,7 @@ EOD;
|
||||
$this->assertEquals(false, $rs->key()); // Ensure recordset key() method to be working ok after closing.
|
||||
|
||||
// Test data is not modified.
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->id = -1; // Has to be ignored.
|
||||
$record->course = 3;
|
||||
$record->lalala = 'lalal'; // Unused.
|
||||
@@ -2409,7 +2427,7 @@ EOD;
|
||||
try {
|
||||
$DB->insert_record($tablename, $record, false);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
}
|
||||
|
||||
@@ -2417,7 +2435,7 @@ EOD;
|
||||
try {
|
||||
$DB->insert_record($tablename, $record, true);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
}
|
||||
|
||||
@@ -2425,7 +2443,7 @@ EOD;
|
||||
try {
|
||||
$DB->insert_record('nonexistenttable', $record, true);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
$this->assertTrue($e instanceof dml_exception);
|
||||
}
|
||||
}
|
||||
@@ -2448,7 +2466,7 @@ EOD;
|
||||
|
||||
$this->assertCount(0, $DB->get_records($tablename));
|
||||
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->id = '1';
|
||||
$record->course = '1';
|
||||
$record->oneint = null;
|
||||
@@ -2506,7 +2524,7 @@ EOD;
|
||||
try {
|
||||
$DB->insert_records($tablename, $records);
|
||||
$this->fail('coding_exception expected when insert_records receives different object data structures');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -2517,7 +2535,7 @@ EOD;
|
||||
try {
|
||||
$DB->insert_records($tablename, $records);
|
||||
$this->fail('coding_exception expected when insert_records receives different object data structures');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -2525,7 +2543,7 @@ EOD;
|
||||
try {
|
||||
$DB->insert_records($tablename, $records);
|
||||
$this->fail('coding_exception expected when insert_records receives non-traversable data');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -2533,7 +2551,7 @@ EOD;
|
||||
try {
|
||||
$DB->insert_records($tablename, $records);
|
||||
$this->fail('coding_exception expected when insert_records receives non-objet record');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
}
|
||||
@@ -2564,7 +2582,7 @@ EOD;
|
||||
try {
|
||||
$DB->insert_record($tablename, (object) ['notnull1' => 1, 'nullable1' => 1, 'nullable2' => 1]);
|
||||
$this->fail('dml_write_exception expected when a record violates a unique index');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_write_exception', $e);
|
||||
}
|
||||
|
||||
@@ -2638,7 +2656,7 @@ EOD;
|
||||
$this->assertEquals(2, $records[13]->course);
|
||||
|
||||
// Check nulls are set properly for all types.
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->id = 20;
|
||||
$record->oneint = null;
|
||||
$record->onenum = null;
|
||||
@@ -2655,7 +2673,7 @@ EOD;
|
||||
$this->assertNull($record->onebinary);
|
||||
|
||||
// Check zeros are set properly for all types.
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->id = 23;
|
||||
$record->oneint = 0;
|
||||
$record->onenum = 0;
|
||||
@@ -2665,29 +2683,29 @@ EOD;
|
||||
$this->assertEquals(0, $record->onenum);
|
||||
|
||||
// Check string data causes exception in numeric types.
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->id = 32;
|
||||
$record->oneint = 'onestring';
|
||||
$record->onenum = 0;
|
||||
try {
|
||||
$DB->import_record($tablename, $record);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
}
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->id = 35;
|
||||
$record->oneint = 0;
|
||||
$record->onenum = 'onestring';
|
||||
try {
|
||||
$DB->import_record($tablename, $record);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
}
|
||||
|
||||
// Check empty strings are set properly in string types.
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->id = 44;
|
||||
$record->oneint = 0;
|
||||
$record->onenum = 0;
|
||||
@@ -2699,7 +2717,7 @@ EOD;
|
||||
$this->assertTrue($record->onetext === '');
|
||||
|
||||
// Check operation ((210.10 + 39.92) - 150.02) against numeric types.
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->id = 47;
|
||||
$record->oneint = ((210.10 + 39.92) - 150.02);
|
||||
$record->onenum = ((210.10 + 39.92) - 150.02);
|
||||
@@ -2716,7 +2734,7 @@ EOD;
|
||||
'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
|
||||
'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
|
||||
foreach ($teststrings as $teststring) {
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->id = $i;
|
||||
$record->onechar = $teststring;
|
||||
$record->onetext = $teststring;
|
||||
@@ -2729,7 +2747,7 @@ EOD;
|
||||
|
||||
// Check LOBs in text/binary columns.
|
||||
$clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->id = 70;
|
||||
$record->onetext = $clob;
|
||||
$record->onebinary = '';
|
||||
@@ -2740,7 +2758,7 @@ EOD;
|
||||
$this->assertEquals($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
|
||||
|
||||
$blob = file_get_contents(__DIR__ . '/fixtures/randombinary');
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->id = 71;
|
||||
$record->onetext = '';
|
||||
$record->onebinary = $blob;
|
||||
@@ -2753,7 +2771,7 @@ EOD;
|
||||
// And "small" LOBs too, just in case.
|
||||
$newclob = substr($clob, 0, 500);
|
||||
$newblob = substr($blob, 0, 250);
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->id = 73;
|
||||
$record->onetext = $newclob;
|
||||
$record->onebinary = $newblob;
|
||||
@@ -2793,7 +2811,7 @@ EOD;
|
||||
try {
|
||||
$DB->update_record_raw($tablename, $record);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('moodle_exception', $e);
|
||||
}
|
||||
|
||||
@@ -2802,7 +2820,7 @@ EOD;
|
||||
try {
|
||||
$DB->update_record_raw($tablename, $record);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
}
|
||||
@@ -2892,7 +2910,7 @@ EOD;
|
||||
try {
|
||||
$DB->update_record($tablename, $record);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
}
|
||||
$record->oneint = 0;
|
||||
@@ -2900,7 +2918,7 @@ EOD;
|
||||
try {
|
||||
$DB->update_record($tablename, $record);
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
}
|
||||
|
||||
@@ -3062,7 +3080,7 @@ EOD;
|
||||
// Only in debug mode - hopefully all devs test code in debug mode...
|
||||
$this->fail('An Exception is missing, expected due to equating of text fields');
|
||||
}
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
$this->assertSame('textconditionsnotallowed', $e->errorcode);
|
||||
}
|
||||
@@ -3168,13 +3186,13 @@ EOD;
|
||||
try {
|
||||
$DB->set_field_select($tablename, 'oneint', 'onestring', 'id = ?', array(1));
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
}
|
||||
try {
|
||||
$DB->set_field_select($tablename, 'onenum', 'onestring', 'id = ?', array(1));
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
}
|
||||
|
||||
@@ -3277,7 +3295,7 @@ EOD;
|
||||
// Only in debug mode - hopefully all devs test code in debug mode...
|
||||
$this->fail('An Exception is missing, expected due to equating of text fields');
|
||||
}
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
$this->assertSame('textconditionsnotallowed', $e->errorcode);
|
||||
}
|
||||
@@ -3330,14 +3348,14 @@ EOD;
|
||||
try {
|
||||
$DB->count_records_sql("SELECT onechar FROM {{$tablename}} WHERE course = ?", array(3));
|
||||
$this->fail('Exception expected when non-number field used in count_records_sql');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
try {
|
||||
$DB->count_records_sql("SELECT course FROM {{$tablename}} WHERE 1 = 2");
|
||||
$this->fail('Exception expected when non-number field used in count_records_sql');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
}
|
||||
@@ -3370,7 +3388,7 @@ EOD;
|
||||
// Only in debug mode - hopefully all devs test code in debug mode...
|
||||
$this->fail('An Exception is missing, expected due to equating of text fields');
|
||||
}
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
$this->assertSame('textconditionsnotallowed', $e->errorcode);
|
||||
}
|
||||
@@ -3521,7 +3539,7 @@ EOD;
|
||||
// Only in debug mode - hopefully all devs test code in debug mode...
|
||||
$this->fail('An Exception is missing, expected due to equating of text fields');
|
||||
}
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
$this->assertSame('textconditionsnotallowed', $e->errorcode);
|
||||
}
|
||||
@@ -3534,7 +3552,7 @@ EOD;
|
||||
// Only in debug mode - hopefully all devs test code in debug mode...
|
||||
$this->fail('An Exception is missing, expected due to equating of text fields');
|
||||
}
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_exception', $e);
|
||||
$this->assertSame('textconditionsnotallowed', $e->errorcode);
|
||||
}
|
||||
@@ -3617,11 +3635,11 @@ EOD;
|
||||
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
|
||||
$dbman->create_table($table);
|
||||
|
||||
$o = new stdClass(); // Objects without __toString - never worked.
|
||||
$o = new \stdClass(); // Objects without __toString - never worked.
|
||||
try {
|
||||
$DB->fix_sql_params("SELECT {{$tablename}} WHERE course = ? ", array($o));
|
||||
$this->fail('coding_exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
@@ -3630,89 +3648,89 @@ EOD;
|
||||
try {
|
||||
$DB->fix_sql_params("SELECT {{$tablename}} WHERE course = ? ", array($o));
|
||||
$this->fail('coding_exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
try {
|
||||
$DB->execute("SELECT {{$tablename}} WHERE course = ? ", array($o));
|
||||
$this->fail('coding_exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
try {
|
||||
$DB->get_recordset_sql("SELECT {{$tablename}} WHERE course = ? ", array($o));
|
||||
$this->fail('coding_exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
try {
|
||||
$DB->get_records_sql("SELECT {{$tablename}} WHERE course = ? ", array($o));
|
||||
$this->fail('coding_exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
try {
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->course = $o;
|
||||
$DB->insert_record_raw($tablename, $record);
|
||||
$this->fail('coding_exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
try {
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->course = $o;
|
||||
$DB->insert_record($tablename, $record);
|
||||
$this->fail('coding_exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
try {
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->course = $o;
|
||||
$DB->import_record($tablename, $record);
|
||||
$this->fail('coding_exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
try {
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->id = 1;
|
||||
$record->course = $o;
|
||||
$DB->update_record_raw($tablename, $record);
|
||||
$this->fail('coding_exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
try {
|
||||
$record = new stdClass();
|
||||
$record = new \stdClass();
|
||||
$record->id = 1;
|
||||
$record->course = $o;
|
||||
$DB->update_record($tablename, $record);
|
||||
$this->fail('coding_exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
try {
|
||||
$DB->set_field_select($tablename, 'course', 1, "course = ? ", array($o));
|
||||
$this->fail('coding_exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
try {
|
||||
$DB->delete_records_select($tablename, "course = ? ", array($o));
|
||||
$this->fail('coding_exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
}
|
||||
@@ -4024,7 +4042,7 @@ EOD;
|
||||
|
||||
try {
|
||||
$DB->insert_record($tablename, array('name'=>'AAA'));
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
// TODO: ignore case insensitive uniqueness problems for now.
|
||||
// $this->fail("Unique index is case sensitive - this may cause problems in some tables");
|
||||
}
|
||||
@@ -4033,7 +4051,7 @@ EOD;
|
||||
$DB->insert_record($tablename, array('name'=>'aäa'));
|
||||
$DB->insert_record($tablename, array('name'=>'aáa'));
|
||||
$this->assertTrue(true);
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$family = $DB->get_dbfamily();
|
||||
if ($family === 'mysql' or $family === 'mssql') {
|
||||
$this->fail("Unique index is accent insensitive, this may cause problems for non-ascii languages. This is usually caused by accent insensitive default collation.");
|
||||
@@ -4567,9 +4585,9 @@ EOD;
|
||||
// Silence php warning.
|
||||
@$DB->sql_substr("name");
|
||||
$this->fail("Expecting an exception, none occurred");
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
} catch (Error $error) {
|
||||
} catch (\Error $error) {
|
||||
// PHP 7.1 throws Error even earlier.
|
||||
$this->assertMatchesRegularExpression('/Too few arguments to function/', $error->getMessage());
|
||||
}
|
||||
@@ -5045,7 +5063,7 @@ EOD;
|
||||
$long = '1234567890123456789';
|
||||
$DB->replace_all_text($tablename, $columns['name'], 'kk', $long);
|
||||
$result = $DB->get_records($tablename, array(), 'id ASC');
|
||||
$expected[$id5]->name = core_text::substr($long.'llll', 0, 20);
|
||||
$expected[$id5]->name = \core_text::substr($long.'llll', 0, 20);
|
||||
$this->assertEquals($expected, $result);
|
||||
|
||||
$DB->replace_all_text($tablename, $columns['intro'], 'kk', $long);
|
||||
@@ -5095,7 +5113,7 @@ EOD;
|
||||
$this->assertEquals(1, $DB->count_records($tablename));
|
||||
try {
|
||||
$DB->insert_record($tablename, (object)array('course'=>1));
|
||||
} catch (Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
// This must be ignored and it must not roll back the whole transaction.
|
||||
}
|
||||
$DB->insert_record($tablename, (object)array('course'=>2));
|
||||
@@ -5112,7 +5130,7 @@ EOD;
|
||||
$this->assertEquals(1, $DB->count_records($tablename));
|
||||
try {
|
||||
$DB->get_records_sql('s e l e c t');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
// This must be ignored and it must not roll back the whole transaction.
|
||||
}
|
||||
$DB->insert_record($tablename, (object)array('course'=>2));
|
||||
@@ -5129,7 +5147,7 @@ EOD;
|
||||
$this->assertEquals(1, $DB->count_records($tablename));
|
||||
try {
|
||||
$DB->execute('xxxx');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
// This must be ignored and it must not roll back the whole transaction.
|
||||
}
|
||||
$DB->insert_record($tablename, (object)array('course'=>2));
|
||||
@@ -5146,7 +5164,7 @@ EOD;
|
||||
$this->assertEquals(1, $DB->count_records($tablename));
|
||||
try {
|
||||
$DB->change_database_structure('xxxx');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
// This must be ignored and it must not roll back the whole transaction.
|
||||
}
|
||||
$DB->insert_record($tablename, (object)array('course'=>2));
|
||||
@@ -5178,9 +5196,9 @@ EOD;
|
||||
$DB->insert_record($tablename, $data);
|
||||
$this->assertEquals(1, $DB->count_records($tablename));
|
||||
try {
|
||||
$transaction->rollback(new Exception('test'));
|
||||
$transaction->rollback(new \Exception('test'));
|
||||
$this->fail('transaction rollback must rethrow exception');
|
||||
} catch (Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
// Ignored.
|
||||
}
|
||||
$this->assertEquals(0, $DB->count_records($tablename));
|
||||
@@ -5224,9 +5242,9 @@ EOD;
|
||||
$DB->insert_record($tablename, $data);
|
||||
$transaction2->allow_commit();
|
||||
try {
|
||||
$transaction1->rollback(new Exception('test'));
|
||||
$transaction1->rollback(new \Exception('test'));
|
||||
$this->fail('transaction rollback must rethrow exception');
|
||||
} catch (Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
$this->assertEquals(get_class($e), 'Exception');
|
||||
}
|
||||
$this->assertEquals(0, $DB->count_records($tablename));
|
||||
@@ -5241,15 +5259,15 @@ EOD;
|
||||
$data = (object)array('course'=>4);
|
||||
$DB->insert_record($tablename, $data);
|
||||
try {
|
||||
$transaction2->rollback(new Exception('test'));
|
||||
$transaction2->rollback(new \Exception('test'));
|
||||
$this->fail('transaction rollback must rethrow exception');
|
||||
} catch (Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
$this->assertEquals(get_class($e), 'Exception');
|
||||
}
|
||||
$this->assertEquals(2, $DB->count_records($tablename)); // Not rolled back yet.
|
||||
try {
|
||||
$transaction1->allow_commit();
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_transaction_exception', $e);
|
||||
}
|
||||
$this->assertEquals(2, $DB->count_records($tablename)); // Not rolled back yet.
|
||||
@@ -5347,7 +5365,7 @@ EOD;
|
||||
$DB->insert_record($tablename, $data);
|
||||
try {
|
||||
$DB->transactions_forbidden();
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_transaction_exception', $e);
|
||||
}
|
||||
// The previous test does not force rollback.
|
||||
@@ -5378,13 +5396,13 @@ EOD;
|
||||
try {
|
||||
$transaction1->allow_commit();
|
||||
$this->fail('wrong order of commits must throw exception');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_transaction_exception', $e);
|
||||
}
|
||||
try {
|
||||
$transaction2->allow_commit();
|
||||
$this->fail('first wrong commit forces rollback');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_transaction_exception', $e);
|
||||
}
|
||||
// This is done in default exception handler usually.
|
||||
@@ -5403,18 +5421,18 @@ EOD;
|
||||
$DB->insert_record($tablename, $data);
|
||||
try {
|
||||
// This first rollback should prevent all other rollbacks.
|
||||
$transaction1->rollback(new Exception('test'));
|
||||
} catch (Exception $e) {
|
||||
$transaction1->rollback(new \Exception('test'));
|
||||
} catch (\Exception $e) {
|
||||
$this->assertEquals(get_class($e), 'Exception');
|
||||
}
|
||||
try {
|
||||
$transaction2->rollback(new Exception('test'));
|
||||
} catch (Exception $e) {
|
||||
$transaction2->rollback(new \Exception('test'));
|
||||
} catch (\Exception $e) {
|
||||
$this->assertEquals(get_class($e), 'Exception');
|
||||
}
|
||||
try {
|
||||
$transaction1->rollback(new Exception('test'));
|
||||
} catch (moodle_exception $e) {
|
||||
$transaction1->rollback(new \Exception('test'));
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_transaction_exception', $e);
|
||||
}
|
||||
// This is done in default exception handler usually.
|
||||
@@ -5430,13 +5448,13 @@ EOD;
|
||||
try {
|
||||
$transaction2->allow_commit();
|
||||
$this->fail('foreign transaction must fail');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_transaction_exception', $e);
|
||||
}
|
||||
try {
|
||||
$transaction1->allow_commit();
|
||||
$this->fail('first wrong commit forces rollback');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_transaction_exception', $e);
|
||||
}
|
||||
$DB->force_transaction_rollback();
|
||||
@@ -5541,7 +5559,7 @@ EOD;
|
||||
$DB2->get_session_lock($rowid, $timeout);
|
||||
$DB2->release_session_lock($rowid); // Should not be executed, but here for safety.
|
||||
$this->fail('An Exception is missing, expected due to session lock acquired.');
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
$this->assertInstanceOf('dml_sessionwait_exception', $e);
|
||||
$DB->release_session_lock($rowid); // Release lock on connection1.
|
||||
}
|
||||
@@ -5876,7 +5894,7 @@ EOD;
|
||||
|
||||
// Inserts counts as writes.
|
||||
|
||||
$rec1 = new stdClass();
|
||||
$rec1 = new \stdClass();
|
||||
$rec1->fieldvalue = 11;
|
||||
$rec1->id = $DB->insert_record($tablename, $rec1);
|
||||
$this->assertEquals($initwrites + 1, $DB->perf_get_writes());
|
||||
@@ -5887,7 +5905,7 @@ EOD;
|
||||
$this->assertGreaterThanOrEqual($previousqueriestime, $lastqueriestime);
|
||||
$previousqueriestime = $lastqueriestime;
|
||||
|
||||
$rec2 = new stdClass();
|
||||
$rec2 = new \stdClass();
|
||||
$rec2->fieldvalue = 22;
|
||||
$rec2->id = $DB->insert_record($tablename, $rec2);
|
||||
$this->assertEquals($initwrites + 2, $DB->perf_get_writes());
|
||||
|
||||
+6
-2
@@ -89,13 +89,17 @@ class read_slave_moodle_database extends test_moodle_database {
|
||||
/**
|
||||
* Query wrapper that calls query_start() and query_end()
|
||||
* @param string $sql
|
||||
* @param array $params
|
||||
* @param array|null $params
|
||||
* @param int $querytype
|
||||
* @param ?callable $callback
|
||||
* @return string $handle handle property
|
||||
*/
|
||||
private function with_query_start_end($sql, array $params = null, $querytype) {
|
||||
public function with_query_start_end($sql, ?array $params, $querytype, $callback = null) {
|
||||
$this->query_start($sql, $params, $querytype);
|
||||
$ret = $this->handle;
|
||||
if ($callback) {
|
||||
call_user_func($callback, $ret);
|
||||
}
|
||||
$this->query_end(null);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
+4
-2
@@ -50,6 +50,7 @@ trait test_moodle_read_slave_trait {
|
||||
$ro = fopen("php://memory", 'r+');
|
||||
fputs($ro, 'ro');
|
||||
|
||||
$this->prefix = 'test_'; // Default, not to leave empty.
|
||||
$this->wantreadslave = true;
|
||||
$this->dbhwrite = $rw;
|
||||
$this->dbhreadonly = $ro;
|
||||
@@ -96,11 +97,11 @@ trait test_moodle_read_slave_trait {
|
||||
/**
|
||||
* Upgrade to public
|
||||
* @param string $sql
|
||||
* @param array $params
|
||||
* @param array|null $params
|
||||
* @param int $type
|
||||
* @param array $extrainfo
|
||||
*/
|
||||
public function query_start($sql, array $params = null, $type, $extrainfo = null) {
|
||||
public function query_start($sql, ?array $params, $type, $extrainfo = null) {
|
||||
return parent::query_start($sql, $params, $type);
|
||||
}
|
||||
|
||||
@@ -109,6 +110,7 @@ trait test_moodle_read_slave_trait {
|
||||
* @param mixed $result
|
||||
*/
|
||||
public function query_end($result) {
|
||||
parent::query_end($result);
|
||||
$this->set_db_handle($this->dbhwrite);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,26 +14,17 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Test \core\dml\recordset_walk.
|
||||
*
|
||||
* @package core
|
||||
* @category dml
|
||||
* @copyright 2015 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
namespace core;
|
||||
|
||||
/**
|
||||
* Test case for recordset_walk.
|
||||
*
|
||||
* @package core
|
||||
* @category dml
|
||||
* @category test
|
||||
* @copyright 2015 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_recordset_walk_testcase extends advanced_testcase {
|
||||
class recordset_walk_test extends \advanced_testcase {
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
@@ -113,7 +104,7 @@ class core_recordset_walk_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Simple callback requiring 1 row fields.
|
||||
*
|
||||
* @param stdClass $data
|
||||
* @param \stdClass $data
|
||||
* @return \Traversable
|
||||
*/
|
||||
public function simple_callback($data, $nothing = 'notpassed') {
|
||||
@@ -126,7 +117,7 @@ class core_recordset_walk_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Callback requiring 1 row fields + other params.
|
||||
*
|
||||
* @param stdClass $data
|
||||
* @param \stdClass $data
|
||||
* @param mixed $extra
|
||||
* @return \Traversable
|
||||
*/
|
||||
|
||||
@@ -15,14 +15,18 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Test sqlsrv dml support.
|
||||
* Test case for sqlsrv dml support.
|
||||
*
|
||||
* @package core
|
||||
* @category dml
|
||||
* @category test
|
||||
* @copyright 2017 John Okely
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core;
|
||||
|
||||
use sqlsrv_native_moodle_database;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
@@ -32,11 +36,11 @@ require_once($CFG->dirroot.'/lib/dml/sqlsrv_native_moodle_database.php');
|
||||
* Test case for sqlsrv dml support.
|
||||
*
|
||||
* @package core
|
||||
* @category dml
|
||||
* @category test
|
||||
* @copyright 2017 John Okely
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class sqlsrv_native_moodle_database_testcase extends advanced_testcase {
|
||||
class sqlsrv_native_moodle_database_test extends \advanced_testcase {
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
@@ -118,7 +122,7 @@ class sqlsrv_native_moodle_database_testcase extends advanced_testcase {
|
||||
public function test_add_no_lock_to_temp_tables($input, $expected) {
|
||||
$sqlsrv = new sqlsrv_native_moodle_database();
|
||||
|
||||
$reflector = new ReflectionObject($sqlsrv);
|
||||
$reflector = new \ReflectionObject($sqlsrv);
|
||||
|
||||
$method = $reflector->getMethod('add_no_lock_to_temp_tables');
|
||||
$method->setAccessible(true);
|
||||
@@ -247,7 +251,7 @@ EOT
|
||||
$this->assertSame($expectedmainquery, $mainquery);
|
||||
|
||||
// The has_query_order_by static method is protected. Use Reflection to call the method.
|
||||
$method = new ReflectionMethod('sqlsrv_native_moodle_database', 'has_query_order_by');
|
||||
$method = new \ReflectionMethod('sqlsrv_native_moodle_database', 'has_query_order_by');
|
||||
$method->setAccessible(true);
|
||||
$result = $method->invoke(null, $sql);
|
||||
$this->assertSame($expectedresult, $result);
|
||||
|
||||
@@ -10,3 +10,22 @@ Feature: Atto edit HTML
|
||||
And I click on "Show more buttons" "button"
|
||||
And I click on "HTML" "button"
|
||||
Then the field "Description" matches value "<p style=\"color: blue;\">Smurf</p>"
|
||||
|
||||
@javascript
|
||||
Scenario: Validation of empty string when the form is submitted with HTML source mode.
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | format |
|
||||
| Course 1 | C1 | weeks |
|
||||
And the following "question categories" exist:
|
||||
| contextlevel | reference | name |
|
||||
| Course | C1 | Test questions |
|
||||
And the following "questions" exist:
|
||||
| questioncategory | qtype | name | questiontext |
|
||||
| Test questions | essay | Essay 01 new | Write about whatever you want |
|
||||
And I am on the "Essay 01 new" "core_question > edit" page logged in as admin
|
||||
And I click on "Show more buttons" "button" in the "Question text" "form_row"
|
||||
And I click on "HTML" "button" in the "Question text" "form_row"
|
||||
And I press the shift + end key
|
||||
And I press the delete key
|
||||
When I press "id_submitbutton"
|
||||
Then I should see "You must supply a value here." in the "Question text" "form_row"
|
||||
|
||||
Vendored
+7
@@ -132,6 +132,10 @@ Y.namespace('M.atto_html').Button = Y.Base.create('button', Y.M.editor_atto.Edit
|
||||
_hideCodeMirror: function() {
|
||||
var host = this.get('host');
|
||||
|
||||
// Disable placeholder for empty content.
|
||||
// This will prevent the editor to copy and submit the empty placeholder.
|
||||
host.disablePlaceholderForEmptyContent();
|
||||
|
||||
// Enable all plugins.
|
||||
host.enablePlugins();
|
||||
|
||||
@@ -164,6 +168,9 @@ Y.namespace('M.atto_html').Button = Y.Base.create('button', Y.M.editor_atto.Edit
|
||||
|
||||
// And re-mark everything as updated.
|
||||
this.markUpdated();
|
||||
|
||||
// Enable placeholder for empty content.
|
||||
host.enablePlaceholderForEmptyContent();
|
||||
}
|
||||
}, {
|
||||
ATTRS: {
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
YUI.add("moodle-atto_html-button",function(o,t){o.namespace("M.atto_html").Button=o.Base.create("button",o.M.editor_atto.EditorPlugin,[],{_codeMirror:null,initializer:function(){this.addButton({icon:"e/source_code",callback:this._toggleHTML});var t=this.get("host").textarea.ancestor("form");t&&t.on("submit",function(){this.get("isHTML")&&this._hideCodeMirror()},this)},_toggleHTML:function(){this.set("isHTML",!this.get("isHTML")),this.get("isHTML")?this._showCodeMirror():this._hideCodeMirror()},_showCodeMirror:function(){var t,e,i=this.get("host");i.disablePlugins(),i.enablePlugins(this.name),i.updateOriginal(),t={width:this.editor.getComputedStyle("width"),height:this.editor.getComputedStyle("height")},e=o.M.atto_html.beautify.html_beautify(i.textarea.get("value"),{indent_size:4,indent_inner_html:!0}),i.textarea.set("value",e),this._codeMirror=o.M.atto_html.CodeMirror.fromTextArea(i.textarea.getDOMNode(),{lineNumbers:!0,mode:"htmlmixed",tabSize:2,lineWrapping:!0}),this._codeMirror.on("change",function(t){t.save()}),this.editor.hide(),this._codeMirror.setSize(t.width,t.height),this._codeMirror.focus()},_hideCodeMirror:function(){var t=this.get("host");t.enablePlugins(),t.updateFromTextArea(),this._codeMirror&&(o.one(this._codeMirror.getWrapperElement()).hide(),this._codeMirror.toTextArea(),o.Object.each(this._codeMirror._handlers,function(t,e){o.Array.each(t,function(t){this.off(e,t)},this)},this._codeMirror),delete this._codeMirror,this._codeMirror=null),t.textarea.hide(),this.editor.show(),t.focus(),this.markUpdated()}},{ATTRS:{isHTML:{value:!1}}})},"@VERSION@",{requires:["promise","moodle-editor_atto-plugin","moodle-atto_html-beautify","moodle-atto_html-codemirror","event-valuechange"]});
|
||||
YUI.add("moodle-atto_html-button",function(o,t){o.namespace("M.atto_html").Button=o.Base.create("button",o.M.editor_atto.EditorPlugin,[],{_codeMirror:null,initializer:function(){this.addButton({icon:"e/source_code",callback:this._toggleHTML});var t=this.get("host").textarea.ancestor("form");t&&t.on("submit",function(){this.get("isHTML")&&this._hideCodeMirror()},this)},_toggleHTML:function(){this.set("isHTML",!this.get("isHTML")),this.get("isHTML")?this._showCodeMirror():this._hideCodeMirror()},_showCodeMirror:function(){var t,e,i=this.get("host");i.disablePlugins(),i.enablePlugins(this.name),i.updateOriginal(),t={width:this.editor.getComputedStyle("width"),height:this.editor.getComputedStyle("height")},e=o.M.atto_html.beautify.html_beautify(i.textarea.get("value"),{indent_size:4,indent_inner_html:!0}),i.textarea.set("value",e),this._codeMirror=o.M.atto_html.CodeMirror.fromTextArea(i.textarea.getDOMNode(),{lineNumbers:!0,mode:"htmlmixed",tabSize:2,lineWrapping:!0}),this._codeMirror.on("change",function(t){t.save()}),this.editor.hide(),this._codeMirror.setSize(t.width,t.height),this._codeMirror.focus()},_hideCodeMirror:function(){var t=this.get("host");t.disablePlaceholderForEmptyContent(),t.enablePlugins(),t.updateFromTextArea(),this._codeMirror&&(o.one(this._codeMirror.getWrapperElement()).hide(),this._codeMirror.toTextArea(),o.Object.each(this._codeMirror._handlers,function(t,e){o.Array.each(t,function(t){this.off(e,t)},this)},this._codeMirror),delete this._codeMirror,this._codeMirror=null),t.textarea.hide(),this.editor.show(),t.focus(),this.markUpdated(),t.enablePlaceholderForEmptyContent()}},{ATTRS:{isHTML:{value:!1}}})},"@VERSION@",{requires:["promise","moodle-editor_atto-plugin","moodle-atto_html-beautify","moodle-atto_html-codemirror","event-valuechange"]});
|
||||
+7
@@ -132,6 +132,10 @@ Y.namespace('M.atto_html').Button = Y.Base.create('button', Y.M.editor_atto.Edit
|
||||
_hideCodeMirror: function() {
|
||||
var host = this.get('host');
|
||||
|
||||
// Disable placeholder for empty content.
|
||||
// This will prevent the editor to copy and submit the empty placeholder.
|
||||
host.disablePlaceholderForEmptyContent();
|
||||
|
||||
// Enable all plugins.
|
||||
host.enablePlugins();
|
||||
|
||||
@@ -164,6 +168,9 @@ Y.namespace('M.atto_html').Button = Y.Base.create('button', Y.M.editor_atto.Edit
|
||||
|
||||
// And re-mark everything as updated.
|
||||
this.markUpdated();
|
||||
|
||||
// Enable placeholder for empty content.
|
||||
host.enablePlaceholderForEmptyContent();
|
||||
}
|
||||
}, {
|
||||
ATTRS: {
|
||||
|
||||
@@ -130,6 +130,10 @@ Y.namespace('M.atto_html').Button = Y.Base.create('button', Y.M.editor_atto.Edit
|
||||
_hideCodeMirror: function() {
|
||||
var host = this.get('host');
|
||||
|
||||
// Disable placeholder for empty content.
|
||||
// This will prevent the editor to copy and submit the empty placeholder.
|
||||
host.disablePlaceholderForEmptyContent();
|
||||
|
||||
// Enable all plugins.
|
||||
host.enablePlugins();
|
||||
|
||||
@@ -162,6 +166,9 @@ Y.namespace('M.atto_html').Button = Y.Base.create('button', Y.M.editor_atto.Edit
|
||||
|
||||
// And re-mark everything as updated.
|
||||
this.markUpdated();
|
||||
|
||||
// Enable placeholder for empty content.
|
||||
host.enablePlaceholderForEmptyContent();
|
||||
}
|
||||
}, {
|
||||
ATTRS: {
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
This files describes API changes in the editor_atto code.
|
||||
|
||||
=== 3.11 ===
|
||||
|
||||
* Two new helper functions have been added to lib/editor/atto
|
||||
to handle enable/disable the empty placeholder for empty content in the HTML source view:
|
||||
- enablePlaceholderForEmptyContent() - Handle to enable the empty placeholder for empty content
|
||||
- disablePlaceholderForEmptyContent() - Handle to disable the empty placeholder for empty content.
|
||||
|
||||
* One property have been added to lib/editor/atto to handle the state of the empty placeholder content.
|
||||
- enableAppropriateEmptyContent - Enable/disable the empty placeholder content.
|
||||
|
||||
=== 3.4 ===
|
||||
|
||||
* The following functions, previously used (exclusively) by upgrade steps are not available
|
||||
|
||||
+31
-1
@@ -158,6 +158,14 @@ Y.extend(Editor, Y.Base, {
|
||||
*/
|
||||
coreDirection: null,
|
||||
|
||||
/**
|
||||
* Enable/disable the empty placeholder content.
|
||||
*
|
||||
* @property enableAppropriateEmptyContent
|
||||
* @type Boolean
|
||||
*/
|
||||
enableAppropriateEmptyContent: null,
|
||||
|
||||
/**
|
||||
* Event Handles to clear on editor destruction.
|
||||
*
|
||||
@@ -207,6 +215,9 @@ Y.extend(Editor, Y.Base, {
|
||||
// Set diretcion according to current page language.
|
||||
this.coreDirection = Y.one('body').hasClass('dir-rtl') ? 'rtl' : 'ltr';
|
||||
|
||||
// Enable the placeholder for empty content.
|
||||
this.enablePlaceholderForEmptyContent();
|
||||
|
||||
// Add everything to the wrapper.
|
||||
this.setupToolbar();
|
||||
|
||||
@@ -420,6 +431,20 @@ Y.extend(Editor, Y.Base, {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable the empty placeholder for empty content.
|
||||
*/
|
||||
enablePlaceholderForEmptyContent: function() {
|
||||
this.enableAppropriateEmptyContent = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable the empty placeholder for empty content.
|
||||
*/
|
||||
disablePlaceholderForEmptyContent: function() {
|
||||
this.enableAppropriateEmptyContent = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Register an event handle for disposal in the destructor.
|
||||
*
|
||||
@@ -692,6 +717,10 @@ EditorTextArea.prototype = {
|
||||
* @private
|
||||
*/
|
||||
_getEmptyContent: function() {
|
||||
if (!this.enableAppropriateEmptyContent) {
|
||||
// Return the empty string if we do not enable the empty placeholder. Ex: HTML mode.
|
||||
return '';
|
||||
}
|
||||
var alignment;
|
||||
if (this.coreDirection === 'rtl') {
|
||||
alignment = 'style="text-align: right;"';
|
||||
@@ -1793,7 +1822,8 @@ EditorClean.prototype = {
|
||||
|
||||
// Use a regular expression to find the next open or close li, ul, or ol tag.
|
||||
// Keep going until there are no more matching tags left.
|
||||
while ((match = toProcess.match(/<(\/?)(li|ul|ol)[^>]*>/i))) {
|
||||
// This expression looks for whole words by employing the word boundary (\b) metacharacter.
|
||||
while ((match = toProcess.match(/<(\/?)(li|ul|ol)\b[^>]*>/i))) {
|
||||
currentTag = {
|
||||
tag: match[2],
|
||||
tagLowerCase: match[2].toLowerCase(),
|
||||
|
||||
+5
-5
File diff suppressed because one or more lines are too long
+31
-1
@@ -158,6 +158,14 @@ Y.extend(Editor, Y.Base, {
|
||||
*/
|
||||
coreDirection: null,
|
||||
|
||||
/**
|
||||
* Enable/disable the empty placeholder content.
|
||||
*
|
||||
* @property enableAppropriateEmptyContent
|
||||
* @type Boolean
|
||||
*/
|
||||
enableAppropriateEmptyContent: null,
|
||||
|
||||
/**
|
||||
* Event Handles to clear on editor destruction.
|
||||
*
|
||||
@@ -205,6 +213,9 @@ Y.extend(Editor, Y.Base, {
|
||||
// Set diretcion according to current page language.
|
||||
this.coreDirection = Y.one('body').hasClass('dir-rtl') ? 'rtl' : 'ltr';
|
||||
|
||||
// Enable the placeholder for empty content.
|
||||
this.enablePlaceholderForEmptyContent();
|
||||
|
||||
// Add everything to the wrapper.
|
||||
this.setupToolbar();
|
||||
|
||||
@@ -417,6 +428,20 @@ Y.extend(Editor, Y.Base, {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable the empty placeholder for empty content.
|
||||
*/
|
||||
enablePlaceholderForEmptyContent: function() {
|
||||
this.enableAppropriateEmptyContent = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable the empty placeholder for empty content.
|
||||
*/
|
||||
disablePlaceholderForEmptyContent: function() {
|
||||
this.enableAppropriateEmptyContent = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Register an event handle for disposal in the destructor.
|
||||
*
|
||||
@@ -687,6 +712,10 @@ EditorTextArea.prototype = {
|
||||
* @private
|
||||
*/
|
||||
_getEmptyContent: function() {
|
||||
if (!this.enableAppropriateEmptyContent) {
|
||||
// Return the empty string if we do not enable the empty placeholder. Ex: HTML mode.
|
||||
return '';
|
||||
}
|
||||
var alignment;
|
||||
if (this.coreDirection === 'rtl') {
|
||||
alignment = 'style="text-align: right;"';
|
||||
@@ -1780,7 +1809,8 @@ EditorClean.prototype = {
|
||||
|
||||
// Use a regular expression to find the next open or close li, ul, or ol tag.
|
||||
// Keep going until there are no more matching tags left.
|
||||
while ((match = toProcess.match(/<(\/?)(li|ul|ol)[^>]*>/i))) {
|
||||
// This expression looks for whole words by employing the word boundary (\b) metacharacter.
|
||||
while ((match = toProcess.match(/<(\/?)(li|ul|ol)\b[^>]*>/i))) {
|
||||
currentTag = {
|
||||
tag: match[2],
|
||||
tagLowerCase: match[2].toLowerCase(),
|
||||
|
||||
+2
-1
@@ -498,7 +498,8 @@ EditorClean.prototype = {
|
||||
|
||||
// Use a regular expression to find the next open or close li, ul, or ol tag.
|
||||
// Keep going until there are no more matching tags left.
|
||||
while ((match = toProcess.match(/<(\/?)(li|ul|ol)[^>]*>/i))) {
|
||||
// This expression looks for whole words by employing the word boundary (\b) metacharacter.
|
||||
while ((match = toProcess.match(/<(\/?)(li|ul|ol)\b[^>]*>/i))) {
|
||||
currentTag = {
|
||||
tag: match[2],
|
||||
tagLowerCase: match[2].toLowerCase(),
|
||||
|
||||
+25
@@ -156,6 +156,14 @@ Y.extend(Editor, Y.Base, {
|
||||
*/
|
||||
coreDirection: null,
|
||||
|
||||
/**
|
||||
* Enable/disable the empty placeholder content.
|
||||
*
|
||||
* @property enableAppropriateEmptyContent
|
||||
* @type Boolean
|
||||
*/
|
||||
enableAppropriateEmptyContent: null,
|
||||
|
||||
/**
|
||||
* Event Handles to clear on editor destruction.
|
||||
*
|
||||
@@ -205,6 +213,9 @@ Y.extend(Editor, Y.Base, {
|
||||
// Set diretcion according to current page language.
|
||||
this.coreDirection = Y.one('body').hasClass('dir-rtl') ? 'rtl' : 'ltr';
|
||||
|
||||
// Enable the placeholder for empty content.
|
||||
this.enablePlaceholderForEmptyContent();
|
||||
|
||||
// Add everything to the wrapper.
|
||||
this.setupToolbar();
|
||||
|
||||
@@ -418,6 +429,20 @@ Y.extend(Editor, Y.Base, {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable the empty placeholder for empty content.
|
||||
*/
|
||||
enablePlaceholderForEmptyContent: function() {
|
||||
this.enableAppropriateEmptyContent = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable the empty placeholder for empty content.
|
||||
*/
|
||||
disablePlaceholderForEmptyContent: function() {
|
||||
this.enableAppropriateEmptyContent = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Register an event handle for disposal in the destructor.
|
||||
*
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user